/////////////////////////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2015 The 8-Bit Bunch. Licensed under the Apache License, Version 1.1 // (the "License"); you may not use this file except in compliance with the License. // You may obtain a copy of the License at . // Unless required by applicable law or agreed to in writing, software distributed under // the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF // ANY KIND, either express or implied. See the License for the specific language // governing permissions and limitations under the License. /////////////////////////////////////////////////////////////////////////////////////////////////// include "gamelib.plh" /////////////////////////////////////////////////////////////////////////////////////////////////// // Fixed memory locations const seed = $4E // Incremented continuously by keyboard read routine const displayEngine = $6000 // main mem (raycaster and tile engine at same location) const expandVec = $2000 // aux mem (only for raycaster) const fontEngine = $E000 // main mem const heapStart = $F000 // main mem const heapSize = $800 /////////////////////////////////////////////////////////////////////////////////////////////////// // Other constants const kbd = $C000 const kbdStrobe = $C010 const CHAR_WND_LIFE_X = 91 const CHAR_WND_GUN_X = 114 const ANIM_FLAG_FWD = $20 const ANIM_FLAG_FWD_BKWD = $40 const ANIM_FLAG_RANDOM = $80 const ANIM_PAUSE_MAX = 300 include "playtype.plh" include "gen_images.plh" include "gen_modules.plh" include "gen_enemies.plh" include "globalScripts.plh" // old-style include "gen_globalScripts.plh" // new-style include "combat.plh" include "party.plh" /////////////////////////////////////////////////////////////////////////////////////////////////// // Data structures include "playtype.pla" word global // the global heap object, from which all live objects must be reachable /////////////////////////////////////////////////////////////////////////////////////////////////// // Predefined functions, for circular calls or out-of-order calls predef setWindow2, initCmds, nextAnimFrame, checkEncounter /////////////////////////////////////////////////////////////////////////////////////////////////// // Global variables byte mapNum = -1 byte mapIs3D = -1 word mapNameHash = 0 word totalMapWidth word totalMapHeight byte needRender = FALSE byte renderLoaded = FALSE byte textDrawn = FALSE byte isPlural = FALSE word skyNum = 9 word groundNum = 10 byte portraitNum = 0 word triggerOriginX, triggerOriginY word triggerTbl word cmdTbl[64] byte frameLoaded = 0 // Queue setMap / teleport, since otherwise script might be replaced while executing byte q_mapIs3D = 0 byte q_mapNum = 1 word q_x = 0 word q_y = 0 byte q_dir = 0 // For decimal conversion and display tabbing byte decimalBuf[7] byte tabBuf[5] // Animation tracking word curPortrait = 0 byte animFlags byte animDir byte animDirCt byte animNumFrames byte animFrame word animPauseCt // Game library functions predef _setScriptInfo, _scriptDisplayStr, _scriptDisplayStrNL, _getYN predef _queue_setMap, _setSky, _setGround, _queue_teleport, _setPortrait, _clearPortrait predef _moveBackward, _getUpperKey, _clearWindow, _setPortrait predef _getGlobals, _rand16 predef _printf1, _printf2, _printf3 predef _displayf1, _displayf2, _displayf3 predef _buildString, _addToString, _finishString, _rawDisplayStr, _displayStr predef _puts, _min, _max predef _countList, _countListFiltered, _randomFromListFiltered, _addToList, _beep predef _showParty, _mmgr, _setWindow1, _setWindow2, _setWindow3 predef _reboot, _brk, _encodeDice, _rollDice predef _setPlural, _makeEnemy, _getStringResponse, _strcmpi, _addEncounterZone, _fatal predef _pause, _tossStrings, _showMapName, _setMapWindow word gameLib_addrs = @_setScriptInfo, @_scriptDisplayStr, @_scriptDisplayStrNL, @_getYN word = @_queue_setMap, @_setSky, @_setGround, @_queue_teleport, @_setPortrait, @_clearPortrait word = @_moveBackward, @_getUpperKey, @_clearWindow word = @_getGlobals, @_rand16 word = @_printf1, @_printf2, @_printf3 word = @_displayf1, @_displayf2, @_displayf3 word = @_buildString, @_addToString, @_finishString, @_rawDisplayStr, @_displayStr word = @_puts, @_min, @_max word = @_countList, @_countListFiltered, @_randomFromListFiltered, @_addToList, @_beep word = @_showParty, @_mmgr, @_setWindow1, @_setWindow2, @_setWindow3 word = @_reboot, @_brk, @_encodeDice, @_rollDice word = @_setPlural, @_makeEnemy, @_getStringResponse, @_strcmpi, @_addEncounterZone, @_fatal word = @_pause, @_tossStrings, @_showMapName, @_setMapWindow word = 0 // end of library functions /////////////////////////////////////////////////////////////////////////////////////////////////// // Definitions used by assembly code asm __defs ; Use hi-bit ASCII for Apple II !convtab "../../include/hiBitAscii.ct" ; Headers !source "../../include/global.i" !source "../../include/plasma.i" !source "../../include/mem.i" !source "../../include/fontEngine.i" ; Optional debug printing support DEBUG = 0 ; General use tmp = $2 pTmp = $4 ysav = $34 ysav1 = $35 ; 16-bit random number seed - incremented by ROM kbd routine seed = $4E magic = $2227 ; there are 2048 magic values that work; this one caught my eye. - MH ; NOTE ABOUT ABSOLUTE CODE ADDRESSING (e.g. STA .var, JMP .label, etc.) ; We cannot use it: this code, including variable space, can be loaded *anywhere*. ; So don't JMP to labels, declare any variables as !byte or !word here, etc. end /////////////////////////////////////////////////////////////////////////////////////////////////// // Temporary hack: after scriptDisplayStr is called, generated code calls this to clear the PLASMA // string pool. That way, many long strings can be used in a single function. asm _tossStrings lda framePtr sta outerFramePtr lda framePtr+1 sta outerFramePtr+1 dex rts end /////////////////////////////////////////////////////////////////////////////////////////////////// // API to call rendering engine (same API for raycaster and tile engine) asm initDisplay // params: mapNum, pMapData, x, y, dir +asmPlasm 5 jmp $6000 end asm flipToPage1 // no params +asmPlasm 0 jmp $6003 end asm getPos // params: @x, @y +asmPlasm 2 jmp $6006 end asm setPos // params: x, y +asmPlasm 2 jmp $6009 end asm getDir // no params; returns: dir (0-15) +asmPlasm 0 jmp $600C end asm setDir // params: dir (0-15) +asmPlasm 1 jmp $600F end asm advance // no params; return: 0 if same pos, 1 if new pos, 2 if new pos and scripted +asmPlasm 0 jmp $6012 end asm setColor // params: slot (0=sky/1=ground), color (0-17) +asmPlasm 2 jmp $6015 end asm render // no params +asmPlasm 0 jmp $6018 end asm texControl // params: load (1=load, 0=unload) +asmPlasm 1 jmp $601B end /////////////////////////////////////////////////////////////////////////////////////////////////// asm setAuxCopy rts end /////////////////////////////////////////////////////////////////////////////////////////////////// asm readAuxByte // params: ptr; ret: char +asmPlasm 1 ; Create the following subroutine, used to copy one char from aux to main: ;0010- 8D 03 C0 STA $C003 ;0013- AD XX XX LDA $XXXX ;0016- 8D 02 C0 STA $C002 ;0019- 60 RTS sta $14 sty $15 lda #$8D sta $10 sta $16 ldx #2 stx $17 inx stx $11 lda #$C0 sta $12 sta $18 lda #$AD sta $13 lda #$60 sta $19 ; And call the routine ldy #0 jmp $10 end /////////////////////////////////////////////////////////////////////////////////////////////////// // String building for display with the font engine. Includes plurality processing to handily // handle things like "Dirt bag(s)" and "his/their" asm _buildString +asmPlasm 1 sta cswl sty cswh lda #0 sta inbuf rts end asm _addToString sty ysav1 inc inbuf ldy inbuf sta inbuf,y ldy ysav1 rts end asm _finishString !zone { +asmPlasm 1 sta tmp ; save isPlural flag jsr setvid ; put the cout vector back to default bit monrts ; V flag for prev-is-punctuation ldy #1 ; dest offset in Y ldx #1 ; source offset in X cpx inbuf beq + ; only process if string has at least 1 char bcs .done + sty tmp+1 ; offset of last punctuation .fetch lda inbuf,x cmp #"(" bne .notpar bvs .notpar ; skip paren processing right punctuation lda tmp ; check isPlural flag bne .plurpr - lda inbuf,x ; it's singular, so skip everything in parens cmp #")" beq .next inx cpx inbuf bne - beq .done ; handle missing trailing paren .plurpr inx ; it's plural, so copy everything within the parens lda inbuf,x ; copy characters cpx inbuf ; handle missing trailing paren beq .store cmp #")" ; go until we reach ending paren beq .next sta inbuf,y iny bne .plurpr ; always taken .notpar cmp #"/" bne .notsl bvs .notsl ; skip slash processing right after punctuation lda tmp ; check isPlural flag bne .plursl - inx ; loop that skips plural form cpx inbuf beq + bcs .done ; handle end of string + lda inbuf,x cmp #"A" ; eat letters (and stop when we hit punctuation) bcs - bcc .store ; copy the ending punctuation and continue normal processing .plursl ldy tmp+1 ; erase singular form by backing up to prev punc iny ; plus 1 to retain prev punc bne .next ; resume regular copying of the plural form .notsl cmp #"A" ; if <= ASCII "A", consider it punctuation bcc + clv ; clear last-is-punc flag bvc .store ; always taken + bit monrts ; set prev-is-punc flag sty tmp+1 ; save dest offset of last punctuation .store sta inbuf,y ; save to dest iny .next inx cpx inbuf ; compare src offset to length bcc .fetch ; loop while less than beq .fetch ; or equal .done dey sty inbuf ; save new length lda #inbuf rts } end /////////////////////////////////////////////////////////////////////////////////////////////////// asm blitPortrait // params: srcData, dstScreenPtr +asmPlasm 2 ; Save the dest pointer sta pTmp sty pTmp+1 ; Save the source pointer lda evalStkL+1,x sta tmp lda evalStkH+1,x sta tmp+1 ; Create the following subroutine, used to copy pixels from aux to main: ; 0010- 8D 03 C0 STA $C003 ; 0013- B1 02 LDA ($02),Y ; 0015- 91 04 STA ($04),Y ; 0017- 88 DEY ; 0018- 10 F9 BPL $0013 ; 001A- 8D 02 C0 STA $C002 ; 001D- 60 RTS lda #$8D sta $10 sta $1A ldx #2 stx $14 stx $1B inx stx $11 lda #$C0 sta $12 sta $1C lda #$B1 sta $13 lda #$91 sta $15 inx stx $16 lda #$88 sta $17 lda #$10 sta $18 lda #$F9 sta $19 lda #$60 sta $1D ; Outer copy loop ldx #128 ; line count .loop ldy #17 ; byte count minus 1. There are 18 bytes per line jsr $10 ; copy pixel bytes ; Advance to next row of data lda tmp clc adc #18 sta tmp bcc + inc tmp+1 + ; Fun code to advance to the next hi-res line ldy pTmp+1 iny iny iny iny cpy #$40 bcc .ok2 tya sbc #$20 ; carry already set tay lda pTmp eor #$80 bmi .ok iny cpy #$24 bcc .ok ldy #$20 adc #$27 ; carry was set, so actually adding $28 .ok sta pTmp .ok2 sty pTmp+1 ; Loop until we've done all rows. dex bne .loop rts end /////////////////////////////////////////////////////////////////////////////////////////////////// // Simply retrieve the X register. Used to double-check that we're not leaking PLASMA eval // stack entries. asm getXReg +asmPlasm 0 txa ldy #0 rts end /////////////////////////////////////////////////////////////////////////////////////////////////// // Calculate 16-bit hash of a string asm hashString +asmPlasm 1 sta pTmp sty pTmp+1 ldy #0 sty tmp+1 lda (pTmp),y tax iny - clc adc (pTmp),y bcc + inc tmp+1 + asl rol tmp+1 bcc + ora #1 + asl rol tmp+1 bcc + ora #1 + asl rol tmp+1 bcc + ora #1 + iny dex bne - ldy tmp+1 rts end /////////////////////////////////////////////////////////////////////////////////////////////////// // Print a string to the current character output vector asm _puts +asmPlasm 1 sta pTmp sty pTmp+1 ldy #0 lda (pTmp),y tax iny - lda (pTmp),y ora #$80 jsr cout iny dex bne - rts end /////////////////////////////////////////////////////////////////////////////////////////////////// // Get a character from the keyboard asm rdkey +asmPlasm 0 jmp rdkey end /////////////////////////////////////////////////////////////////////////////////////////////////// // Print part of a string, until we hit the end or a '%' code. Return how far we got, or -1 for end. asm partialPrintf !zone { +asmPlasm 2 lda evalStkL+1,x ; get string pointer sta pTmp lda evalStkH+1,x sta pTmp+1 ldy #0 lda (pTmp),y ; get length byte sec sbc evalStkL,x ; minus offset sta tmp ; to count of characters left to print bcc .eos ; avoid overrunning beq .eos lda evalStkL,x ; get desired offset into string tay iny ; increment past length byte - lda (pTmp),y ora #$80 cmp #'%' ; stop if we hit % code beq + jsr cout iny dec tmp ; otherwise go until end of string bne - .eos ldy #$FF ; if we hit end of string, return -1 tya rts + dey ; adjust back for length byte tya ; that's the lo byte of return ldy #0 ; hi byte of return is zero rts } end /////////////////////////////////////////////////////////////////////////////////////////////////// // Print a 16-bit hex value asm printHex +asmPlasm 1 tax tya jmp prntax end /////////////////////////////////////////////////////////////////////////////////////////////////// // Print a single character asm printChar +asmPlasm 1 ora #$80 jmp cout end /////////////////////////////////////////////////////////////////////////////////////////////////// // Print a carriage return asm crout +asmPlasm 0 jmp crout end /////////////////////////////////////////////////////////////////////////////////////////////////// // Ring the bell asm _beep +asmPlasm 0 jmp bell end /////////////////////////////////////////////////////////////////////////////////////////////////// // Read a string from the keyboard, turn it into a PLASMA string and return a pointer to the string. asm readStr +asmPlasm 0 jsr getln1 txa pha beq + - lda inbuf-1,x and #$7F sta inbuf,x dex bne - + pla sta inbuf,x lda #inbuf rts end /////////////////////////////////////////////////////////////////////////////////////////////////// // Send a command to the memory manager // Params: cmd, wordParam asm _mmgr +asmPlasm 2 lda evalStkL+1,x ; command code pha ldy evalStkH,x ; address (or other param)... hi byte in Y lda evalStkL,x tax ; ...lo byte in X pla jsr mainLoader ; ret value in X=lo/Y=hi txa ; to A=lo/Y=hi for asmPlasm rts end // Aux version of memory manager command asm auxMmgr +asmPlasm 2 lda evalStkL+1,x ; command code pha ldy evalStkH,x ; address (or other param) lda evalStkL,x tax pla jsr auxLoader ; ret value in X=lo/Y=hi txa ; to A=lo/Y=hi for asmPlasm rts end /////////////////////////////////////////////////////////////////////////////////////////////////// // Show or hide the disk activity icon (at the top of hi-res page 1). The icon consists of a 4x4 // block of blue pixels surrounded by a black border. // Params: show/hide ($FF, or 0) asm diskActivity +asmPlasm 1 sta tmp ; save show(FF) / hide(0) flag ldx #0 stx pTmp ldy #$F8 ; offset of screen holes lda #$20 ; first line of screen is $2000 - sta pTmp+1 cmp #$30 ; pre-check for last line bit tmp ; check mode beq + lda (pTmp,x) ; show mode sta (pTmp),y lda #$85 bcc ++ ; first 4 lines lda #0 ; last line beq ++ ; always taken + lda (pTmp),y ; hide mode ++ sta (pTmp,x) lda pTmp+1 clc adc #4 cmp #$34 ; Do 5 lines: $2000, $2400, $2800, $2C00, $3000; Stop before $3400. bne - rts end /////////////////////////////////////////////////////////////////////////////////////////////////// // Jump straight to the system monitor // Params: None asm goMon jmp $FF69 end /////////////////////////////////////////////////////////////////////////////////////////////////// // Execute a monitor breakpoint // Params: None asm _brk bit setText bit page1 brk end /////////////////////////////////////////////////////////////////////////////////////////////////// // Clear the screen and reboot the machine // Params: None asm _reboot bit setROM jsr home bit setText bit page1 inc $3F4 ; invalidate reset vector jmp $FA62 ; and reset end /////////////////////////////////////////////////////////////////////////////////////////////////// // Set up the font engine // Params: pFont asm setFont +asmPlasm_bank2 1 jmp SetFont end /////////////////////////////////////////////////////////////////////////////////////////////////// // Use the font engine to clear the current text window. // Parameters: top, bottom, left, right asm setWindow +asmPlasm_bank2 4 jmp SetWindow end /////////////////////////////////////////////////////////////////////////////////////////////////// // Save the cursor position (1 save slot) asm saveCursor +asmPlasm_bank2 0 jmp SaveCursor end /////////////////////////////////////////////////////////////////////////////////////////////////// // Restore the cursor position (1 save slot) asm restoreCursor +asmPlasm_bank2 0 jmp RestCursor end /////////////////////////////////////////////////////////////////////////////////////////////////// // Read a keyboard string up to 40 chars using the Font Engine. Returns a zero-terminated string // in the input buffer at $200, with the length stored in $2FF. asm rawGetStr +asmPlasm_bank2 0 jmp GetStr end /////////////////////////////////////////////////////////////////////////////////////////////////// // Use the font engine to clear the current text window // Params: None asm _clearWindow +asmPlasm_bank2 0 jmp ClearWindow end /////////////////////////////////////////////////////////////////////////////////////////////////// // Use the font engine to copy the current text window to hi-res page 2 // Params: None asm copyWindow +asmPlasm_bank2 0 jmp CopyWindow end /////////////////////////////////////////////////////////////////////////////////////////////////// // Display a character using the font engine. // Params: ch asm displayChar +asmPlasm_bank2 1 jmp DisplayChar end /////////////////////////////////////////////////////////////////////////////////////////////////// // Display a string using the font engine. // Params: pStr asm _displayStr +asmPlasm_bank2 1 jmp DisplayStr end /////////////////////////////////////////////////////////////////////////////////////////////////// // Calculate string width using the font engine. // Params: pStr asm calcWidth +asmPlasm_bank2 1 jmp CalcWidth end /////////////////////////////////////////////////////////////////////////////////////////////////// // Display a string using the font engine but not its parser. // Params: pStr asm _rawDisplayStr +asmPlasm_bank2 1 sta pTmp sty pTmp+1 ldy #0 lda (pTmp),y sta tmp - cpy tmp bcc + rts + iny lda (pTmp),y ora #$80 cmp #"^" bne + iny lda (pTmp),y and #$1F ora #$80 + sty tmp+1 jsr DisplayChar ldy tmp+1 bne - end /////////////////////////////////////////////////////////////////////////////////////////////////// // Random number generator // Adapted from http://codebase64.org/doku.php?id=base:small_fast_16-bit_prng asm _rand16 +asmPlasm 0 lda seed beq .lowZero ; $0000 and $8000 are special values to test for ; Do a normal shift asl seed lda seed+1 rol bcc .noEor .doEor: ; high byte is in A eor #>magic sta seed+1 tay ; for asmPlasm, return hi byte in Y, lo byte in A lda seed eor #1 = (^p & $7F) next ^$200 = ^$2FF return mmgr(HEAP_INTERN, $200) end /////////////////////////////////////////////////////////////////////////////////////////////////// // Setter functions for library use def _setPlural(flg) isPlural = flg end /////////////////////////////////////////////////////////////////////////////////////////////////// // Convert signed decimal to string in decimalBuf def convertDec(n) word n0 word p p = @decimalBuf + 1 if n < 0; ^p = '-'; p=p+1; n = -n; fin n0 = n if n0 > 9999; ^p = '0' + n/10000; p=p+1; n = n%10000; fin if n0 > 999; ^p = '0' + n/1000; p=p+1; n = n%1000; fin if n0 > 99; ^p = '0' + n/100; p=p+1; n = n%100; fin if n0 > 9; ^p = '0' + n/10; p=p+1; n = n%10; fin ^p = '0' + n; p=p+1 decimalBuf[0] = p - @decimalBuf - 1 // record final length of string return @decimalBuf end /////////////////////////////////////////////////////////////////////////////////////////////////// // Print a formatted string a'la C printf, with up to three parameters. def _printf3(str, arg1, arg2, arg3) word pos word curArg word p pos = 0 curArg = @arg1 while TRUE pos = partialPrintf(str, pos) if pos < 0 break fin p = str + pos + 2 when ^p is 'c' printChar(*curArg); break is 'd' puts(convertDec(*curArg)); break is 's' puts(*curArg); break is 'x' printHex(*curArg); break is '%' printChar('%'); break otherwise printHex(^p); fatal("Unknown % code") wend curArg = curArg + 2 pos = pos + 2 loop end def _printf1(str, arg1); printf3(str, arg1, 0, 0); end def _printf2(str, arg1, arg2); printf3(str, arg1, arg2, 0); end // Like printf, but displays text using font engine def _displayf3(str, arg1, arg2, arg3) buildString(@addToString) printf3(str, arg1, arg2, arg3) displayStr(finishString(isPlural)) end def _displayf1(str, arg1); displayf3(str, arg1, 0, 0); end def _displayf2(str, arg1, arg2); displayf3(str, arg1, arg2, 0); end /////////////////////////////////////////////////////////////////////////////////////////////////// def parseDec(str) word n word pend word p byte neg neg = FALSE n = 0 p = str + 1 pend = p + ^str while p < pend if p == (str+1) and ^p == '-' neg = TRUE elsif ^p >= '0' and ^p <= '9' n = (n*10) + (^p - '0') else break fin p = p+1 loop if neg; return -n; fin return n end /////////////////////////////////////////////////////////////////////////////////////////////////// def parseDecWithDefault(str, default) if ^str == 0 return default fin return parseDec(str) end /////////////////////////////////////////////////////////////////////////////////////////////////// // Convert a lower-case character to upper-case (or return unchanged if it's not lower-case) def charToUpper(c) if c >= 'a' and c <= 'z' return c - $20 fin return c end /////////////////////////////////////////////////////////////////////////////////////////////////// // Get a keystroke and convert it to upper case def _getUpperKey() byte key while ^kbd < 128 *seed = *seed + 1 animPauseCt = animPauseCt - 1 if animPauseCt < 0 if curPortrait and animNumFrames > 1 nextAnimFrame() fin animPauseCt = ANIM_PAUSE_MAX fin loop key = ^kbd ^kbdStrobe return charToUpper(key & $7F) end /////////////////////////////////////////////////////////////////////////////////////////////////// // Pause for a specified count period, advancing the animation periodically. def _pause(count) while count >= 0 animPauseCt = animPauseCt - 1 if animPauseCt < 0 if curPortrait and animNumFrames > 1 nextAnimFrame() fin animPauseCt = ANIM_PAUSE_MAX fin count-- loop end /////////////////////////////////////////////////////////////////////////////////////////////////// def _encodeDice(nDice, dieSize, add) // ndice=0..15, dieSize=0..15, add=0..255 return (nDice << 12) | (dieSize << 8) | add end /////////////////////////////////////////////////////////////////////////////////////////////////// def _rollDice(encoded) byte i, nDice, dieSize, add, result nDice = encoded >> 12 dieSize = (encoded >> 8) & $F add = encoded & $F result = add for i = 1 to nDice add = (rand16() % dieSize) + 1 result = result + add next return result end /////////////////////////////////////////////////////////////////////////////////////////////////// // Set the sky color (relevant to 3D display only) def _setSky(num) skyNum = num setColor(0, skyNum) needRender = TRUE end /////////////////////////////////////////////////////////////////////////////////////////////////// // Switch to the next sky color (3D only) def nextSky() setSky((skyNum + 1) % 18) end /////////////////////////////////////////////////////////////////////////////////////////////////// // Set the ground color (relevant to 3D display only) def _setGround(num) groundNum = num setColor(1, groundNum) needRender = TRUE end /////////////////////////////////////////////////////////////////////////////////////////////////// // Switch to the next ground color (3D only) def nextGround() setGround((groundNum + 1) % 18) end /////////////////////////////////////////////////////////////////////////////////////////////////// // Establish a keystroke -> command association in the command table def initCmd(key, func) if key >= $60 key = key - $20 fin cmdTbl[key-$20] = func end /////////////////////////////////////////////////////////////////////////////////////////////////// // Load the Frame Image, and lock it. def loadFrameImg() byte img img = mapIs3D+2 if frameLoaded <> img if frameLoaded mmgr(UNLOCK_MEMORY,$2000) mmgr(FREE_MEMORY, $2000) fin mmgr(SET_MEM_TARGET, $2000) mmgr(QUEUE_LOAD, img<<8 | RES_TYPE_SCREEN) mmgr(LOCK_MEMORY, $2000) frameLoaded = img fin end /////////////////////////////////////////////////////////////////////////////////////////////////// // Window for the map name bar def _setWindow1() setWindow(8, 18, 35, 119) // Top, Bottom, Left, Right mapNameHash = 0 // on the assumption that it's being set because somebody's going to print there end /////////////////////////////////////////////////////////////////////////////////////////////////// // Window for the large upper right bar def _setWindow2() displayChar('N'-$40) // Set normal mode - clear all special modes (like underline, etc.) setWindow(24, 132, 154, 269) // Top, Bottom, Left, Right end /////////////////////////////////////////////////////////////////////////////////////////////////// // Window for the mid-size lower right bar def _setWindow3() setWindow(144, 180, 154, 269) // Top, Bottom, Left, Right end /////////////////////////////////////////////////////////////////////////////////////////////////// // Window for the map area (used for clearing it) def _setMapWindow() if mapIs3D setWindow(24, 153, 14, 140) // Top, Bottom, Left, Right else setWindow(24, 169, 14, 140) // Top, Bottom, Left, Right fin end /////////////////////////////////////////////////////////////////////////////////////////////////// def rightJustifyStr(str, rightX) word x x = rightX - calcWidth(str) tabBuf[0] = 4 // length tabBuf[1] = 20 // Ctrl-T tabBuf[2] = (x / 100) + '0' tabBuf[3] = ((x / 10) % 10) + '0' tabBuf[4] = (x % 10) + '0' rawDisplayStr(@tabBuf) rawDisplayStr(str) end /////////////////////////////////////////////////////////////////////////////////////////////////// def rightJustifyNum(num, rightX) rightJustifyStr(convertDec(num), rightX) end /////////////////////////////////////////////////////////////////////////////////////////////////// def displayPlayerData(player) word pi // Display the player's name and health rawDisplayStr(player=>s_name) rightJustifyNum(player=>w_health, CHAR_WND_LIFE_X) // Locate the first weapon, and display it's clip pi = player=>p_items while pi and pi->t_type <> TYPE_WEAPON; pi = pi=>p_nextObj; loop if pi rightJustifyNum(pi->b_clipCurrent, CHAR_WND_GUN_X) else rightJustifyStr("---", CHAR_WND_GUN_X) fin // All done. rawDisplayStr("\n") end /////////////////////////////////////////////////////////////////////////////////////////////////// // Display the party data on the screen def _showParty() word p saveCursor() setWindow3() clearWindow() // Display header rawDisplayStr("^LName") // begin underline mode rightJustifyStr("Life", CHAR_WND_LIFE_X) rightJustifyStr("Gun", CHAR_WND_GUN_X) rawDisplayStr("^L\n") // end underline mode // Display each character p = global=>p_players while p displayPlayerData(p) p = p=>p_nextObj loop // Finish up if mapIs3D; copyWindow(); fin setWindow2() restoreCursor() end /////////////////////////////////////////////////////////////////////////////////////////////////// // Load code and data, set up everything to display a 2D or 3D map def initMap(x, y, dir) word pMap // Set up the command table initCmds() // Reset memory (our module will stay since memory manager locked it upon load) mmgr(RESET_MEMORY, 0) // Show disk activity icon while we load diskActivity($FF) // Queue loading of the raycaster or tile engine and the map data mmgr(SET_MEM_TARGET, displayEngine) if mapIs3D mmgr(QUEUE_LOAD, CODE_RENDER<<8 | RES_TYPE_CODE) pMap = mmgr(QUEUE_LOAD, mapNum<<8 | RES_TYPE_3D_MAP) auxMmgr(SET_MEM_TARGET, expandVec) auxMmgr(QUEUE_LOAD, CODE_EXPAND<<8 | RES_TYPE_CODE) else mmgr(QUEUE_LOAD, CODE_TILE_ENGINE<<8 | RES_TYPE_CODE) pMap = mmgr(QUEUE_LOAD, mapNum<<8 | RES_TYPE_2D_MAP) fin renderLoaded = TRUE // Load everything that we just queued mmgr(FINISH_LOAD, 1) // 1 = keep open diskActivity(0) // Load the frame image (and lock it there) loadFrameImg() mmgr(FINISH_LOAD, 1) // 1 = keep open // Clear all the windows to the background color (hi-bit set) setWindow1() clearWindow() setWindow2() clearWindow() setWindow3() clearWindow() // Clear the list of encounter zones from any previous maps global=>p_encounterZones = NULL // Start up the display engine with map data and starting position. This will also load and // init the script module, if any, which will end up calling us back at the setScriptInfo triggerTbl = NULL setWindow2() initDisplay(mapNum, pMap, x, y, dir) needRender = FALSE curPortrait = 0 // Display the party characters showParty() end /////////////////////////////////////////////////////////////////////////////////////////////////// // Check for script(s) attached to the given location, and call them if there are any. // Returns TRUE if any were triggered. def checkScripts(x, y) word p word pNext word script byte anyTriggered anyTriggered = FALSE x = x - triggerOriginX y = y - triggerOriginY p = triggerTbl while p if ^p == $FF break fin pNext = p + p->1 if ^p == y p = p + 2 while p < pNext if x == ^p script = p=>1 setWindow2() script() anyTriggered = TRUE fin p = p + 3 loop fin p = pNext loop return anyTriggered end /////////////////////////////////////////////////////////////////////////////////////////////////// // Display a portrait drawing (typically called from scripts) def _clearPortrait() if curPortrait auxMmgr(FREE_MEMORY, curPortrait) curPortrait = 0 if renderLoaded texControl(1) // 1=load fin needRender = TRUE fin end /////////////////////////////////////////////////////////////////////////////////////////////////// // Perform rendering, copy if necessary, clear appropriate flags def doRender() if curPortrait; clearPortrait(); fin render() if textDrawn and mapIs3D; copyWindow(); fin needRender = FALSE end /////////////////////////////////////////////////////////////////////////////////////////////////// // Advance one step forward (works for either 3D or 2D maps) def moveForward() byte val word x, y val = advance() // If not blocked, render at the new position. if val > 0 if !mapIs3D doRender() else needRender = TRUE fin fin // If we're on a new map tile, clear text from script(s) on the old tile. if val >= 2 and textDrawn clearWindow() if mapIs3D; copyWindow(); fin textDrawn = FALSE fin // If there are script(s) on the new tile, run them. if val == 3 getPos(@x, @y) if !checkScripts(x, y) if global=>p_encounterZones checkEncounter(x, y) fin fin elsif val >= 2 and global=>p_encounterZones getPos(@x, @y) checkEncounter(x, y) fin end /////////////////////////////////////////////////////////////////////////////////////////////////// // Adjust player's direction plus or minus n increments def adjustDir(n) setDir((getDir() + n) & 15) end /////////////////////////////////////////////////////////////////////////////////////////////////// // Move backward one step (3D mode) def _moveBackward() adjustDir(8) moveForward() adjustDir(8) end /////////////////////////////////////////////////////////////////////////////////////////////////// // Turn left (3D mode) def rotateLeft() adjustDir(-1) needRender = TRUE end /////////////////////////////////////////////////////////////////////////////////////////////////// // Rotate to the right (3D mode) def rotateRight() adjustDir(1) needRender = TRUE end /////////////////////////////////////////////////////////////////////////////////////////////////// // Sidestep to the right (3D mode) def strafeRight() adjustDir(4) moveForward() adjustDir(-4) end /////////////////////////////////////////////////////////////////////////////////////////////////// // Sidestep to the left (3D mode) def strafeLeft() adjustDir(-4) moveForward() adjustDir(4) end /////////////////////////////////////////////////////////////////////////////////////////////////// def moveNorth() word x, y getPos(@x, @y) if y > 4 setDir(0) moveForward() else beep() fin end def moveEast() word x, y getPos(@x, @y) if x < totalMapWidth-5 setDir(4) moveForward() else beep() fin end def moveSouth() word x, y getPos(@x, @y) if y < totalMapHeight-5 setDir(8) moveForward() else beep() fin end def moveWest() word x, y getPos(@x, @y) if x > 4 setDir(12) moveForward() else beep() fin end /////////////////////////////////////////////////////////////////////////////////////////////////// // Switch to a new map (2D or 3D) and establish position on it def setMap(is3D, num, x, y, dir) if is3D == mapIs3D and num == mapNum setPos(x, y) setDir(dir) needRender = TRUE else flipToPage1() setWindow1(); clearWindow() displayChar('Y'-$40) // center mode displayStr("Traveling...") displayChar('N'-$40) // normal mode setMapWindow(); clearWindow() setWindow2(); clearWindow() mapIs3D = is3D mapNum = num initMap(x, y, dir) fin // Don't check scripts, because we often land on an "Exit to wilderness?" script //NO:checkScripts() end /////////////////////////////////////////////////////////////////////////////////////////////////// def kbdTeleport() byte d3, num word x, y byte dir flipToPage1() ^$c053 if ^$25 < 23; ^$25 = 23; fin getPos(@x, @y) dir = getDir() printf3("\nCurrent: X=%d Y=%d Facing=%d\n", x, y, dir) printf1("3D [%d]: ", mapIs3D) d3 = parseDecWithDefault(readStr(), mapIs3D) if d3 > 1; d3 = 1; fin printf1("Map [%d]: ", mapNum) num = parseDecWithDefault(readStr(), mapNum) printf1("X [%d] : ", x) x = parseDecWithDefault(readStr(), x) printf1("Y [%d] : ", y) y = parseDecWithDefault(readStr(), y) printf1("Facing [%d]: ", dir) dir = parseDecWithDefault(readStr(), dir) ^$c052 setMap(d3, num, x, y, dir) end /////////////////////////////////////////////////////////////////////////////////////////////////// def showPos() word x, y byte dir flipToPage1() ^$c053 if ^$25 < 23; ^$25 = 23; fin getPos(@x, @y) printf2("\nX=%d Y=%d ", x, y) if mapIs3D printf3("Facing=%d Sky=%d Ground=%d", getDir(), skyNum, groundNum) fin puts("\nHit any key.\n") getUpperKey() ^$c052 end /////////////////////////////////////////////////////////////////////////////////////////////////// def _queue_setMap(is3D, num, x, y, dir) q_mapIs3D = is3D q_mapNum = num q_x = x q_y = y q_dir = dir end /////////////////////////////////////////////////////////////////////////////////////////////////// def _queue_teleport(x, y, dir) queue_setMap(mapIs3D, mapNum, x, y, dir) end /////////////////////////////////////////////////////////////////////////////////////////////////// // Get a key and dispatch it to a command. Then do it again, forever. def kbdLoop() word key, func word errMsg byte xreg, tmp errMsg = "Error: x reg changed from %x to %x\n" xreg = getXReg() while TRUE tmp = getXReg() if tmp <> xreg printf2(errMsg, xreg, tmp) brk() fin key = getUpperKey() key = key - $20 if key >= 0 and key < $40 func = cmdTbl[key] if func; func(); fin fin if q_mapNum setMap(q_mapIs3D, q_mapNum, q_x, q_y, q_dir) q_mapNum = 0 fin if needRender doRender() fin loop end /////////////////////////////////////////////////////////////////////////////////////////////////// def _showMapName(mapName) word newNameHash newNameHash = hashString(mapName) if newNameHash <> mapNameHash setWindow1() clearWindow() displayChar('Y'-$40) // center mode displayStr(mapName) displayChar('N'-$40) // normal mode if mapIs3D; copyWindow(); fin mapNameHash = newNameHash fin end /////////////////////////////////////////////////////////////////////////////////////////////////// // Set initial info for the scripts on this map: the name of the map, its trigger table, and the // maximum extent (width, height). This is called by the init function for the scripts. def _setScriptInfo(mapName, trigTbl, wdt, hgt) // Grab the trigger table origins (used so the table can be more compact) triggerOriginX = trigTbl=>0 triggerOriginY = trigTbl=>2 // Record the trigger table pointer triggerTbl = trigTbl + 4 // Record the maximum width and height totalMapWidth = wdt totalMapHeight = hgt // Display map name showMapName(mapName) // Get ready for new encounter zones global=>p_encounterZones = NULL // Back to the main text window. setWindow2() end /////////////////////////////////////////////////////////////////////////////////////////////////// // Called by scripts to display a string. We set the flag noting that something has been // displayed, then use an assembly routine to do the work. def _scriptDisplayStr(str) textDrawn = TRUE flipToPage1() displayStr(str) tossStrings() end def _scriptDisplayStrNL(str) scriptDisplayStr(str) displayStr("\n") end /////////////////////////////////////////////////////////////////////////////////////////////////// // Get a key, and don't return until it's Y or N (or lower-case of those). Returns 1 for Y. def _getYN() byte key while TRUE key = getUpperKey() if key == 'Y' return 1 elsif key == 'N' clearWindow() if textDrawn and mapIs3D; clearWindow(); fin textDrawn = FALSE return 0 fin beep() loop end /////////////////////////////////////////////////////////////////////////////////////////////////// // Show the current animation frame def showAnimFrame() word pData // Determine data pointer based on current animation frame if !curPortrait; return; fin pData = curPortrait + 1 + (animFrame * 2304) // 18*128 = 2304 // Show it on-screen if mapIs3D blitPortrait(pData, $2182) // start at 3rd text line else blitPortrait(pData, $2202) // start at 4th text line fin needRender = FALSE // suppress display of map for this frame end /////////////////////////////////////////////////////////////////////////////////////////////////// // Advance to next frame of current animation, if any def nextAnimFrame() word oldAnimFrame if !curPortrait; return; fin oldAnimFrame = animFrame // Choose a new direction based on the flags. Do this the first time, and once every 3-7 frames. animDirCt = animDirCt - 1 if !animDir or animDirCt == 0 if animFlags & ANIM_FLAG_FWD animDir = 'F' elsif animFlags & ANIM_FLAG_FWD_BKWD if animDir == 'B' animDir = 'F' else animDir = 'B' fin elsif (animFlags & ANIM_FLAG_RANDOM) animDir = 'R' else animDir = 0 fin animDirCt = (rand16() % 5) + 3 fin // Advance in the current direction if animDir == 'F' // forward animFrame = animFrame + 1 if animFrame >= animNumFrames animFrame = 0 fin elsif animDir == 'B' // backward if animFrame == 0 animFrame = animNumFrames - 1 else animFrame = animFrame - 1 fin elsif animDir == 'R' // random animFrame = rand16() % animNumFrames fin // And show it. if animFrame <> oldAnimFrame showAnimFrame() fin // Reset the animation pause animPauseCt = ANIM_PAUSE_MAX end /////////////////////////////////////////////////////////////////////////////////////////////////// // Display a portrait drawing (typically called from scripts) def _setPortrait(portraitNum) word srcData byte cx, cy clearPortrait() flipToPage1() // We're going to switch windows. Save the cursor pos in the text window. saveCursor() // Make room by unloading the textures (only if renderer is loaded) if renderLoaded texControl(0) fin // Now clear out the map area setMapWindow() clearWindow() // Restore the cursor position setWindow2() restoreCursor() // Load the portrait image and display it diskActivity($FF) curPortrait = auxMmgr(QUEUE_LOAD, portraitNum<<8 | RES_TYPE_PORTRAIT) mmgr(FINISH_LOAD, 0) // 0 = close diskActivity(0) animFrame = 0 animFlags = readAuxByte(curPortrait) animNumFrames = animFlags & $F animDir = 0 animDirCt = 1 animPauseCt = ANIM_PAUSE_MAX // And show the first frame showAnimFrame() // Do not render over the portrait needRender = FALSE end /////////////////////////////////////////////////////////////////////////////////////////////////// // Test out portrait drawing def nextPortrait() portraitNum = portraitNum + 1 printf1("Portrait #%d\n", portraitNum) setPortrait(portraitNum) end /////////////////////////////////////////////////////////////////////////////////////////////////// // Test out portrait drawing def prevPortrait() if portraitNum > 1 portraitNum = portraitNum - 1 fin setPortrait(portraitNum) end /////////////////////////////////////////////////////////////////////////////////////////////////// def _countList(p) byte n n = 0 while p n = n+1 p = p=>p_nextObj loop return n end /////////////////////////////////////////////////////////////////////////////////////////////////// def _countListFiltered(p, offset, filterFunc) byte n n = 0 while p if filterFunc(p) n = n+1 fin p = *(p + offset) loop return n end /////////////////////////////////////////////////////////////////////////////////////////////////// def _randomFromListFiltered(p, offset, filterFunc) byte n n = rand16() % countListFiltered(p, offset, filterFunc) while p if filterFunc(p) if n == 0; return p; fin n = n+1 fin p = *(p + offset) loop return NULL end /////////////////////////////////////////////////////////////////////////////////////////////////// // Call like this: addToList(@player=>p_items, itemToAdd) def _addToList(addTo, p) // Get to the end of the list while *addTo addTo = (*addTo) + p_nextObj loop p=>p_nextObj = *addTo *addTo = p end /////////////////////////////////////////////////////////////////////////////////////////////////// // Call like this: removeFromList(player + items, itemToRemove) def _removeFromList(pList, toRemove) word p p = *pList while p and p <> toRemove pList = p + p_nextObj p = *pList loop if p *pList = p=>p_nextObj p=>p_nextObj = NULL else fatal("InvalUnlink") fin end /////////////////////////////////////////////////////////////////////////////////////////////////// def saveMapPos() global->b_mapIs3D = mapIs3D global->b_mapNum = mapNum getPos(@global=>w_mapX, @global=>w_mapY) global->b_mapDir = getDir() end /////////////////////////////////////////////////////////////////////////////////////////////////// def restoreMapPos() mapIs3D = global->b_mapIs3D mapNum = global->b_mapNum initMap(global=>w_mapX, global=>w_mapY, global->b_mapDir) end /////////////////////////////////////////////////////////////////////////////////////////////////// // Load the Party engine and show data for the given player def showPlayerSheet(num) word partyEngine word x, y byte dir saveMapPos() // Load the party engine flipToPage1() mmgr(RESET_MEMORY, 0) renderLoaded = FALSE partyEngine = mmgr(QUEUE_LOAD, MODULE_PARTY<<8 | RES_TYPE_MODULE) mmgr(FINISH_LOAD, 1) // 1 = keep open // Run the party engine partyEngine()=>party_showPlayerSheet(num) // Resume where we left off textDrawn = TRUE clearPortrait() setWindow2() restoreMapPos() end def showPlayer1() showPlayerSheet(0) end def showPlayer2() showPlayerSheet(1) end def showPlayer3() showPlayerSheet(2) end /////////////////////////////////////////////////////////////////////////////////////////////////// def _makeEnemy(name, healthDice, image0, image1, attackType, attackText, attackRange, chanceToHit, dmg, groupSize) word p; p = mmgr(HEAP_ALLOC, TYPE_ENEMY) p=>s_name = mmgr(HEAP_INTERN, name) p=>w_health = rollDice(healthDice) // 4d6 if !image1 or (rand16() % 2) p->b_image = image0 else p->b_image = image1 fin p->b_attackType = attackType p=>s_attackText = mmgr(HEAP_INTERN, attackText) p->b_enemyAttackRange = attackRange p->b_chanceToHit = chanceToHit p=>r_enemyDmg = dmg p=>r_groupSize = groupSize return p end /////////////////////////////////////////////////////////////////////////////////////////////////// def _addEncounterZone(code, x, y, dist, chance) word p; p = mmgr(HEAP_ALLOC, TYPE_ENCOUNTER_ZONE) p=>s_name = mmgr(HEAP_INTERN, code) p=>w_encX = x p=>w_encY = y p=>w_encMaxDist = dist p=>w_encChance = chance addToList(@global=>p_encounterZones, p) end /////////////////////////////////////////////////////////////////////////////////////////////////// def _strcmpi(a, b) word limit, lenDiff, diff lenDiff = ^a - ^b limit = a + min(^a, ^b) while a < limit a++ b++ diff = charToUpper(^a) - charToUpper(^b) if diff <> 0 return diff fin loop return lenDiff end /////////////////////////////////////////////////////////////////////////////////////////////////// def doCombat(mapCode) word combatEngine word x, y byte dir saveMapPos() // Load the combat engine flipToPage1() diskActivity($FF) mmgr(RESET_MEMORY, 0) renderLoaded = FALSE combatEngine = mmgr(QUEUE_LOAD, MODULE_COMBAT<<8 | RES_TYPE_MODULE) mmgr(FINISH_LOAD, 1) // 1 = keep open diskActivity(0) // Run the combat engine combatEngine()=>combat_zoneEncounter(mapCode) // Clear enemies out of the heap global=>p_enemyGroups = NULL mmgr(HEAP_COLLECT, 0) // Resume where we left off textDrawn = TRUE clearPortrait() setWindow2() restoreMapPos() end /////////////////////////////////////////////////////////////////////////////////////////////////// // Check for a random encounter at this position def checkEncounter(x, y) word p word p_bestZone, bestDist word d // Find the zone that's closest, but not too far. bestDist = INT_MAX p_bestZone = NULL p = global=>p_encounterZones while p d = min(abs(x - p=>w_encX), abs(y - p=>w_encY)) if d < bestDist and (p=>w_encMaxDist == 0 or d < p=>w_encMaxDist)) p_bestZone = p bestDist = d fin p = p=>p_nextObj loop // Roll for an encounter in the zone. d = rand16() % 1000 if p_bestZone and d < p_bestZone=>w_encChance // Encounter! doCombat(p_bestZone=>s_name) fin end /////////////////////////////////////////////////////////////////////////////////////////////////// def testCombat if global=>p_encounterZones doCombat(global=>p_encounterZones=>s_name) fin end /////////////////////////////////////////////////////////////////////////////////////////////////// // Set up the command table for 3D mode def initCmds() // Clear the command table byte i for i = 0 to 63 cmdTbl[i] = 0 next // Commands common to both 2D and 3D initCmd('T', @kbdTeleport) initCmd('P', @showPos) initCmd('/', @nextPortrait) initCmd('?', @prevPortrait) initCmd('!', @testCombat) initCmd('1', @showPlayer1) initCmd('2', @showPlayer2) initCmd('3', @showPlayer3) // Commands handled differently in 3D vs 2D if mapIs3D initCmd('W', @moveForward) initCmd('A', @rotateLeft) initCmd('D', @rotateRight) initCmd('S', @moveBackward) initCmd('X', @moveBackward) initCmd('Z', @strafeLeft) initCmd('C', @strafeRight) initCmd('I', @moveForward) initCmd('J', @rotateLeft) initCmd('L', @rotateRight) initCmd('K', @moveBackward) initCmd(',', @moveBackward) initCmd('M', @strafeLeft) initCmd('.', @strafeRight) initCmd('Y', @nextSky) initCmd('G', @nextGround) else initCmd('W', @moveNorth) initCmd('D', @moveEast) initCmd('S', @moveSouth) initCmd('X', @moveSouth) initCmd('A', @moveWest) initCmd('I', @moveNorth) initCmd('J', @moveWest) initCmd('L', @moveEast) initCmd('K', @moveSouth) initCmd(',', @moveSouth) fin end /////////////////////////////////////////////////////////////////////////////////////////////////// // Load and display the title screen. def loadTitle() word pFont puts("Loading Lawless Legends.\n") // Load the title screen mmgr(UNLOCK_MEMORY, $2000) mmgr(FREE_MEMORY, $2000) mmgr(SET_MEM_TARGET, $2000) mmgr(QUEUE_LOAD, 1<<8 | RES_TYPE_SCREEN) // title screen is fixed at #1 mmgr(LOCK_MEMORY, $2000) mmgr(FINISH_LOAD, 1) // 1 = keep open frameLoaded = 1 ^$C050 // graphics ^$C057 // hi-res ^$C054 // page 1 ^$C052 // full screen // Hack for real (not emulated) IIc: sometimes displays only lo-bit graphics // unless we do this. *HUGE* thanks to Brendan Robert for the fix! ^$C07E=0 // disable double-hi-res ^$C05F // disable double-hi-res // Load the font engine and its font, and lock them in place forever. mmgr(SET_MEM_TARGET, fontEngine) mmgr(QUEUE_LOAD, CODE_FONT_ENGINE<<8 | RES_TYPE_CODE) mmgr(LOCK_MEMORY, fontEngine) mmgr(SET_MEM_TARGET, $9000) pFont = mmgr(QUEUE_LOAD, 1<<8 | RES_TYPE_FONT) mmgr(LOCK_MEMORY, pFont) mmgr(FINISH_LOAD, 1) // 1 = keep open // Tell the font engine where to find its font setFont(pFont) // Wait for a key. getUpperKey() end /////////////////////////////////////////////////////////////////////////////////////////////////// // Set up the small-object heap def initHeap() byte i mmgr(SET_MEM_TARGET, heapStart) mmgr(REQUEST_MEMORY, heapSize) mmgr(LOCK_MEMORY, heapStart) mmgr(HEAP_SET, heapStart) i = 0 while typeTbls[i] mmgr(HEAP_ADD_TYPE, typeTbls[i]) i = i+1 loop global = mmgr(HEAP_ALLOC, TYPE_GLOBAL) end /////////////////////////////////////////////////////////////////////////////////////////////////// def _getGlobals() return global end /////////////////////////////////////////////////////////////////////////////////////////////////// // Set up the library vectors so we can share the library routines def setLibVecs() word sp, dp // We're going to put the vectors at a known location, and lock that space _mmgr(SET_MEM_TARGET, gameLibVecs) _mmgr(REQUEST_MEMORY, $100) _mmgr(LOCK_MEMORY, gameLibVecs) // Now set up the jump vectors sp = @gameLib_addrs dp = gameLibVecs while *sp dp->0 = $4c dp=>1 = *sp sp = sp + 2 dp = dp + 3 loop end /////////////////////////////////////////////////////////////////////////////////////////////////// def makeModifier(kind, value) word p p = mmgr(HEAP_ALLOC, TYPE_MODIFIER) p->b_modKind = kind p->b_modValue = value return p end /////////////////////////////////////////////////////////////////////////////////////////////////// def makeArmor(name, kind, cost, armorValue, modifier) word p p = mmgr(HEAP_ALLOC, TYPE_ARMOR) p=>s_name = mmgr(HEAP_INTERN, name) p->b_itemKind = kind p=>w_cost = cost p->b_armorValue = armorValue p=>p_modifiers = modifier return p end /////////////////////////////////////////////////////////////////////////////////////////////////// def makeWeapon(name, kind, cost, ammoKind, clipSize, meleeDmg, projectileDmg) word p p = mmgr(HEAP_ALLOC, TYPE_WEAPON) p=>s_name = mmgr(HEAP_INTERN, name) p->b_itemKind = kind p=>w_cost = cost p->b_ammoKind = ammoKind p->b_clipSize = clipSize p->b_clipCurrent = clipSize p=>r_meleeDmg = meleeDmg p=>r_projectileDmg = projectileDmg return p end /////////////////////////////////////////////////////////////////////////////////////////////////// def finishWeapon(p, modifier, attack0, attack1, attack2, weaponRange, combatText) p->ba_attacks[0] = attack0 p->ba_attacks[1] = attack1 p->ba_attacks[2] = attack2 p->b_weaponRange = weaponRange p=>s_combatText = mmgr(HEAP_INTERN, combatText) return p end /////////////////////////////////////////////////////////////////////////////////////////////////// // Create the party def initParty() word globalScripts // Old-style globalScripts = mmgr(QUEUE_LOAD, MODULE_GLOBAL_SCRIPTS<<8 | RES_TYPE_MODULE) mmgr(FINISH_LOAD, 1) // 1 = keep open addToList(@global=>p_players, globalScripts()=>script_new_Player_Hue_Hauser()) addToList(@global=>p_players, globalScripts()=>script_new_Player_Mokahnu()) mmgr(FREE_MEMORY, globalScripts) // New-style globalScripts = mmgr(QUEUE_LOAD, MODULE_GEN_GLOBAL_SCRIPTS<<8 | RES_TYPE_MODULE) mmgr(FINISH_LOAD, 1) // 1 = keep open globalScripts()=>sc_newGame() mmgr(FREE_MEMORY, globalScripts) mapIs3D = q_mapIs3D mapNum = q_mapNum q_mapNum = 0 initMap(q_x, q_y, q_dir) end /////////////////////////////////////////////////////////////////////////////////////////////////// // Main code. // setLibVecs() initHeap() loadTitle() initParty() kbdLoop() done