{-# enc CP932 #-
rlBabel.kh: dynamically lineated text display engine
with proportional text support.
Copyright (c) 2006 Peter Jolly.
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
As a special exception to the GNU Lesser General Public License, you
may include a publicly distributed version of the library in a "work
that uses the Library" to produce a scenario file containing
portions of the library, and distribute that scenario file under
terms of your choice, without any of the additional requirements
listed in clause 6 of the GNU Lesser General Public License. A
"publicly distributed version of the library" means either an
unmodified library as distributed by Haeleth, or a modified version
of the library that is distributed under the conditions defined in
clause 2 of the GNU Lesser General Public License. Note that this
exception does not invalidate any other reasons why the scenario
file might be covered by the GNU Lesser General Public License.
-}
#ifdef __TEXTOUT_KH__
#error 'The Textout and rlBabel libraries are mutually exclusive: you can only use one or the other.'
#endif
#ifndef __RLBABEL_KH__
#define __RLBABEL_KH__
#ifndef __DynamicLineation__ #define __DynamicLineation__ #endif
#if !__DynamicLineation__ #set __DynamicLineation__ = 1 #endif
#ifdef __dynamic_textout_print #undef __dynamic_textout_print #endif
#if target_lt (1,2)
#error 'The rlBabel library can only be used with RealLive 1.2 or greater.'
#endif
#const OldType = target_lt (1,2,5)
#ifndef rlBabel
#if OldType
#const rlBabel = 'rlBabelF'
#else
#const rlBabel = 'rlBabel'
#endif
#endif
// DLL index... this won't apply to 1.2.3.5 etc, of course.
#ifndef rlBabelDLL
#const rlBabelDLL = -1
#if OldType
#set rlBabelDLL = 0
#else
#for i = 0 .. 15:
#if rlBabelDLL == -1
#if __equal_strings? (gameexe ('DLL.\i:3{i}', 0, ''), rlBabel)
#set rlBabelDLL = i
#endif
#endif;
#if rlBabelDLL == -1
#error 'load of ' + rlBabel + ' not found'
#endif
#endif
#endif
// Static variables
#if default (__DebugTextout__, 0) && !defined? (__NoDebug__) #define __AllLabelled__ #endif
int __rlb_glosses_active, __rlb_temp[4]
str __rlb_buffer, __rlb_empty, __rlb_nest_buffer
#if default (__DebugTextout__, 0) && !defined? (__NoDebug__) #undef __AllLabelled__ #endif
// Function IDs: DLL
#const dllInitialise = 0
// Function IDs: Textout
#const dllTextoutStart = 10,
dllTextoutAppend = 11,
dllTextoutGetChar = 12,
dllTextoutNewScreen = 13,
dllClearGlosses = 20,
dllNewGloss = 21,
dllAddGloss = 22,
dllTestGlosses = 23
// Function IDs: Utility
#const dllSetNameMod = 100,
dllGetNameMod = 101,
dllSetWindowName = 102,
dllGetTextWindow = 103,
dllGetRCommandMod = 104,
dllMessageBox = 105
// Function IDs: Select
#const dllSelectAdd = 200
// Return values
#const getcError = 0,
getcEndOfString = 1,
getcPrintChar = 2,
getcNewLine = 3,
getcNewScreen = 4,
getcSetIndent = 5,
getcClearIndent = 6,
getcBeginGloss = 7
// DLL wrappers: DLL
#inline __initialise_dynamic_textout ():
#if OldType
if !LoadDLL (0, rlBabel)
rlcError ('failed to initialise text rendering engine')
#endif
strclear (__rlb_empty)
__rlb_glosses_active = 0
objOfText (255, 'A') // to force the font to load -_-;
return CallDLL (rlBabelDLL, dllInitialise, rlBabelDLL);
// Public utility functions
#inline GetTextWindow ():
return CallDLL (rlBabelDLL, dllGetTextWindow);
#inline SetNameMod (__value, __window = -1):
return CallDLL (rlBabelDLL, dllSetNameMod, __window, __value);
#inline GetNameMod (__window = -1):
return CallDLL (rlBabelDLL, dllGetNameMod, __window);
#inline SetCurrentWindowName (__value):
#if !__variable? (__value)
str s = __value
CallDLL (rlBabelDLL, dllSetWindowName, __addr(s))
#else
CallDLL (rlBabelDLL, dllSetWindowName, __addr(__value))
#endif
return store;
#inline GetRCommandMod (__window = -1):
return CallDLL (rlBabelDLL, dllGetRCommandMod, __window);
#inline MessageBox (__text, __title = 0):
#if integer? (__title)
#bind tval = __title
#elseif __variable? (__title)
#const tval = __addr(__title)
#else
str t = __title
#const tval = __addr(t)
#endif
#if !__variable? (__text)
str s
if integer? (__text), s = itoa (__text) else s = __text
CallDLL (rlBabelDLL, dllMessageBox, __addr(s), tval)
#else
CallDLL (rlBabelDLL, dllMessageBox, __addr(__text), tval)
#endif
return store;
// DLL wrappers: Textout
#inline __vwf_TryLoad()
if !LoadDLL (0, rlBabel)
rlcError ('failed to initialise text rendering engine')
#inline __vwf_TextoutStart (__string, __is_start = 1):
// Initialise textout and print an empty token to fix a save point here.
#ifndef __DynamicLineationUsed__ #define __DynamicLineationUsed__ #endif
if __is_start, strout (__rlb_empty)
// Define the argument to pass the startup routine.
#if !__variable? (__string)
#if __empty_string? (__string)
#sdefine arg = -1
#else
__rlb_buffer = __string
#sdefine arg = __addr(__rlb_buffer)
#endif
#else
#sdefine arg = __addr(__string)
#endif
#if OldType
// Old versions unload the DLL when a saved game is loaded. We have to
// check for such failures and force it to be reloaded when necessary.
while !CallDLL (rlBabelDLL, dllTextoutStart, arg) __vwf_TryLoad
#else
CallDLL (rlBabelDLL, dllTextoutStart, arg)
#endif
return store;
#inline __vwf_TextoutAppend (__string):
#if !__variable? (__string)
#if !__empty_string? (__string)
__rlb_buffer = __string
CallDLL (rlBabelDLL, dllTextoutAppend, __addr(__rlb_buffer))
#endif
#else
CallDLL (rlBabelDLL, dllTextoutAppend, __addr(__string))
#endif;
// DLL wrappers: Select
#inline __vwf_SelectInit (__window, [__string1], [__string2], [__string3]):
#ifndef __DynamicLineationUsed__ #define __DynamicLineationUsed__ #endif
strout (__rlb_empty)
DisableAutoSavepoints
#if OldType
// Old versions unload the DLL when a saved game is loaded. We have to
// check for such failures and force it to be reloaded when necessary.
#if defined?(__string3)
while !CallDLL (rlBabelDLL, dllSelectAdd, __window, __addr(__string1), __addr(__string2), __addr(__string3)) __vwf_TryLoad
#elseif defined?(__string2)
while !CallDLL (rlBabelDLL, dllSelectAdd, __window, __addr(__string1), __addr(__string2)) __vwf_TryLoad
#elseif defined?(__string1)
while !CallDLL (rlBabelDLL, dllSelectAdd, __window, __addr(__string1)) __vwf_TryLoad
#else
while !CallDLL (rlBabelDLL, dllSelectAdd, __window) __vwf_TryLoad
#endif
#else
#if defined?(__string3)
CallDLL (rlBabelDLL, dllSelectAdd, __window, __addr(__string1), __addr(__string2), __addr(__string3))
#elseif defined?(__string2)
CallDLL (rlBabelDLL, dllSelectAdd, __window, __addr(__string1), __addr(__string2))
#elseif defined?(__string1)
CallDLL (rlBabelDLL, dllSelectAdd, __window, __addr(__string1))
#else
CallDLL (rlBabelDLL, dllSelectAdd, __window)
#endif
#endif;
#inline __vwf_SelectAdd (__window, [__string1], [__string2], [__string3]):
#if defined?(__string3)
CallDLL (rlBabelDLL, dllSelectAdd, __window, __addr(__string1), __addr(__string2), __addr(__string3))
#elseif defined?(__string2)
CallDLL (rlBabelDLL, dllSelectAdd, __window, __addr(__string1), __addr(__string2))
#elseif defined?(__string1)
CallDLL (rlBabelDLL, dllSelectAdd, __window, __addr(__string1))
#else
CallDLL (rlBabelDLL, dllSelectAdd, __window)
#endif;
#inline __vwf_SelectCleanup (__dest = store):
EnableAutoSavepoints
return __dest;
// DLL wrappers: glosses
#inline __vwf_BeginGloss ():
FontColour (6) // TODO: make customisable, disableable, etc.
CallDLL (rlBabelDLL, dllNewGloss)
__rlb_glosses_active += 1
;
#inline __vwf_GlossTextStart (__string):
__rlb_buffer = __string;
#inline __vwf_GlossTextAppend (__string):
__rlb_buffer += __string;
#inline __vwf_GlossTextSet ([__setstring]):
#ifdef __setstring
__vwf_GlossTextStart (__setstring) // different name because naive compiler = name capture = nontermination
#endif;
#inline __vwf_EndGloss ():
CallDLL (rlBabelDLL, dllAddGloss, __addr(__rlb_buffer))
FontColour
;
// Pause function wrappers
#define __HideFunctionsSilently__
#const rlbPauseContinue = 0,
rlbPauseClear = 1,
rlbPauseClearUnlessNovel = 2
#inline __vwf_DoPauseFunc (__func, __clear):
if defined?(__EnableGlosses__) && __rlb_glosses_active
rlc_parse_string ('gosub @__vwf_handle_glosses_\i{__clear}__')
else
at (__CALLER_FILE__, __CALLER_LINE__, __func)
;
#inline pause () __vwf_DoPauseFunc ('pause', rlbPauseClearUnlessNovel)
#inline spause () __vwf_DoPauseFunc ('spause', rlbPauseContinue)
#inline page () __vwf_DoPauseFunc ('page', rlbPauseClear)
{- Wrap direct calls to breaking functions with calls to the textout routines.
This will ensure that screen overflows are handled properly.
A consequence is that it becomes more space-efficient to use control codes
in strings than to call the functions directly. -}
#inline br () '\n'
#inline par () '\r'
#undef __HideFunctionsSilently__
// The main rlBabel functions
#inline __vwf_TextoutDisplay ([__text]):
#bind a = __rlb_temp[0], buf = __rlb_buffer
#ifndef __text
gosub @__textoutdisplay__
#elseif __empty_string? (__text)
gosub @__textoutdisplay__
#else
// Initialise textout and print an empty token to fix a save point here.
#ifndef __DynamicLineationUsed__ #define __DynamicLineationUsed__ #endif
// strout (__rlb_empty) // let's try moving this to rlBabel.ml
#if !__variable? (__text)
buf = __text
gosub @__textoutdisplay_start_buffer__
#else
a = __addr(__text) & $fff
gosub @__textoutdisplay_start__
#endif
#endif;
#inline __dynamic_textout_print ():
// Inserted at the end of scenarios using rlBabel
#bind a = __rlb_temp[0], b = __rlb_temp[1], x = __rlb_temp[2], y = __rlb_temp[3], buf = __rlb_buffer
halt
@__textoutdisplay_start__
__vwf_TextoutStart (strS[a], 0)
goto @__textoutdisplay__
@__textoutdisplay_start_buffer__
__vwf_TextoutStart (buf, 0)
@__textoutdisplay__
DisableAutoSavepoints
stralloc (__rlb_buffer, 512)
while 1
case CallDLL (rlBabelDLL, dllTextoutGetChar, __addr(buf), __addr(a))
of getcNewLine
#hiding br, br
continue
of getcNewScreen
page
TextPos (0, 0) // page() doesn't reset the values that are otherwise perfect position counters?
CallDLL (rlBabelDLL, dllTextoutNewScreen, __addr(buf))
continue
of getcSetIndent
SetIndent
continue
of getcClearIndent
ClearIndent
continue
of getcBeginGloss
__vwf_BeginGloss
// fall through
of getcPrintChar
strout (buf)
TextPosX (a)
continue
of getcError
__rlb_empty = buf
buf = "Error: "
buf += __rlb_empty
buf += ". Unable to format text."
strclear (__rlb_empty)
FastText
strout (buf)
NormalText
of getcEndOfString
other
EnableAutoSavepoints
ret
ecase
#ifdef __EnableGlosses__
// Pause handling in the presence of glosses.
@__vwf_handle_glosses_0__
a = rlbPauseContinue
goto @__vwf_handle_glosses__
@__vwf_handle_glosses_1__
a = rlbPauseClear
goto @__vwf_handle_glosses__
@__vwf_handle_glosses_2__
a = rlbPauseClearUnlessNovel
@__vwf_handle_glosses__
stralloc (__rlb_nest_buffer, 2048)
while 1:
FlushClick
#hiding spause, spause
GetCursorPos (x, y, b, store)
if b != 2, break
if !CallDLL (rlBabelDLL, dllTestGlosses, x, y, __addr(__rlb_nest_buffer), GetWakuAll), break
#ifdef rlBabelGlossCallback
// If the user has supplied a gloss callback, do what they want.
rlBabelGlossCallback (__rlb_nest_buffer)
#else
// Default gloss behaviour: simple & sane
MessageBox (__rlb_nest_buffer, 1)
#endif;
case a
of rlbPauseClearUnlessNovel
if GetRCommandMod:
par
break;
of rlbPauseClear
__rlb_glosses_active = 0
msgClear
CallDLL (rlBabelDLL, dllClearGlosses)
of rlbPauseContinue
ecase
store = 1
ret
#endif;
#endif // !defined? (__VWF_KH__)