All pastes #580500 Raw Edit

Someone

public text v1 · immutable
·published 2007-06-21 03:01 UTC
rendered paste body
{-# 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__)