2018-10-15 21:47:27 -07: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 .
*
*/
2018-10-16 22:29:06 -07:00
# include "gargoyle/scott/scott.h"
2018-10-15 21:47:27 -07:00
namespace Gargoyle {
namespace Scott {
2018-10-17 20:07:11 -07:00
/*
glkunix_argumentlist_t glkunix_arguments [ ] =
{
{ " -y " , glkunix_arg_NoValue , " -y Generate 'You are', 'You are carrying' type messages for games that use these instead (eg Robin Of Sherwood) " } ,
{ " -i " , glkunix_arg_NoValue , " -i Generate 'I am' type messages (default) " } ,
{ " -d " , glkunix_arg_NoValue , " -d Debugging info on load " } ,
{ " -s " , glkunix_arg_NoValue , " -s Generate authentic Scott Adams driver light messages rather than other driver style ones (Light goes out in n turns..) " } ,
{ " -t " , glkunix_arg_NoValue , " -t Generate TRS80 style display (terminal width is 64 characters; a line <-----------------> is displayed after the top stuff; objects have periods after them instead of hyphens " } ,
{ " -p " , glkunix_arg_NoValue , " -p Use for prehistoric databases which don't use bit 16 " } ,
{ " -w " , glkunix_arg_NoValue , " -w Disable upper window " } ,
{ " " , glkunix_arg_ValueFollows , " filename file to load " } ,
{ nullptr , glkunix_arg_End , nullptr }
} ;
*/
Scott : : Scott ( OSystem * syst , const GargoyleGameDescription * gameDesc ) : Glk ( syst , gameDesc ) ,
Items ( nullptr ) , Rooms ( nullptr ) , Verbs ( nullptr ) , Nouns ( nullptr ) , Messages ( nullptr ) ,
Actions ( nullptr ) , CurrentCounter ( 0 ) , SavedRoom ( 0 ) , Options ( 0 ) , Width ( 0 ) , TopHeight ( 0 ) ,
split_screen ( true ) , Bottom ( 0 ) , Top ( 0 ) , BitFlags ( 0 ) {
Common : : fill ( & NounText [ 0 ] , & NounText [ 16 ] , ' \0 ' ) ;
Common : : fill ( & Counters [ 0 ] , & Counters [ 16 ] , 0 ) ;
Common : : fill ( & RoomSaved [ 0 ] , & RoomSaved [ 16 ] , 0 ) ;
}
void Scott : : runGame ( Common : : SeekableReadStream * gameFile ) {
int vb , no ;
initialize ( ) ;
Bottom = glk_window_open ( 0 , 0 , 0 , wintype_TextBuffer , 1 ) ;
if ( Bottom = = nullptr )
glk_exit ( ) ;
glk_set_window ( Bottom ) ;
if ( Options & TRS80_STYLE ) {
Width = 64 ;
TopHeight = 11 ;
} else {
Width = 80 ;
TopHeight = 10 ;
}
if ( split_screen ) {
Top = glk_window_open ( Bottom , winmethod_Above | winmethod_Fixed , TopHeight , wintype_TextGrid , 0 ) ;
if ( Top = = nullptr ) {
split_screen = 0 ;
Top = Bottom ;
}
} else {
Top = Bottom ;
}
Output ( " \
Scott Free , A Scott Adams game driver in C . \ n \
Release 1.14 , ( c ) 1993 , 1994 , 1995 Swansea University Computer Society . \ n \
Distributed under the GNU software license \ n \ n " );
LoadDatabase ( gameFile , ( Options & DEBUGGING ) ? 1 : 0 ) ;
while ( ! shouldQuit ( ) ) {
glk_tick ( ) ;
PerformActions ( 0 , 0 ) ;
Look ( ) ;
if ( GetInput ( & vb , & no ) = = - 1 )
continue ;
switch ( PerformActions ( vb , no ) ) {
case - 1 :
Output ( " I don't understand your command. " ) ;
break ;
case - 2 :
Output ( " I can't do that yet. " ) ;
break ;
default :
break ;
}
/* Brian Howarth games seem to use -1 for forever */
if ( Items [ LIGHT_SOURCE ] . Location /*==-1*/ ! = DESTROYED & & GameHeader . LightTime ! = - 1 ) {
GameHeader . LightTime - - ;
if ( GameHeader . LightTime < 1 ) {
BitFlags | = ( 1 < < LIGHTOUTBIT ) ;
if ( Items [ LIGHT_SOURCE ] . Location = = CARRIED | |
Items [ LIGHT_SOURCE ] . Location = = MyLoc ) {
if ( Options & SCOTTLIGHT )
Output ( " Light has run out! " ) ;
else
Output ( " Your light has run out. " ) ;
}
if ( Options & PREHISTORIC_LAMP )
Items [ LIGHT_SOURCE ] . Location = DESTROYED ;
} else if ( GameHeader . LightTime < 25 ) {
if ( Items [ LIGHT_SOURCE ] . Location = = CARRIED | |
Items [ LIGHT_SOURCE ] . Location = = MyLoc ) {
if ( Options & SCOTTLIGHT ) {
Output ( " Light runs out in " ) ;
OutputNumber ( GameHeader . LightTime ) ;
Output ( " turns. " ) ;
} else {
if ( GameHeader . LightTime % 5 = = 0 )
Output ( " Your light is growing dim. " ) ;
}
}
}
}
}
}
void Scott : : initialize ( ) {
/*
int argc = data - > argc ;
char * * argv = data - > argv ;
if ( argc < 1 )
return 0 ;
while ( argv [ 1 ] )
{
if ( * argv [ 1 ] ! = ' - ' )
break ;
switch ( argv [ 1 ] [ 1 ] )
{
case ' y ' :
Options | = YOUARE ;
break ;
case ' i ' :
Options & = ~ YOUARE ;
break ;
case ' d ' :
Options | = DEBUGGING ;
break ;
case ' s ' :
Options | = SCOTTLIGHT ;
break ;
case ' t ' :
Options | = TRS80_STYLE ;
break ;
case ' p ' :
Options | = PREHISTORIC_LAMP ;
break ;
case ' w ' :
split_screen = 0 ;
break ;
}
argv + + ;
argc - - ;
}
if ( argc = = 2 )
{
game_file = argv [ 1 ] ;
# ifdef GARGLK
const char * s ;
if ( ( s = strrchr ( game_file , ' / ' ) ) ! = nullptr | | ( s = strrchr ( game_file , ' \\ ' ) ) ! = nullptr )
{
garglk_set_story_name ( s + 1 ) ;
}
else
{
garglk_set_story_name ( game_file ) ;
}
# endif
}
*/
}
void Scott : : Display ( winid_t w , const char * fmt , . . . ) {
va_list ap ;
va_start ( ap , fmt ) ;
Common : : String msg = Common : : String : : vformat ( fmt , ap ) ;
va_end ( ap ) ;
glk_put_string_stream ( glk_window_get_stream ( w ) , msg . c_str ( ) ) ;
}
void Scott : : Delay ( int seconds ) {
2018-10-16 21:06:07 -07:00
event_t ev ;
2018-10-17 20:07:11 -07:00
if ( ! glk_gestalt ( gestalt_Timer , 0 ) )
return ;
glk_request_timer_events ( 1000 * seconds ) ;
do
{
glk_select ( & ev ) ;
} while ( ev . type ! = evtype_Timer ) ;
glk_request_timer_events ( 0 ) ;
}
void Scott : : Fatal ( const char * x ) {
error ( x ) ;
}
void Scott : : ClearScreen ( void ) {
glk_window_clear ( Bottom ) ;
}
void * Scott : : MemAlloc ( int size ) {
void * t = ( void * ) malloc ( size ) ;
if ( t = = nullptr )
Fatal ( " Out of memory " ) ;
return t ;
}
bool Scott : : RandomPercent ( uint n ) {
return _random . getRandomNumber ( 99 ) < n ;
}
int Scott : : CountCarried ( void ) {
int ct = 0 ;
int n = 0 ;
while ( ct < = GameHeader . NumItems ) {
if ( Items [ ct ] . Location = = CARRIED )
n + + ;
ct + + ;
}
return n ;
}
const char * Scott : : MapSynonym ( const char * word ) {
int n = 1 ;
const char * tp ;
static char lastword [ 16 ] ; /* Last non synonym */
while ( n < = GameHeader . NumWords ) {
tp = Nouns [ n ] ;
if ( * tp = = ' * ' )
tp + + ;
else
strcpy ( lastword , tp ) ;
if ( xstrncasecmp ( word , tp , GameHeader . WordLength ) = = 0 )
return lastword ;
n + + ;
}
return nullptr ;
}
int Scott : : MatchUpItem ( const char * text , int loc ) {
const char * word = MapSynonym ( text ) ;
int ct = 0 ;
if ( word = = nullptr )
word = text ;
while ( ct < = GameHeader . NumItems ) {
if ( Items [ ct ] . AutoGet & & Items [ ct ] . Location = = loc & &
xstrncasecmp ( Items [ ct ] . AutoGet , word , GameHeader . WordLength ) = = 0 )
return ct ;
ct + + ;
}
return - 1 ;
}
char * Scott : : ReadString ( Common : : SeekableReadStream * f )
{
char tmp [ 1024 ] ;
char * t ;
int c , nc ;
int ct = 0 ;
do {
c = f - > readByte ( ) ;
} while ( f - > pos ( ) < f - > size ( ) & & Common : : isSpace ( c ) ) ;
if ( c ! = ' " ' ) {
Fatal ( " Initial quote expected " ) ;
}
do {
c = f - > readByte ( ) ;
if ( c = = EOF )
Fatal ( " EOF in string " ) ;
if ( c = = ' " ' )
{
nc = f - > readByte ( ) ;
if ( nc ! = ' " ' ) {
f - > seek ( - 1 , SEEK_CUR ) ;
break ;
}
}
if ( c = = ' ` ' )
c = ' " ' ; /* pdd */
/* Ensure a valid Glk newline is sent. */
if ( c = = ' \n ' )
tmp [ ct + + ] = 10 ;
/* Special case: assume CR is part of CRLF in a
* DOS - formatted file , and ignore it .
*/
else if ( c = = 13 )
;
/* Pass only ASCII to Glk; the other reasonable option
* would be to pass Latin - 1 , but it ' s probably safe to
* assume that Scott Adams games are ASCII only .
*/
else if ( ( c > = 32 & & c < = 126 ) )
tmp [ ct + + ] = c ;
else
tmp [ ct + + ] = ' ? ' ;
} while ( 1 ) ;
tmp [ ct ] = 0 ;
t = ( char * ) MemAlloc ( ct + 1 ) ;
memcpy ( t , tmp , ct + 1 ) ;
return t ;
}
void Scott : : LoadDatabase ( Common : : SeekableReadStream * f , int loud ) {
int unused , ni , na , nw , nr , mc , pr , tr , wl , lt , mn , trm ;
int ct ;
int lo ;
Action * ap ;
Room * rp ;
Item * ip ;
/* Load the header */
readInts ( f , 12 , & unused , & ni , & na , & nw , & nr , & mc , & pr , & tr , & wl , & lt , & mn , & trm ) ;
GameHeader . NumItems = ni ;
Items = ( Item * ) MemAlloc ( sizeof ( Item ) * ( ni + 1 ) ) ;
GameHeader . NumActions = na ;
Actions = ( Action * ) MemAlloc ( sizeof ( Action ) * ( na + 1 ) ) ;
GameHeader . NumWords = nw ;
GameHeader . WordLength = wl ;
Verbs = ( const char * * ) MemAlloc ( sizeof ( char * ) * ( nw + 1 ) ) ;
Nouns = ( const char * * ) MemAlloc ( sizeof ( char * ) * ( nw + 1 ) ) ;
GameHeader . NumRooms = nr ;
Rooms = ( Room * ) MemAlloc ( sizeof ( Room ) * ( nr + 1 ) ) ;
GameHeader . MaxCarry = mc ;
GameHeader . PlayerRoom = pr ;
GameHeader . Treasures = tr ;
GameHeader . LightTime = lt ;
LightRefill = lt ;
GameHeader . NumMessages = mn ;
Messages = ( const char * * ) MemAlloc ( sizeof ( char * ) * ( mn + 1 ) ) ;
GameHeader . TreasureRoom = trm ;
/* Load the actions */
ct = 0 ;
ap = Actions ;
if ( loud )
debug ( " Reading %d actions. " , na ) ;
while ( ct < na + 1 ) {
readInts ( f , 8 ,
& ap - > Vocab ,
& ap - > Condition [ 0 ] ,
& ap - > Condition [ 1 ] ,
& ap - > Condition [ 2 ] ,
& ap - > Condition [ 3 ] ,
& ap - > Condition [ 4 ] ,
& ap - > action [ 0 ] ,
& ap - > action [ 1 ] ) ;
ap + + ;
ct + + ;
}
ct = 0 ;
if ( loud )
debug ( " Reading %d word pairs. " , nw ) ;
while ( ct < nw + 1 ) {
Verbs [ ct ] = ReadString ( f ) ;
Nouns [ ct ] = ReadString ( f ) ;
ct + + ;
}
ct = 0 ;
rp = Rooms ;
if ( loud )
debug ( " Reading %d rooms. " , nr ) ;
while ( ct < nr + 1 ) {
readInts ( f , 6 ,
& rp - > Exits [ 0 ] , & rp - > Exits [ 1 ] , & rp - > Exits [ 2 ] ,
& rp - > Exits [ 3 ] , & rp - > Exits [ 4 ] , & rp - > Exits [ 5 ] ) ;
rp - > Text = ReadString ( f ) ;
ct + + ;
rp + + ;
}
ct = 0 ;
if ( loud )
debug ( " Reading %d messages. " , mn ) ;
while ( ct < mn + 1 ) {
Messages [ ct ] = ReadString ( f ) ;
ct + + ;
}
ct = 0 ;
if ( loud )
debug ( " Reading %d items. " , ni ) ;
ip = Items ;
while ( ct < ni + 1 ) {
ip - > Text = ReadString ( f ) ;
ip - > AutoGet = strchr ( ip - > Text , ' / ' ) ;
/* Some games use // to mean no auto get/drop word! */
if ( ip - > AutoGet & & strcmp ( ip - > AutoGet , " // " ) & & strcmp ( ip - > AutoGet , " /* " ) ) {
char * t ;
* ip - > AutoGet + + = 0 ;
t = strchr ( ip - > AutoGet , ' / ' ) ;
if ( t ! = nullptr )
* t = 0 ;
}
readInts ( f , 1 , & lo ) ;
ip - > Location = ( unsigned char ) lo ;
ip - > InitialLoc = ip - > Location ;
ip + + ;
ct + + ;
}
ct = 0 ;
/* Discard Comment Strings */
while ( ct < na + 1 ) {
free ( ReadString ( f ) ) ;
ct + + ;
}
readInts ( f , 1 , & ct ) ;
if ( loud )
debug ( " Version %d.%02d of Adventure " , ct / 100 , ct % 100 ) ;
readInts ( f , 1 , & ct ) ;
if ( loud )
debug ( " %d. \n Load Complete. \n " , ct ) ;
}
void Scott : : Output ( const char * a ) {
Display ( Bottom , " %s " , a ) ;
}
void Scott : : OutputNumber ( int a ) {
Display ( Bottom , " %d " , a ) ;
}
void Scott : : Look ( void ) {
static char * ExitNames [ 6 ] = { " North " , " South " , " East " , " West " , " Up " , " Down " } ;
Room * r ;
int ct , f ;
int pos ;
if ( split_screen )
glk_window_clear ( Top ) ;
if ( ( BitFlags & ( 1 < < DARKBIT ) ) & & Items [ LIGHT_SOURCE ] . Location ! = CARRIED
& & Items [ LIGHT_SOURCE ] . Location ! = MyLoc ) {
if ( Options & YOUARE )
Display ( Top , " You can't see. It is too dark! \n " ) ;
else
Display ( Top , " I can't see. It is too dark! \n " ) ;
if ( Options & TRS80_STYLE )
Display ( Top , TRS80_LINE ) ;
return ;
}
r = & Rooms [ MyLoc ] ;
if ( * r - > Text = = ' * ' )
Display ( Top , " %s \n " , r - > Text + 1 ) ;
else {
if ( Options & YOUARE )
Display ( Top , " You are in a %s \n " , r - > Text ) ;
else
Display ( Top , " I'm in a %s \n " , r - > Text ) ;
}
ct = 0 ;
f = 0 ;
Display ( Top , " \n Obvious exits: " ) ;
while ( ct < 6 ) {
if ( r - > Exits [ ct ] ! = 0 )
{
if ( f = = 0 )
f = 1 ;
else
Display ( Top , " , " ) ;
Display ( Top , " %s " , ExitNames [ ct ] ) ;
}
ct + + ;
}
if ( f = = 0 )
Display ( Top , " none " ) ;
Display ( Top , " . \n " ) ;
ct = 0 ;
f = 0 ;
pos = 0 ;
while ( ct < = GameHeader . NumItems ) {
if ( Items [ ct ] . Location = = MyLoc ) {
if ( f = = 0 ) {
if ( Options & YOUARE ) {
Display ( Top , " \n You can also see: " ) ;
pos = 18 ;
} else {
Display ( Top , " \n I can also see: " ) ;
pos = 16 ;
}
f + + ;
} else if ( ! ( Options & TRS80_STYLE ) ) {
Display ( Top , " - " ) ;
pos + = 3 ;
}
if ( pos + ( int ) strlen ( Items [ ct ] . Text ) > ( Width - 10 ) ) {
pos = 0 ;
Display ( Top , " \n " ) ;
}
Display ( Top , " %s " , Items [ ct ] . Text ) ;
pos + = strlen ( Items [ ct ] . Text ) ;
if ( Options & TRS80_STYLE ) {
Display ( Top , " . " ) ;
pos + = 2 ;
}
}
ct + + ;
}
Display ( Top , " \n " ) ;
if ( Options & TRS80_STYLE )
Display ( Top , TRS80_LINE ) ;
}
int Scott : : WhichWord ( const char * word , const char * * list ) {
int n = 1 ;
int ne = 1 ;
const char * tp ;
while ( ne < = GameHeader . NumWords ) {
tp = list [ ne ] ;
if ( * tp = = ' * ' )
tp + + ;
else
n = ne ;
if ( xstrncasecmp ( word , tp , GameHeader . WordLength ) = = 0 )
return n ;
ne + + ;
}
return - 1 ;
}
void Scott : : LineInput ( char * buf , size_t n ) {
event_t ev ;
glk_request_line_event ( Bottom , buf , n - 1 , 0 ) ;
2018-10-16 21:06:07 -07:00
do {
glk_select ( & ev ) ;
2018-10-17 20:07:11 -07:00
if ( ev . type = = evtype_LineInput )
2018-10-16 21:06:07 -07:00
break ;
2018-10-17 20:07:11 -07:00
else if ( ev . type = = evtype_Arrange & & split_screen )
Look ( ) ;
2018-10-16 21:06:07 -07:00
} while ( ev . type ! = evtype_Quit ) ;
2018-10-17 20:07:11 -07:00
buf [ ev . val1 ] = 0 ;
}
void Scott : : SaveGame ( void ) {
strid_t file ;
frefid_t ref ;
int ct ;
Common : : String msg ;
ref = glk_fileref_create_by_prompt ( fileusage_TextMode | fileusage_SavedGame , filemode_Write , 0 ) ;
if ( ref = = nullptr ) return ;
file = glk_stream_open_file ( ref , filemode_Write , 0 ) ;
glk_fileref_destroy ( ref ) ;
if ( file = = nullptr ) return ;
for ( ct = 0 ; ct < 16 ; ct + + ) {
msg = Common : : String : : format ( " %d %d \n " , Counters [ ct ] , RoomSaved [ ct ] ) ;
glk_put_string_stream ( file , msg . c_str ( ) ) ;
}
msg = Common : : String : : format ( " %lu %d %hd %d %d %hd \n " ,
BitFlags , ( BitFlags & ( 1 < < DARKBIT ) ) ? 1 : 0 ,
MyLoc , CurrentCounter , SavedRoom , GameHeader . LightTime ) ;
glk_put_string_stream ( file , msg . c_str ( ) ) ;
for ( ct = 0 ; ct < = GameHeader . NumItems ; ct + + ) {
msg = Common : : String : : format ( " %hd \n " , ( short ) Items [ ct ] . Location ) ;
glk_put_string_stream ( file , msg . c_str ( ) ) ;
}
glk_stream_close ( file , nullptr ) ;
Output ( " Saved. \n " ) ;
}
void Scott : : LoadGame ( void ) {
strid_t file ;
frefid_t ref ;
char buf [ 128 ] ;
int ct = 0 ;
short lo ;
short DarkFlag ;
ref = glk_fileref_create_by_prompt ( fileusage_TextMode | fileusage_SavedGame , filemode_Read , 0 ) ;
if ( ref = = nullptr ) return ;
file = glk_stream_open_file ( ref , filemode_Read , 0 ) ;
glk_fileref_destroy ( ref ) ;
if ( file = = nullptr ) return ;
for ( ct = 0 ; ct < 16 ; ct + + ) {
glk_get_line_stream ( file , buf , sizeof buf ) ;
sscanf ( buf , " %d %d " , & Counters [ ct ] , & RoomSaved [ ct ] ) ;
}
glk_get_line_stream ( file , buf , sizeof buf ) ;
sscanf ( buf , " %ld %hd %d %d %d %d \n " ,
& BitFlags , & DarkFlag , & MyLoc , & CurrentCounter , & SavedRoom ,
& GameHeader . LightTime ) ;
/* Backward compatibility */
if ( DarkFlag )
BitFlags | = ( 1 < < 15 ) ;
for ( ct = 0 ; ct < = GameHeader . NumItems ; ct + + ) {
glk_get_line_stream ( file , buf , sizeof buf ) ;
sscanf ( buf , " %hd \n " , & lo ) ;
Items [ ct ] . Location = ( unsigned char ) lo ;
}
}
int Scott : : GetInput ( int * vb , int * no ) {
char buf [ 256 ] ;
char verb [ 10 ] , noun [ 10 ] ;
int vc , nc ;
int num ;
do {
do {
Output ( " \n Tell me what to do ? " ) ;
LineInput ( buf , sizeof buf ) ;
num = sscanf ( buf , " %9s %9s " , verb , noun ) ;
} while ( num = = 0 | | * buf = = ' \n ' ) ;
if ( xstrcasecmp ( verb , " restore " ) = = 0 ) {
LoadGame ( ) ;
return - 1 ;
}
if ( num = = 1 )
* noun = 0 ;
if ( * noun = = 0 & & strlen ( verb ) = = 1 ) {
switch ( Common : : isUpper ( ( unsigned char ) * verb ) ? tolower ( ( unsigned char ) * verb ) : * verb ) {
case ' n ' : strcpy ( verb , " NORTH " ) ; break ;
case ' e ' : strcpy ( verb , " EAST " ) ; break ;
case ' s ' : strcpy ( verb , " SOUTH " ) ; break ;
case ' w ' : strcpy ( verb , " WEST " ) ; break ;
case ' u ' : strcpy ( verb , " UP " ) ; break ;
case ' d ' : strcpy ( verb , " DOWN " ) ; break ;
/* Brian Howarth interpreter also supports this */
case ' i ' : strcpy ( verb , " INVENTORY " ) ; break ;
}
}
nc = WhichWord ( verb , Nouns ) ;
/* The Scott Adams system has a hack to avoid typing 'go' */
if ( nc > = 1 & & nc < = 6 ) {
vc = 1 ;
} else {
vc = WhichWord ( verb , Verbs ) ;
nc = WhichWord ( noun , Nouns ) ;
}
* vb = vc ;
* no = nc ;
if ( vc = = - 1 ) {
Output ( " You use word(s) I don't know! " ) ;
}
} while ( vc = = - 1 ) ;
strcpy ( NounText , noun ) ; /* Needed by GET/DROP hack */
return 0 ;
}
int Scott : : PerformLine ( int ct ) {
int continuation = 0 ;
int param [ 5 ] , pptr = 0 ;
int act [ 4 ] ;
int cc = 0 ;
while ( cc < 5 ) {
int cv , dv ;
cv = Actions [ ct ] . Condition [ cc ] ;
dv = cv / 20 ;
cv % = 20 ;
switch ( cv ) {
case 0 :
param [ pptr + + ] = dv ;
break ;
case 1 :
if ( Items [ dv ] . Location ! = CARRIED )
return 0 ;
break ;
case 2 :
if ( Items [ dv ] . Location ! = MyLoc )
return 0 ;
break ;
case 3 :
if ( Items [ dv ] . Location ! = CARRIED & &
Items [ dv ] . Location ! = MyLoc )
return 0 ;
break ;
case 4 :
if ( MyLoc ! = dv )
return 0 ;
break ;
case 5 :
if ( Items [ dv ] . Location = = MyLoc )
return 0 ;
break ;
case 6 :
if ( Items [ dv ] . Location = = CARRIED )
return 0 ;
break ;
case 7 :
if ( MyLoc = = dv )
return 0 ;
break ;
case 8 :
if ( ( BitFlags & ( 1 < < dv ) ) = = 0 )
return 0 ;
break ;
case 9 :
if ( BitFlags & ( 1 < < dv ) )
return 0 ;
break ;
case 10 :
if ( CountCarried ( ) = = 0 )
return 0 ;
break ;
case 11 :
if ( CountCarried ( ) )
return 0 ;
break ;
case 12 :
if ( Items [ dv ] . Location = = CARRIED | | Items [ dv ] . Location = = MyLoc )
return 0 ;
break ;
case 13 :
if ( Items [ dv ] . Location = = 0 )
return 0 ;
break ;
case 14 :
if ( Items [ dv ] . Location )
return 0 ;
break ;
case 15 :
if ( CurrentCounter > dv )
return 0 ;
break ;
case 16 :
if ( CurrentCounter < = dv )
return 0 ;
break ;
case 17 :
if ( Items [ dv ] . Location ! = Items [ dv ] . InitialLoc )
return 0 ;
break ;
case 18 :
if ( Items [ dv ] . Location = = Items [ dv ] . InitialLoc )
return 0 ;
break ;
case 19 : /* Only seen in Brian Howarth games so far */
if ( CurrentCounter ! = dv )
return 0 ;
break ;
}
cc + + ;
}
/* Actions */
act [ 0 ] = Actions [ ct ] . action [ 0 ] ;
act [ 2 ] = Actions [ ct ] . action [ 1 ] ;
act [ 1 ] = act [ 0 ] % 150 ;
act [ 3 ] = act [ 2 ] % 150 ;
act [ 0 ] / = 150 ;
act [ 2 ] / = 150 ;
cc = 0 ;
pptr = 0 ;
while ( cc < 4 )
{
if ( act [ cc ] > = 1 & & act [ cc ] < 52 ) {
Output ( Messages [ act [ cc ] ] ) ;
Output ( " \n " ) ;
} else if ( act [ cc ] > 101 ) {
Output ( Messages [ act [ cc ] - 50 ] ) ;
Output ( " \n " ) ;
}
else {
switch ( act [ cc ] ) {
case 0 : /* NOP */
break ;
case 52 :
if ( CountCarried ( ) = = GameHeader . MaxCarry )
{
if ( Options & YOUARE )
Output ( " You are carrying too much. " ) ;
else
Output ( " I've too much to carry! " ) ;
break ;
}
Items [ param [ pptr + + ] ] . Location = CARRIED ;
break ;
case 53 :
Items [ param [ pptr + + ] ] . Location = MyLoc ;
break ;
case 54 :
MyLoc = param [ pptr + + ] ;
break ;
case 55 :
Items [ param [ pptr + + ] ] . Location = 0 ;
break ;
case 56 :
BitFlags | = 1 < < DARKBIT ;
break ;
case 57 :
BitFlags & = ~ ( 1 < < DARKBIT ) ;
break ;
case 58 :
BitFlags | = ( 1 < < param [ pptr + + ] ) ;
break ;
case 59 :
Items [ param [ pptr + + ] ] . Location = 0 ;
break ;
case 60 :
BitFlags & = ~ ( 1 < < param [ pptr + + ] ) ;
break ;
case 61 :
if ( Options & YOUARE )
Output ( " You are dead. \n " ) ;
else
Output ( " I am dead. \n " ) ;
BitFlags & = ~ ( 1 < < DARKBIT ) ;
MyLoc = GameHeader . NumRooms ; /* It seems to be what the code says! */
break ;
case 62 :
{
/* Bug fix for some systems - before it could get parameters wrong */
int i = param [ pptr + + ] ;
Items [ i ] . Location = param [ pptr + + ] ;
break ;
}
case 63 :
doneit : Output ( " The game is now over. \n " ) ;
glk_exit ( ) ;
case 64 :
break ;
case 65 :
{
int i = 0 ;
int n = 0 ;
while ( i < = GameHeader . NumItems )
{
if ( Items [ i ] . Location = = GameHeader . TreasureRoom & &
* Items [ i ] . Text = = ' * ' )
n + + ;
i + + ;
}
if ( Options & YOUARE )
Output ( " You have stored " ) ;
else
Output ( " I've stored " ) ;
OutputNumber ( n ) ;
Output ( " treasures. On a scale of 0 to 100, that rates " ) ;
OutputNumber ( ( n * 100 ) / GameHeader . Treasures ) ;
Output ( " . \n " ) ;
if ( n = = GameHeader . Treasures )
{
Output ( " Well done. \n " ) ;
goto doneit ;
}
break ;
}
case 66 :
{
int i = 0 ;
int f = 0 ;
if ( Options & YOUARE )
Output ( " You are carrying: \n " ) ;
else
Output ( " I'm carrying: \n " ) ;
while ( i < = GameHeader . NumItems )
{
if ( Items [ i ] . Location = = CARRIED )
{
if ( f = = 1 )
{
if ( Options & TRS80_STYLE )
Output ( " . " ) ;
else
Output ( " - " ) ;
}
f = 1 ;
Output ( Items [ i ] . Text ) ;
}
i + + ;
}
if ( f = = 0 )
Output ( " Nothing " ) ;
Output ( " . \n " ) ;
break ;
}
case 67 :
BitFlags | = ( 1 < < 0 ) ;
break ;
case 68 :
BitFlags & = ~ ( 1 < < 0 ) ;
break ;
case 69 :
GameHeader . LightTime = LightRefill ;
Items [ LIGHT_SOURCE ] . Location = CARRIED ;
BitFlags & = ~ ( 1 < < LIGHTOUTBIT ) ;
break ;
case 70 :
ClearScreen ( ) ; /* pdd. */
break ;
case 71 :
SaveGame ( ) ;
break ;
case 72 :
{
int i1 = param [ pptr + + ] ;
int i2 = param [ pptr + + ] ;
int t = Items [ i1 ] . Location ;
Items [ i1 ] . Location = Items [ i2 ] . Location ;
Items [ i2 ] . Location = t ;
break ;
}
case 73 :
continuation = 1 ;
break ;
case 74 :
Items [ param [ pptr + + ] ] . Location = CARRIED ;
break ;
case 75 :
{
int i1 , i2 ;
i1 = param [ pptr + + ] ;
i2 = param [ pptr + + ] ;
Items [ i1 ] . Location = Items [ i2 ] . Location ;
break ;
}
case 76 : /* Looking at adventure .. */
break ;
case 77 :
if ( CurrentCounter > = 0 )
CurrentCounter - - ;
break ;
case 78 :
OutputNumber ( CurrentCounter ) ;
break ;
case 79 :
CurrentCounter = param [ pptr + + ] ;
break ;
case 80 :
{
int t = MyLoc ;
MyLoc = SavedRoom ;
SavedRoom = t ;
break ;
}
case 81 :
{
/* This is somewhat guessed. Claymorgue always
seems to do select counter n , thing , select counter n ,
but uses one value that always seems to exist . Trying
a few options I found this gave sane results on ageing */
int t = param [ pptr + + ] ;
int c1 = CurrentCounter ;
CurrentCounter = Counters [ t ] ;
Counters [ t ] = c1 ;
break ;
}
case 82 :
CurrentCounter + = param [ pptr + + ] ;
break ;
case 83 :
CurrentCounter - = param [ pptr + + ] ;
if ( CurrentCounter < - 1 )
CurrentCounter = - 1 ;
/* Note: This seems to be needed. I don't yet
know if there is a maximum value to limit too */
break ;
case 84 :
Output ( NounText ) ;
break ;
case 85 :
Output ( NounText ) ;
Output ( " \n " ) ;
break ;
case 86 :
Output ( " \n " ) ;
break ;
case 87 :
{
/* Changed this to swap location<->roomflag[x]
not roomflag 0 and x */
int p = param [ pptr + + ] ;
int sr = MyLoc ;
MyLoc = RoomSaved [ p ] ;
RoomSaved [ p ] = sr ;
break ;
}
case 88 :
Delay ( 2 ) ;
break ;
case 89 :
pptr + + ;
/* SAGA draw picture n */
/* Spectrum Seas of Blood - start combat ? */
/* Poking this into older spectrum games causes a crash */
break ;
default :
error ( " Unknown action %d [Param begins %d %d] \n " ,
act [ cc ] , param [ pptr ] , param [ pptr + 1 ] ) ;
break ;
}
}
cc + + ;
}
return 1 + continuation ;
}
int Scott : : PerformActions ( int vb , int no ) {
static int disable_sysfunc = 0 ; /* Recursion lock */
int d = BitFlags & ( 1 < < DARKBIT ) ;
int ct = 0 ;
int fl ;
int doagain = 0 ;
if ( vb = = 1 & & no = = - 1 ) {
Output ( " Give me a direction too. " ) ;
return 0 ;
}
if ( vb = = 1 & & no > = 1 & & no < = 6 ) {
int nl ;
if ( Items [ LIGHT_SOURCE ] . Location = = MyLoc | |
Items [ LIGHT_SOURCE ] . Location = = CARRIED )
d = 0 ;
if ( d )
Output ( " Dangerous to move in the dark! " ) ;
nl = Rooms [ MyLoc ] . Exits [ no - 1 ] ;
if ( nl ! = 0 ) {
MyLoc = nl ;
return 0 ;
}
if ( d ) {
if ( Options & YOUARE )
Output ( " You fell down and broke your neck. " ) ;
else
Output ( " I fell down and broke my neck. " ) ;
glk_exit ( ) ;
}
if ( Options & YOUARE )
Output ( " You can't go in that direction. " ) ;
else
Output ( " I can't go in that direction. " ) ;
return 0 ;
}
fl = - 1 ;
while ( ct < = GameHeader . NumActions ) {
int vv , nv ;
vv = Actions [ ct ] . Vocab ;
/* Think this is now right. If a line we run has an action73
run all following lines with vocab of 0 , 0 */
if ( vb ! = 0 & & ( doagain & & vv ! = 0 ) )
break ;
/* Oops.. added this minor cockup fix 1.11 */
if ( vb ! = 0 & & ! doagain & & fl = = 0 )
break ;
nv = vv % 150 ;
vv / = 150 ;
if ( ( vv = = vb ) | | ( doagain & & Actions [ ct ] . Vocab = = 0 ) ) {
if ( ( vv = = 0 & & RandomPercent ( nv ) ) | | doagain | |
( vv ! = 0 & & ( nv = = no | | nv = = 0 ) ) ) {
int f2 ;
if ( fl = = - 1 )
fl = - 2 ;
if ( ( f2 = PerformLine ( ct ) ) > 0 ) {
/* ahah finally figured it out ! */
fl = 0 ;
if ( f2 = = 2 )
doagain = 1 ;
if ( vb ! = 0 & & doagain = = 0 )
return 0 ;
}
}
}
ct + + ;
/* Previously this did not check ct against
* GameHeader . NumActions and would read past the end of
* Actions . I don ' t know what should happen on the last
* action , but doing nothing is better than reading one
* past the end .
* - - Chris
*/
if ( ct < = GameHeader . NumActions & & Actions [ ct ] . Vocab ! = 0 )
doagain = 0 ;
}
if ( fl ! = 0 & & disable_sysfunc = = 0 ) {
int item ;
if ( Items [ LIGHT_SOURCE ] . Location = = MyLoc | |
Items [ LIGHT_SOURCE ] . Location = = CARRIED )
d = 0 ;
if ( vb = = 10 | | vb = = 18 ) {
/* Yes they really _are_ hardcoded values */
if ( vb = = 10 ) {
if ( xstrcasecmp ( NounText , " ALL " ) = = 0 ) {
int i = 0 ;
int f = 0 ;
if ( d ) {
Output ( " It is dark. \n " ) ;
return 0 ;
}
while ( i < = GameHeader . NumItems ) {
if ( Items [ i ] . Location = = MyLoc & & Items [ i ] . AutoGet ! = nullptr & & Items [ i ] . AutoGet [ 0 ] ! = ' * ' ) {
no = WhichWord ( Items [ i ] . AutoGet , Nouns ) ;
disable_sysfunc = 1 ; /* Don't recurse into auto get ! */
PerformActions ( vb , no ) ; /* Recursively check each items table code */
disable_sysfunc = 0 ;
if ( CountCarried ( ) = = GameHeader . MaxCarry ) {
if ( Options & YOUARE )
Output ( " You are carrying too much. " ) ;
else
Output ( " I've too much to carry. " ) ;
return 0 ;
}
Items [ i ] . Location = CARRIED ;
Output ( Items [ i ] . Text ) ;
Output ( " : O.K. \n " ) ;
f = 1 ;
}
i + + ;
}
if ( f = = 0 )
Output ( " Nothing taken. " ) ;
return 0 ;
}
if ( no = = - 1 )
{
Output ( " What ? " ) ;
return 0 ;
}
if ( CountCarried ( ) = = GameHeader . MaxCarry )
{
if ( Options & YOUARE )
Output ( " You are carrying too much. " ) ;
else
Output ( " I've too much to carry. " ) ;
return 0 ;
}
item = MatchUpItem ( NounText , MyLoc ) ;
if ( item = = - 1 )
{
if ( Options & YOUARE )
Output ( " It is beyond your power to do that. " ) ;
else
Output ( " It's beyond my power to do that. " ) ;
return 0 ;
}
Items [ item ] . Location = CARRIED ;
Output ( " O.K. " ) ;
return 0 ;
}
if ( vb = = 18 ) {
if ( xstrcasecmp ( NounText , " ALL " ) = = 0 ) {
int i = 0 ;
int f = 0 ;
while ( i < = GameHeader . NumItems ) {
if ( Items [ i ] . Location = = CARRIED & & Items [ i ] . AutoGet & & Items [ i ] . AutoGet [ 0 ] ! = ' * ' ) {
no = WhichWord ( Items [ i ] . AutoGet , Nouns ) ;
disable_sysfunc = 1 ;
PerformActions ( vb , no ) ;
disable_sysfunc = 0 ;
Items [ i ] . Location = MyLoc ;
Output ( Items [ i ] . Text ) ;
Output ( " : O.K. \n " ) ;
f = 1 ;
}
i + + ;
}
if ( f = = 0 )
Output ( " Nothing dropped. \n " ) ;
return 0 ;
}
if ( no = = - 1 ) {
Output ( " What ? " ) ;
return 0 ;
}
item = MatchUpItem ( NounText , CARRIED ) ;
if ( item = = - 1 ) {
if ( Options & YOUARE )
Output ( " It's beyond your power to do that. \n " ) ;
else
Output ( " It's beyond my power to do that. \n " ) ;
return 0 ;
}
Items [ item ] . Location = MyLoc ;
Output ( " O.K. " ) ;
return 0 ;
}
}
}
return fl ;
}
int Scott : : xstrcasecmp ( const char * s1 , const char * s2 ) {
const unsigned char
* us1 = ( const unsigned char * ) s1 ,
* us2 = ( const unsigned char * ) s2 ;
while ( tolower ( * us1 ) = = tolower ( * us2 + + ) )
if ( * us1 + + = = ' \0 ' )
return ( 0 ) ;
return ( tolower ( * us1 ) - tolower ( * - - us2 ) ) ;
}
int Scott : : xstrncasecmp ( const char * s1 , const char * s2 , size_t n ) {
if ( n ! = 0 ) {
const unsigned char
* us1 = ( const unsigned char * ) s1 ,
* us2 = ( const unsigned char * ) s2 ;
do {
if ( tolower ( * us1 ) ! = tolower ( * us2 + + ) )
return ( tolower ( * us1 ) - tolower ( * - - us2 ) ) ;
if ( * us1 + + = = ' \0 ' )
break ;
} while ( - - n ! = 0 ) ;
}
return 0 ;
}
void Scott : : readInts ( Common : : SeekableReadStream * f , size_t count , . . . ) {
va_list va ;
va_start ( va , count ) ;
unsigned char c = ' \0 ' ;
for ( size_t idx = 0 ; idx < count ; + + idx ) {
if ( idx > 0 ) {
while ( f - > pos ( ) < f - > size ( ) & & Common : : isSpace ( c ) )
c = f - > readByte ( ) ;
} else {
c = f - > readByte ( ) ;
}
// Get the next value
int * val = ( int * ) va_arg ( va , int ) ;
* val = 0 ;
while ( Common : : isDigit ( c ) ) {
* val = ( * val * 10 ) + ( c - ' 0 ' ) ;
c = f - > readByte ( ) ;
}
}
va_end ( va ) ;
2018-10-16 21:06:07 -07:00
}
2018-10-15 21:47:27 -07:00
} // End of namespace Scott
} // End of namespace Gargoyle