2014-08-21 15:17:50 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2016-07-12 15:09:55 +00:00
|
|
|
// Copyright (C) 2015 The 8-Bit Bunch. Licensed under the Apache License, Version 1.1
|
2015-09-10 16:22:07 +00:00
|
|
|
// (the "License"); you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at <http://www.apache.org/licenses/LICENSE-1.1>.
|
2016-07-12 15:09:55 +00:00
|
|
|
// 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
|
2015-09-10 16:22:07 +00:00
|
|
|
// governing permissions and limitations under the License.
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2014-08-21 15:17:50 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Fixed memory locations
|
2015-08-28 15:24:21 +00:00
|
|
|
const seed = $4E // Incremented continuously by keyboard read routine
|
2015-04-05 16:52:38 +00:00
|
|
|
const displayEngine = $6000 // main mem (raycaster and tile engine at same location)
|
2014-07-06 21:40:35 +00:00
|
|
|
|
2015-01-07 22:48:04 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Other constants
|
2017-06-30 15:11:34 +00:00
|
|
|
const CHAR_WND_HEALTH_X = 112
|
2015-12-17 17:19:49 +00:00
|
|
|
|
2018-02-21 17:54:30 +00:00
|
|
|
const ANIM_PAUSE_MAX = 150
|
2015-11-05 17:09:45 +00:00
|
|
|
|
2018-01-09 18:02:28 +00:00
|
|
|
const CLOCK_ADV_2D_HOURS = 1
|
|
|
|
const CLOCK_ADV_2D_MINS = 0
|
|
|
|
const CLOCK_ADV_2D_SECS = 0
|
|
|
|
|
|
|
|
const CLOCK_ADV_3D_HOURS = 0
|
|
|
|
const CLOCK_ADV_3D_MINS = 0
|
|
|
|
const CLOCK_ADV_3D_SECS = 30
|
|
|
|
|
2020-07-07 16:13:59 +00:00
|
|
|
const CLOCK_ADV_COMBAT_HOURS = 0
|
|
|
|
const CLOCK_ADV_COMBAT_MINS = 10
|
|
|
|
const CLOCK_ADV_COMBAT_SECS = 0
|
|
|
|
|
2018-04-27 15:03:21 +00:00
|
|
|
// Raycaster tables - must match values in render.i
|
|
|
|
const RAY_TABLE_START = $A800
|
|
|
|
const RAY_TABLE_SIZE = $1781
|
|
|
|
|
2016-07-04 16:31:33 +00:00
|
|
|
// Max gold
|
|
|
|
const GOLD_MAX = 20000
|
|
|
|
|
2016-07-03 20:39:00 +00:00
|
|
|
include "globalDefs.plh"
|
2015-10-01 15:34:49 +00:00
|
|
|
include "playtype.plh"
|
2015-12-09 15:35:02 +00:00
|
|
|
include "gen_images.plh"
|
2015-12-10 17:43:21 +00:00
|
|
|
include "gen_modules.plh"
|
2016-01-01 15:53:57 +00:00
|
|
|
include "gen_enemies.plh"
|
2016-06-17 00:42:18 +00:00
|
|
|
include "gen_players.plh"
|
2016-09-24 18:10:17 +00:00
|
|
|
include "gen_items.plh"
|
2016-03-31 17:01:04 +00:00
|
|
|
include "combat.plh"
|
2016-06-01 16:03:14 +00:00
|
|
|
include "party.plh"
|
2017-03-04 16:20:01 +00:00
|
|
|
include "store.plh"
|
2016-06-19 15:38:40 +00:00
|
|
|
include "diskops.plh"
|
2017-02-19 18:08:03 +00:00
|
|
|
include "godmode.plh"
|
2018-02-24 18:20:01 +00:00
|
|
|
include "automap.plh"
|
2018-09-13 15:27:57 +00:00
|
|
|
include "questlog.plh"
|
2019-01-24 16:52:02 +00:00
|
|
|
include "story.plh"
|
2019-07-15 17:21:12 +00:00
|
|
|
include "util3d.plh"
|
2016-07-03 14:33:12 +00:00
|
|
|
|
2015-12-29 19:22:14 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Data structures
|
|
|
|
include "playtype.pla"
|
2015-08-28 15:24:21 +00:00
|
|
|
|
2017-01-22 16:44:10 +00:00
|
|
|
export word global // the global heap object, from which all live objects must be reachable
|
2015-09-02 16:55:21 +00:00
|
|
|
|
2014-08-21 15:17:50 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2015-01-10 21:17:56 +00:00
|
|
|
// Predefined functions, for circular calls or out-of-order calls
|
2017-06-10 14:32:31 +00:00
|
|
|
predef setWindow2()#0
|
|
|
|
predef initCmds()#0
|
|
|
|
predef nextAnimFrame()#0
|
|
|
|
predef checkEncounter(x, y, force)#0
|
2017-06-10 15:56:23 +00:00
|
|
|
predef doCombat(mapCode, backUpOnFlee)#1
|
2017-06-10 14:32:31 +00:00
|
|
|
predef clearPortrait()#0
|
|
|
|
predef showMapName(mapName)#0
|
|
|
|
predef doRender()#0
|
2018-02-21 17:54:30 +00:00
|
|
|
predef pause(count)#1
|
2019-04-24 13:33:18 +00:00
|
|
|
predef printf1(str, arg1)#0
|
2018-02-26 14:45:15 +00:00
|
|
|
predef printf2(str, arg1, arg2)#0
|
2017-06-10 14:32:31 +00:00
|
|
|
predef playerDeath()#0
|
2020-08-22 17:50:45 +00:00
|
|
|
predef saveGame()#1
|
2020-07-07 15:33:59 +00:00
|
|
|
predef setStat(player, statName, val)#0
|
2018-03-29 17:12:22 +00:00
|
|
|
predef startGame(firstTime, ask)#0
|
2017-06-10 14:32:31 +00:00
|
|
|
predef showAnimFrame()#0
|
|
|
|
predef showParty()#0
|
2019-07-15 17:21:12 +00:00
|
|
|
predef textureControl(flg)#0
|
2018-06-07 16:11:07 +00:00
|
|
|
predef clearEncounterZones()#0
|
2021-09-02 16:22:17 +00:00
|
|
|
predef adjustPartyStat(statName, val)#0
|
2023-10-15 19:44:34 +00:00
|
|
|
predef printMem()#1
|
2015-01-18 16:59:56 +00:00
|
|
|
|
2014-08-21 15:17:50 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Global variables
|
2017-02-19 18:08:03 +00:00
|
|
|
export byte mapNum = -1
|
2017-07-08 23:36:08 +00:00
|
|
|
export byte mapIs3D = 0
|
2018-03-10 16:31:09 +00:00
|
|
|
export word totalMapWidth
|
|
|
|
export word totalMapHeight
|
|
|
|
export word pCurMap
|
2015-08-21 14:59:03 +00:00
|
|
|
word mapNameHash = 0
|
|
|
|
|
2019-07-16 18:03:40 +00:00
|
|
|
export byte needRender = FALSE
|
2016-07-16 12:39:22 +00:00
|
|
|
byte needShowParty = FALSE
|
2015-12-29 16:59:13 +00:00
|
|
|
byte renderLoaded = FALSE
|
2016-09-08 15:30:08 +00:00
|
|
|
byte texturesLoaded = FALSE
|
2015-12-23 23:42:37 +00:00
|
|
|
byte textDrawn = FALSE
|
2018-03-06 16:58:47 +00:00
|
|
|
byte anyInteraction = FALSE
|
2017-08-02 17:20:22 +00:00
|
|
|
byte textClearCountdown = 0
|
2020-08-22 17:50:45 +00:00
|
|
|
byte forceRawScrDisp = FALSE
|
2019-11-07 20:01:46 +00:00
|
|
|
export byte isPlural = 0
|
2017-08-10 17:51:20 +00:00
|
|
|
byte inScript = FALSE
|
2019-01-10 17:45:40 +00:00
|
|
|
export byte isFloppyVer
|
2021-10-13 13:21:01 +00:00
|
|
|
export byte isJace
|
2015-12-23 23:42:37 +00:00
|
|
|
|
2018-04-08 17:08:50 +00:00
|
|
|
byte scriptModule = 0
|
|
|
|
byte prevScriptModule = 0
|
|
|
|
|
2017-02-19 18:08:03 +00:00
|
|
|
export word skyNum = 9
|
|
|
|
export word groundNum = 10
|
2017-08-12 17:29:54 +00:00
|
|
|
export byte portraitNum = 0
|
2015-08-21 14:59:03 +00:00
|
|
|
|
2015-05-21 14:19:22 +00:00
|
|
|
word triggerOriginX, triggerOriginY
|
2015-02-27 13:33:11 +00:00
|
|
|
word triggerTbl
|
2015-08-21 14:59:03 +00:00
|
|
|
|
2019-12-19 17:38:14 +00:00
|
|
|
byte cmdKey // last command key pressed
|
2016-06-19 15:38:40 +00:00
|
|
|
word cmdTbl[96] // ASCII $00..$5F
|
2015-05-07 16:12:52 +00:00
|
|
|
byte frameLoaded = 0
|
2016-09-22 14:32:54 +00:00
|
|
|
word curEngine = NULL
|
2019-07-15 17:21:12 +00:00
|
|
|
word pModUtil3d = NULL
|
|
|
|
word util3d = NULL
|
2018-03-29 17:12:22 +00:00
|
|
|
export word pResourceIndex = NULL
|
2018-05-09 16:11:27 +00:00
|
|
|
export word pGlobalTileset = NULL
|
2018-03-10 16:31:09 +00:00
|
|
|
export byte curMapPartition = 0
|
2017-08-14 15:01:31 +00:00
|
|
|
export word typeHash = 0
|
2020-06-15 14:21:37 +00:00
|
|
|
export byte curHeapPct = 0
|
2018-04-16 15:19:38 +00:00
|
|
|
byte lastMoveDir = $FF
|
2014-07-06 23:02:52 +00:00
|
|
|
|
2020-06-17 19:42:43 +00:00
|
|
|
// Queue setMap / teleport, since otherwise script might be replaced while executing
|
2016-03-24 16:45:07 +00:00
|
|
|
byte q_mapIs3D = 0
|
|
|
|
byte q_mapNum = 1
|
|
|
|
word q_x = 0
|
|
|
|
word q_y = 0
|
|
|
|
byte q_dir = 0
|
2015-08-21 14:59:03 +00:00
|
|
|
|
2018-09-29 17:10:25 +00:00
|
|
|
// Queue of player death
|
|
|
|
byte q_playerDeath = FALSE
|
|
|
|
|
2016-09-15 18:08:43 +00:00
|
|
|
// Script tracking
|
|
|
|
const MAX_MAP_SCRIPTS = 4
|
|
|
|
byte nMapScripts = 0
|
|
|
|
word mapScripts[MAX_MAP_SCRIPTS]
|
|
|
|
|
2015-11-05 17:09:45 +00:00
|
|
|
// For decimal conversion and display tabbing
|
|
|
|
byte decimalBuf[7]
|
2017-04-02 16:25:11 +00:00
|
|
|
byte fontPosBuf[4]
|
2015-11-05 17:09:45 +00:00
|
|
|
byte tabBuf[5]
|
|
|
|
|
2018-02-21 17:54:30 +00:00
|
|
|
// Animation and lamp tracking
|
2016-10-23 17:42:11 +00:00
|
|
|
word curPortrait = NULL
|
2017-04-03 15:22:32 +00:00
|
|
|
byte curPortraitNum = 0
|
2018-03-10 16:31:09 +00:00
|
|
|
byte preEnginePortraitNum = 0
|
2016-10-23 17:42:11 +00:00
|
|
|
word curFullscreenImg = NULL
|
2015-12-23 23:42:37 +00:00
|
|
|
byte animDirCt
|
2016-10-22 15:46:37 +00:00
|
|
|
byte anyAnims = TRUE
|
2015-12-17 17:19:49 +00:00
|
|
|
word animPauseCt
|
2018-02-21 17:54:30 +00:00
|
|
|
byte showingLamp = TRUE
|
2019-07-15 17:21:12 +00:00
|
|
|
export byte lampFrame = 0
|
|
|
|
export word lampDir = 1
|
2019-01-24 16:52:02 +00:00
|
|
|
byte storyMode = FALSE
|
2015-12-09 18:16:05 +00:00
|
|
|
|
2018-01-10 16:47:20 +00:00
|
|
|
// Time and clock
|
2019-07-15 17:21:12 +00:00
|
|
|
export byte prevClockColor, prevClockHour, prevClockMinute
|
|
|
|
export byte nextSignificantMinute
|
2018-01-10 16:47:20 +00:00
|
|
|
byte snoozeX0, snoozeX1, snoozeY
|
|
|
|
word timeEventFunc
|
2021-09-07 16:08:28 +00:00
|
|
|
byte mapEmuSound = 0
|
2018-01-09 18:02:28 +00:00
|
|
|
|
2020-08-22 17:50:45 +00:00
|
|
|
export byte[] S_GAME1_FILENAME = "GAME.1.SAVE"
|
|
|
|
|
2017-09-13 15:06:52 +00:00
|
|
|
// Context for lambda functions (in lieu of closures, for now at least)
|
|
|
|
export word ctx
|
|
|
|
|
2016-07-07 21:22:48 +00:00
|
|
|
// Shared string constants
|
2017-06-24 18:43:16 +00:00
|
|
|
export byte[] S_INTELLIGENCE = "Intelligence"
|
|
|
|
export byte[] S_STRENGTH = "Strength"
|
|
|
|
export byte[] S_AGILITY = "Agility"
|
|
|
|
export byte[] S_STAMINA = "Stamina"
|
|
|
|
export byte[] S_CHARISMA = "Charisma"
|
|
|
|
export byte[] S_SPIRIT = "Spirit"
|
|
|
|
export byte[] S_LUCK = "Luck"
|
|
|
|
export byte[] S_HEALTH = "Health"
|
|
|
|
export byte[] S_MAX_HEALTH = "Max health"
|
|
|
|
export byte[] S_AIMING = "Aiming"
|
2021-04-17 14:57:52 +00:00
|
|
|
export byte[] S_HAND_TO_HAND = "Hand-to-hand"
|
2017-06-24 18:43:16 +00:00
|
|
|
export byte[] S_DODGING = "Dodging"
|
|
|
|
export byte[] S_GOLD = "Gold"
|
2018-01-16 17:15:52 +00:00
|
|
|
export byte[] S_TIME = "Time"
|
2017-06-24 18:43:16 +00:00
|
|
|
export byte[] S_XP = "XP"
|
|
|
|
export byte[] S_SP = "SP"
|
2021-01-05 16:50:09 +00:00
|
|
|
export byte[] S_BANK_BAL = "Bank bal"
|
2017-09-12 15:21:23 +00:00
|
|
|
export byte[] S_PACK_SIZE = "Pack size"
|
2017-06-24 18:43:16 +00:00
|
|
|
export byte[] S_ENTER = "Enter"
|
|
|
|
export byte[] S_LEAVE = "Leave"
|
|
|
|
export byte[] S_USE = "Use"
|
2017-07-15 18:41:29 +00:00
|
|
|
export byte[] S_HIS = "his"
|
|
|
|
export byte[] S_HER = "her"
|
|
|
|
export byte[] S_THEIR = "their"
|
2016-07-07 21:22:48 +00:00
|
|
|
|
2018-02-26 14:45:15 +00:00
|
|
|
word lastTick = 0
|
2018-02-14 16:58:22 +00:00
|
|
|
|
2019-04-30 13:35:41 +00:00
|
|
|
export byte recordMode = 0
|
2021-11-05 16:55:36 +00:00
|
|
|
export word recordSeed
|
2019-04-12 14:21:57 +00:00
|
|
|
|
2020-06-15 14:21:37 +00:00
|
|
|
export byte diskLimit = 0
|
|
|
|
|
2021-09-14 15:24:16 +00:00
|
|
|
export word playerUsing = NULL
|
|
|
|
|
2014-08-21 15:17:50 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Definitions used by assembly code
|
2016-07-03 20:39:00 +00:00
|
|
|
asm _defs
|
2015-02-27 13:33:11 +00:00
|
|
|
|
2015-03-05 17:34:42 +00:00
|
|
|
; Use hi-bit ASCII for Apple II
|
|
|
|
!convtab "../../include/hiBitAscii.ct"
|
|
|
|
|
2015-02-27 13:33:11 +00:00
|
|
|
; Headers
|
2014-07-06 21:40:35 +00:00
|
|
|
!source "../../include/global.i"
|
|
|
|
!source "../../include/plasma.i"
|
|
|
|
!source "../../include/mem.i"
|
|
|
|
!source "../../include/fontEngine.i"
|
2021-10-10 16:30:40 +00:00
|
|
|
!source "../../include/sound.i"
|
2020-08-22 17:50:45 +00:00
|
|
|
!source "../../include/prorwts.i"
|
2015-02-27 13:33:11 +00:00
|
|
|
|
|
|
|
; Optional debug printing support
|
2015-06-06 16:37:04 +00:00
|
|
|
DEBUG = 0
|
2015-02-27 13:33:11 +00:00
|
|
|
|
|
|
|
; General use
|
|
|
|
tmp = $2
|
|
|
|
pTmp = $4
|
2015-11-19 16:27:04 +00:00
|
|
|
ysav = $34
|
2015-11-11 16:55:16 +00:00
|
|
|
ysav1 = $35
|
2015-02-27 13:33:11 +00:00
|
|
|
|
2015-08-21 15:57:15 +00:00
|
|
|
; 16-bit random number seed - incremented by ROM kbd routine
|
|
|
|
seed = $4E
|
2019-04-24 13:33:18 +00:00
|
|
|
MAGIC = $7FED ; largest prime < $8000
|
2015-08-21 15:57:15 +00:00
|
|
|
|
2015-12-29 16:59:13 +00:00
|
|
|
; NOTE ABOUT ABSOLUTE CODE ADDRESSING (e.g. STA .var, JMP .label, etc.)
|
2017-09-19 15:32:25 +00:00
|
|
|
; We cannot use it: this code will be preceded by stubs for the PLASMA routines, hence
|
|
|
|
; absolute addressing must be done carefully, adding ABS_OFFSET below.
|
|
|
|
;
|
|
|
|
; So don't JMP to labels, declare any variables as !byte or !word here, etc. without
|
|
|
|
; accounting for that.
|
2019-04-12 14:21:57 +00:00
|
|
|
;
|
|
|
|
; The way to account for that is to use ABS_OFFSET, and only within this module. This
|
|
|
|
; method works because this module is the only one loaded at an absolute address.
|
|
|
|
;
|
|
|
|
; See examples below.
|
2017-09-20 15:44:35 +00:00
|
|
|
ABS_OFFSET = (_DEFCNT*5) - 13 ; 13 for plasma's module header (stripped by packer)
|
2015-04-20 15:32:02 +00:00
|
|
|
|
2015-03-25 18:51:29 +00:00
|
|
|
end
|
|
|
|
|
2016-05-10 05:13:22 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-09-19 15:49:00 +00:00
|
|
|
// After scriptDisplayStr is called, we clear the PLASMA string pool. That way, many long strings
|
|
|
|
// can be used in a single function.
|
|
|
|
export asm scriptDisplayStr(str)#0
|
|
|
|
lda .callScriptDisplay + ABS_OFFSET + 2 ; first time init?
|
|
|
|
beq +
|
|
|
|
.callScriptDisplay
|
|
|
|
jsr 0 ; self-modified below
|
2016-07-07 21:22:48 +00:00
|
|
|
lda framePtr
|
|
|
|
sta outerFramePtr
|
|
|
|
lda framePtr+1
|
|
|
|
sta outerFramePtr+1
|
|
|
|
rts
|
2017-09-19 15:49:00 +00:00
|
|
|
+ ; first-time init
|
|
|
|
lda evalStkL,x
|
|
|
|
sta .callScriptDisplay + ABS_OFFSET + 1
|
|
|
|
lda evalStkH,x
|
|
|
|
sta .callScriptDisplay + ABS_OFFSET + 2
|
|
|
|
inx
|
|
|
|
rts
|
2016-05-10 05:13:22 +00:00
|
|
|
end
|
|
|
|
|
2015-04-09 17:31:36 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// API to call rendering engine (same API for raycaster and tile engine)
|
2017-06-10 15:56:23 +00:00
|
|
|
asm initDisplay(mapPartition, mapNum, pMapData, x, y, dir)#0
|
2017-06-10 14:21:58 +00:00
|
|
|
+asmPlasmNoRet 6
|
2016-07-07 21:22:48 +00:00
|
|
|
jmp $6000
|
2015-04-09 17:31:36 +00:00
|
|
|
end
|
2017-06-10 15:56:23 +00:00
|
|
|
export asm flipToPage1()#0
|
2017-06-10 14:21:58 +00:00
|
|
|
+asmPlasmNoRet 0
|
2016-07-07 21:22:48 +00:00
|
|
|
jmp $6003
|
2015-04-09 17:31:36 +00:00
|
|
|
end
|
2017-06-10 15:56:23 +00:00
|
|
|
export asm getPos(px, py)#0
|
2017-06-10 14:21:58 +00:00
|
|
|
+asmPlasmNoRet 2
|
2016-07-07 21:22:48 +00:00
|
|
|
jmp $6006
|
2015-04-09 17:31:36 +00:00
|
|
|
end
|
2017-06-10 15:56:23 +00:00
|
|
|
asm setPos(x, y)#0
|
2017-06-10 14:21:58 +00:00
|
|
|
+asmPlasmNoRet 2
|
2016-07-07 21:22:48 +00:00
|
|
|
jmp $6009
|
2015-04-09 17:31:36 +00:00
|
|
|
end
|
2017-06-10 15:56:23 +00:00
|
|
|
export asm getDir()#1 // returns: dir (0-15)
|
2017-06-10 14:21:58 +00:00
|
|
|
+asmPlasmRet 0
|
2016-07-07 21:22:48 +00:00
|
|
|
jmp $600C
|
2015-04-09 17:31:36 +00:00
|
|
|
end
|
2019-07-16 18:03:40 +00:00
|
|
|
export asm setDir(dir)#0
|
2017-06-10 14:32:31 +00:00
|
|
|
+asmPlasmNoRet 1
|
2016-07-07 21:22:48 +00:00
|
|
|
jmp $600F
|
2015-04-09 17:31:36 +00:00
|
|
|
end
|
2019-06-27 15:59:17 +00:00
|
|
|
asm advance(nSteps)#1 // returns: 0 if same pos, 1 if new pos, 2 if new pos and scripted
|
|
|
|
+asmPlasmRet 1
|
2016-07-07 21:22:48 +00:00
|
|
|
jmp $6012
|
2015-04-09 17:31:36 +00:00
|
|
|
end
|
2017-06-10 15:56:23 +00:00
|
|
|
asm setColor(slot, color)#0 // params: slot (0=sky/1=ground), color (0-17)
|
|
|
|
+asmPlasmNoRet 2
|
2016-07-07 21:22:48 +00:00
|
|
|
jmp $6015
|
2015-04-09 17:31:36 +00:00
|
|
|
end
|
2018-03-03 18:22:10 +00:00
|
|
|
asm render(intrOnKbd)#0
|
|
|
|
+asmPlasmNoRet 1
|
2016-07-07 21:22:48 +00:00
|
|
|
jmp $6018
|
2015-05-07 17:17:38 +00:00
|
|
|
end
|
2019-07-15 17:21:12 +00:00
|
|
|
asm _texControl(doLoad)#0
|
2017-06-10 15:56:23 +00:00
|
|
|
+asmPlasmNoRet 1
|
2016-07-07 21:22:48 +00:00
|
|
|
jmp $601B
|
2015-12-28 18:20:28 +00:00
|
|
|
end
|
2017-06-10 15:56:23 +00:00
|
|
|
asm getMapScript()#1
|
2017-06-10 14:21:58 +00:00
|
|
|
+asmPlasmRet 0
|
2016-07-07 21:22:48 +00:00
|
|
|
jmp $601E
|
2015-12-17 17:19:49 +00:00
|
|
|
end
|
2017-06-10 15:56:23 +00:00
|
|
|
asm setAvatar(tileNum)#0 // tile number (in the global tileset)
|
|
|
|
+asmPlasmNoRet 1
|
2016-07-11 00:18:03 +00:00
|
|
|
jmp $6021
|
|
|
|
end
|
2018-03-07 16:49:21 +00:00
|
|
|
asm copyTile(fromX, fromY, toX, toY)#0
|
2017-06-10 15:56:23 +00:00
|
|
|
+asmPlasmNoRet 4
|
2016-11-01 16:25:36 +00:00
|
|
|
jmp $6024
|
|
|
|
end
|
2015-12-17 17:19:49 +00:00
|
|
|
|
2017-01-05 22:27:44 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
// Memory copy - Non-overlapping regions only!
|
2018-04-16 12:35:40 +00:00
|
|
|
export asm memcpy(pSrc, pDst, len, auxWr)#0
|
|
|
|
+asmPlasmNoRet 4
|
|
|
|
lda evalStkL+3,x ; source ptr
|
2017-01-05 22:27:44 +00:00
|
|
|
sta tmp
|
2018-04-16 12:35:40 +00:00
|
|
|
lda evalStkH+3,x
|
2017-01-05 22:27:44 +00:00
|
|
|
sta tmp+1
|
2018-04-16 12:35:40 +00:00
|
|
|
lda evalStkL+2,x ; dest ptr
|
2017-01-05 22:27:44 +00:00
|
|
|
sta pTmp
|
2018-04-16 12:35:40 +00:00
|
|
|
lda evalStkH+2,x
|
2017-01-05 22:27:44 +00:00
|
|
|
sta pTmp+1
|
2018-04-16 12:35:40 +00:00
|
|
|
ldy evalStkL,x ; auxWr
|
|
|
|
sei ; prevent interrupts while possibly in aux
|
|
|
|
sta clrAuxWr,y
|
|
|
|
lda evalStkH+1,x ; len hi
|
2017-01-05 22:27:44 +00:00
|
|
|
pha
|
2018-04-16 12:35:40 +00:00
|
|
|
lda evalStkL+1,x ; len lo
|
2017-01-05 22:27:44 +00:00
|
|
|
tax
|
|
|
|
ldy #0
|
|
|
|
.pglup:
|
|
|
|
pla
|
|
|
|
sec
|
|
|
|
sbc #1
|
|
|
|
bcc .part
|
|
|
|
pha
|
|
|
|
- lda (tmp),y
|
|
|
|
sta (pTmp),y
|
|
|
|
iny
|
|
|
|
bne -
|
|
|
|
inc tmp+1
|
|
|
|
inc pTmp+1
|
|
|
|
bne .pglup ; always taken
|
|
|
|
.part:
|
2023-09-03 21:41:10 +00:00
|
|
|
txa
|
2017-01-05 22:27:44 +00:00
|
|
|
beq .done
|
|
|
|
- lda (tmp),y
|
|
|
|
sta (pTmp),y
|
|
|
|
iny
|
|
|
|
dex
|
|
|
|
bne -
|
|
|
|
.done
|
2018-04-16 12:35:40 +00:00
|
|
|
sta clrAuxWr
|
|
|
|
cli ; inerrupts ok now that we're back in main
|
2017-01-05 22:27:44 +00:00
|
|
|
rts
|
|
|
|
end
|
|
|
|
|
2017-03-22 16:46:17 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
export asm memset(pDst, val, len)#0
|
|
|
|
+asmPlasmNoRet 3
|
2017-03-22 16:46:17 +00:00
|
|
|
ldy #0
|
|
|
|
lda evalStkL+2,x ; dest ptr
|
|
|
|
sta pTmp
|
|
|
|
lda evalStkH+2,x
|
|
|
|
sta pTmp+1
|
|
|
|
lda evalStkL+1,x ; value
|
|
|
|
sta tmp
|
|
|
|
lda evalStkL,x ; len lo
|
|
|
|
pha
|
|
|
|
lda evalStkH,x ; len hi
|
|
|
|
beq +
|
2023-09-03 21:42:51 +00:00
|
|
|
tax
|
2017-03-22 16:46:17 +00:00
|
|
|
lda tmp
|
|
|
|
- sta (pTmp),y
|
|
|
|
iny
|
|
|
|
bne -
|
|
|
|
inc pTmp+1
|
|
|
|
dex
|
|
|
|
bne -
|
|
|
|
+ pla
|
|
|
|
beq +
|
|
|
|
tax
|
|
|
|
lda tmp
|
|
|
|
- sta (pTmp),y
|
|
|
|
iny
|
|
|
|
dex
|
|
|
|
bne -
|
|
|
|
+ rts
|
|
|
|
end
|
|
|
|
|
2015-12-17 17:19:49 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2018-04-26 16:19:33 +00:00
|
|
|
export asm readAuxByte(ptr)#1
|
2017-06-10 14:21:58 +00:00
|
|
|
+asmPlasmRet 1
|
2017-09-20 15:44:35 +00:00
|
|
|
sta pTmp
|
|
|
|
sty pTmp+1
|
|
|
|
ldy #12
|
|
|
|
- lda .rdauxb-1+ABS_OFFSET, y
|
|
|
|
sta $10-1,y
|
|
|
|
dey
|
|
|
|
bne -
|
|
|
|
sei ; prevent interrupts while in aux mem
|
|
|
|
sta setAuxRd
|
2023-09-03 21:41:10 +00:00
|
|
|
jmp $10
|
|
|
|
.rdauxb
|
2017-09-20 15:44:35 +00:00
|
|
|
lda (pTmp),y
|
|
|
|
sta clrAuxRd
|
|
|
|
cli
|
|
|
|
rts
|
2015-12-17 17:19:49 +00:00
|
|
|
end
|
|
|
|
|
2015-11-11 16:55:16 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2016-07-12 15:09:55 +00:00
|
|
|
// String building for display with the font engine. Includes plurality processing to handily
|
2015-11-19 16:27:04 +00:00
|
|
|
// handle things like "Dirt bag(s)" and "his/their"
|
2019-04-12 14:21:57 +00:00
|
|
|
export asm buildString()#0
|
|
|
|
+asmPlasmNoRet 0
|
|
|
|
lda cswl
|
|
|
|
sta prevCSWL+ABS_OFFSET
|
|
|
|
lda cswh
|
|
|
|
sta prevCSWL+1+ABS_OFFSET
|
|
|
|
lda #<addToString+ABS_OFFSET
|
2016-07-07 21:22:48 +00:00
|
|
|
sta cswl
|
2019-04-12 14:21:57 +00:00
|
|
|
lda #>addToString+ABS_OFFSET
|
|
|
|
sta cswh
|
2016-07-07 21:22:48 +00:00
|
|
|
lda #0
|
|
|
|
sta inbuf
|
|
|
|
rts
|
2019-04-12 14:21:57 +00:00
|
|
|
addToString:
|
2016-07-07 21:22:48 +00:00
|
|
|
sty ysav1
|
|
|
|
inc inbuf
|
|
|
|
ldy inbuf
|
2019-11-07 20:01:46 +00:00
|
|
|
and #$7F
|
2016-07-07 21:22:48 +00:00
|
|
|
sta inbuf,y
|
|
|
|
ldy ysav1
|
|
|
|
rts
|
2019-04-12 14:21:57 +00:00
|
|
|
prevCSWL !word 0
|
2015-11-11 16:55:16 +00:00
|
|
|
end
|
|
|
|
|
2017-05-24 14:47:47 +00:00
|
|
|
// Complete string building (including plural processing), and return pointer
|
|
|
|
// to the string (in the input buffer)
|
2017-06-10 15:56:23 +00:00
|
|
|
export asm finishString(isPlural)#1
|
2015-11-19 16:27:04 +00:00
|
|
|
!zone {
|
2017-06-10 14:21:58 +00:00
|
|
|
+asmPlasmRet 1
|
2023-09-03 21:41:10 +00:00
|
|
|
ldy prevCSWL+ABS_OFFSET ; put the cout vector back to default
|
|
|
|
sty cswl
|
|
|
|
ldy prevCSWL+1+ABS_OFFSET ; put the cout vector back to default
|
|
|
|
sty cswh
|
|
|
|
ldy #0 ; dest offset in Y (will be incremented before store)
|
|
|
|
cpy inbuf
|
|
|
|
beq .done1 ; failsafe: handle zero-length string
|
2019-11-07 20:01:46 +00:00
|
|
|
tax ; test for isPlural == 0
|
|
|
|
beq +
|
|
|
|
lda #$40 ; for setting V later
|
|
|
|
+ sta tmp ; save isPlural flag
|
2017-08-15 17:01:28 +00:00
|
|
|
clv ; V flag for prev-is-alpha
|
|
|
|
ldx #0 ; source offset in X (will be incremented before load)
|
2016-07-07 21:22:48 +00:00
|
|
|
.fetch
|
2017-08-15 17:01:28 +00:00
|
|
|
inx
|
|
|
|
lda inbuf,x ; get next input char
|
|
|
|
iny
|
2019-11-07 20:01:46 +00:00
|
|
|
and #$7F ; clear hi-bit for final string, in case it gets interned
|
2017-08-15 17:01:28 +00:00
|
|
|
sta inbuf,y ; by default copy the char to output
|
2019-11-07 20:01:46 +00:00
|
|
|
ora #$80
|
2017-08-15 17:01:28 +00:00
|
|
|
cmp #"(" ; plural processing triggered by parentheses
|
2016-07-07 21:22:48 +00:00
|
|
|
bne .notpar
|
2017-08-15 17:01:28 +00:00
|
|
|
bvc .notpar ; but only parens directly after alpha char, e.g. preserving "Happy (and safe)."
|
|
|
|
|
|
|
|
dey ; undo copy of the paren
|
|
|
|
stx tmp+1 ; save position in input
|
2019-11-07 20:01:46 +00:00
|
|
|
bit tmp ; set copy flag (V) initially to same as isPlural flag
|
2017-08-15 17:01:28 +00:00
|
|
|
.findsl ; see if there's a slash within the parens
|
2016-07-07 21:22:48 +00:00
|
|
|
inx
|
|
|
|
cpx inbuf
|
2023-09-03 21:42:51 +00:00
|
|
|
bcs .done ; failsafe: handle missing end-paren
|
2017-08-15 17:01:28 +00:00
|
|
|
lda inbuf,x
|
2019-11-07 20:01:46 +00:00
|
|
|
ora #$80 ; normalize hi-bit for comparisons below
|
2017-08-15 17:01:28 +00:00
|
|
|
cmp #"/"
|
|
|
|
bne +
|
|
|
|
php
|
|
|
|
pla
|
|
|
|
eor #$40 ; flip V flag, meaning singular text is before slash, plural after.
|
|
|
|
pha
|
|
|
|
plp
|
|
|
|
+ cmp #")" ; scan until ending paren
|
2023-09-03 21:41:10 +00:00
|
|
|
bne .findsl ; loop to scan next char
|
|
|
|
ldx tmp+1 ; get back to start of parens
|
2017-08-15 17:01:28 +00:00
|
|
|
; copy mode flag is now in V: if slash present, single=copy, plural=nocopy
|
|
|
|
; if no slash: single=nocopy, plural=copy
|
|
|
|
.plup
|
|
|
|
inx
|
|
|
|
lda inbuf,x
|
2019-11-07 20:01:46 +00:00
|
|
|
ora #$80
|
2017-08-15 17:01:28 +00:00
|
|
|
cmp #"/"
|
|
|
|
bne +
|
|
|
|
php
|
|
|
|
pla
|
|
|
|
eor #$40 ; flip from copying to not-copying, or vice-versa
|
|
|
|
pha
|
|
|
|
plp
|
|
|
|
bcs .plup ; always taken
|
|
|
|
+ cmp #")"
|
2017-08-25 15:16:06 +00:00
|
|
|
beq .notpar ; stop at closing paren
|
2017-08-15 17:01:28 +00:00
|
|
|
bvc .plup ; if not in copy mode, skip copy
|
2016-07-07 21:22:48 +00:00
|
|
|
iny
|
2019-11-07 20:01:46 +00:00
|
|
|
and #$7F ; clear hi-bit for final string, in case it gets interned
|
2017-08-15 17:01:28 +00:00
|
|
|
sta inbuf,y ; else do copy
|
|
|
|
bne .plup ; always taken
|
2016-07-07 21:22:48 +00:00
|
|
|
.notpar
|
2017-08-15 17:01:28 +00:00
|
|
|
bit fixedRTS; set prev-is-alpha flag
|
|
|
|
cmp #"A" ; if >= ASCII "A", consider it alpha
|
2017-08-25 15:16:06 +00:00
|
|
|
bcs .next
|
2017-08-15 17:01:28 +00:00
|
|
|
clv ; clear prev-is-alpha flag
|
2016-07-07 21:22:48 +00:00
|
|
|
|
|
|
|
.next
|
|
|
|
cpx inbuf ; compare src offset to length
|
|
|
|
bcc .fetch ; loop while less than
|
|
|
|
|
|
|
|
.done
|
|
|
|
sty inbuf ; save new length
|
2023-09-03 21:41:10 +00:00
|
|
|
.done1
|
2017-05-24 14:47:47 +00:00
|
|
|
lda #<inbuf ; return pointer to string
|
2016-07-07 21:22:48 +00:00
|
|
|
ldy #>inbuf
|
|
|
|
rts
|
2015-11-19 16:27:04 +00:00
|
|
|
}
|
2015-11-11 16:55:16 +00:00
|
|
|
end
|
|
|
|
|
2015-05-27 15:13:09 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2019-02-21 17:23:34 +00:00
|
|
|
export asm blit(isAux, srcData, dstScreenPtr, nLines, lineSize)#0
|
2017-08-04 15:15:40 +00:00
|
|
|
+asmPlasmNoRet 5
|
2016-10-11 16:03:57 +00:00
|
|
|
|
|
|
|
; Save line size
|
|
|
|
sta ysav
|
|
|
|
|
|
|
|
; Save nLines
|
|
|
|
lda evalStkL+1,x
|
|
|
|
pha
|
2016-07-07 21:22:48 +00:00
|
|
|
|
|
|
|
; Save the dest pointer
|
2016-10-11 16:03:57 +00:00
|
|
|
lda evalStkL+2,x
|
2016-07-07 21:22:48 +00:00
|
|
|
sta pTmp
|
2016-10-11 16:03:57 +00:00
|
|
|
lda evalStkH+2,x
|
|
|
|
sta pTmp+1
|
2016-07-07 21:22:48 +00:00
|
|
|
|
|
|
|
; Save the source pointer
|
2016-10-11 16:03:57 +00:00
|
|
|
lda evalStkL+3,x
|
2016-07-07 21:22:48 +00:00
|
|
|
sta tmp
|
2016-10-11 16:03:57 +00:00
|
|
|
lda evalStkH+3,x
|
2016-07-07 21:22:48 +00:00
|
|
|
sta tmp+1
|
|
|
|
|
2017-08-04 15:15:40 +00:00
|
|
|
; Save aux/main flag
|
|
|
|
lda evalStkL+4,x
|
|
|
|
lsr ; to carry bit
|
|
|
|
bcc +
|
2017-09-20 15:44:35 +00:00
|
|
|
ldy #15 ; put aux copy routine in zero page
|
2023-09-03 21:41:10 +00:00
|
|
|
- ldx .cpaux + ABS_OFFSET,y
|
|
|
|
stx $10,y
|
2017-09-20 15:44:35 +00:00
|
|
|
dey
|
|
|
|
bpl -
|
2017-08-04 15:15:40 +00:00
|
|
|
+ pla ; get line count
|
2016-10-11 16:03:57 +00:00
|
|
|
tax
|
2017-08-04 15:15:40 +00:00
|
|
|
--
|
|
|
|
ldy ysav ; get byte count
|
2016-10-11 16:03:57 +00:00
|
|
|
dey
|
2017-08-04 15:15:40 +00:00
|
|
|
bcc +
|
|
|
|
jsr $10 ; copy pixel bytes (aux version)
|
|
|
|
bcs ++
|
|
|
|
+
|
|
|
|
- lda (tmp),y
|
|
|
|
sta (pTmp),y
|
|
|
|
dey
|
|
|
|
bpl -
|
|
|
|
++
|
|
|
|
php
|
2017-03-22 16:46:17 +00:00
|
|
|
lda tmp ; advance to next row of data
|
2016-07-07 21:22:48 +00:00
|
|
|
clc
|
2016-10-11 16:03:57 +00:00
|
|
|
adc ysav
|
2016-07-07 21:22:48 +00:00
|
|
|
sta tmp
|
|
|
|
bcc +
|
|
|
|
inc tmp+1
|
2017-03-22 16:46:17 +00:00
|
|
|
+ jsr NextScreenLine ; and next screen line
|
2017-08-04 15:15:40 +00:00
|
|
|
plp
|
2017-03-22 16:46:17 +00:00
|
|
|
dex
|
2017-08-04 15:15:40 +00:00
|
|
|
bne -- ; Loop until we've done all rows.
|
2017-03-22 16:46:17 +00:00
|
|
|
rts
|
2017-09-20 15:44:35 +00:00
|
|
|
.cpaux
|
|
|
|
sei ; avoid interrupts while reading aux
|
|
|
|
sta setAuxRd
|
|
|
|
- lda (tmp),y
|
|
|
|
sta (pTmp),y
|
|
|
|
dey
|
|
|
|
bpl -
|
|
|
|
sta clrAuxRd
|
|
|
|
cli
|
|
|
|
rts
|
2017-03-22 16:46:17 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
asm vline(dstScreenPtr, val, nLines)#0
|
|
|
|
+asmPlasmNoRet 3
|
2017-03-22 16:46:17 +00:00
|
|
|
|
|
|
|
; Save number of lines
|
|
|
|
pha
|
|
|
|
|
|
|
|
; Save value
|
|
|
|
lda evalStkL+1,x
|
|
|
|
sta tmp
|
|
|
|
|
|
|
|
; Save the dest pointer
|
|
|
|
lda evalStkL+2,x
|
2016-07-07 21:22:48 +00:00
|
|
|
sta pTmp
|
2017-03-22 16:46:17 +00:00
|
|
|
lda evalStkH+2,x
|
|
|
|
sta pTmp+1
|
|
|
|
|
|
|
|
pla ; line count
|
|
|
|
tax
|
|
|
|
- ldy #0
|
|
|
|
lda tmp
|
|
|
|
sta (pTmp),y
|
|
|
|
jsr NextScreenLine
|
2016-07-07 21:22:48 +00:00
|
|
|
dex
|
2017-03-22 16:46:17 +00:00
|
|
|
bne -
|
2016-07-07 21:22:48 +00:00
|
|
|
rts
|
2015-05-27 15:13:09 +00:00
|
|
|
end
|
|
|
|
|
2015-04-20 15:32:02 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Simply retrieve the X register. Used to double-check that we're not leaking PLASMA eval
|
|
|
|
// stack entries.
|
2017-06-10 15:56:23 +00:00
|
|
|
asm getXReg()#1
|
2017-06-10 14:21:58 +00:00
|
|
|
+asmPlasmRet 0
|
2016-07-07 21:22:48 +00:00
|
|
|
txa
|
|
|
|
ldy #0
|
|
|
|
rts
|
2015-04-20 15:32:02 +00:00
|
|
|
end
|
|
|
|
|
2015-06-30 16:27:10 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2018-04-16 14:46:54 +00:00
|
|
|
// Calculate 16-bit hash of a buffer. Note: ignores anything beyond 256 bytes.
|
2017-06-10 15:56:23 +00:00
|
|
|
export asm hashBuffer(ptr, len)#1
|
2017-06-10 14:21:58 +00:00
|
|
|
+asmPlasmRet 2
|
2016-08-05 16:27:11 +00:00
|
|
|
lda evalStkL+1,x ; first arg is buffer pointer
|
2016-07-07 21:22:48 +00:00
|
|
|
sta pTmp
|
2016-08-05 16:27:11 +00:00
|
|
|
lda evalStkH+1,x
|
|
|
|
sta pTmp+1
|
|
|
|
lda evalStkL,x ; second arg is length
|
|
|
|
tax
|
2016-07-07 21:22:48 +00:00
|
|
|
ldy #0
|
2018-01-09 18:02:28 +00:00
|
|
|
sty tmp
|
2016-08-05 16:27:11 +00:00
|
|
|
tya
|
2018-01-09 18:02:28 +00:00
|
|
|
- clc
|
|
|
|
adc (pTmp),y
|
|
|
|
bcc +
|
|
|
|
inc tmp
|
|
|
|
+ lsr
|
|
|
|
ror tmp
|
|
|
|
bcc +
|
|
|
|
ora #$80
|
|
|
|
+ lsr
|
|
|
|
ror tmp
|
|
|
|
bcc +
|
|
|
|
ora #$80
|
|
|
|
+ iny
|
2016-07-07 21:22:48 +00:00
|
|
|
dex
|
|
|
|
bne -
|
2018-01-09 18:02:28 +00:00
|
|
|
ldy tmp
|
2016-07-07 21:22:48 +00:00
|
|
|
rts
|
2015-06-30 16:27:10 +00:00
|
|
|
end
|
|
|
|
|
2014-08-21 15:17:50 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2016-03-22 15:22:26 +00:00
|
|
|
// Print a string to the current character output vector
|
2017-06-10 15:56:23 +00:00
|
|
|
export asm puts(str)#0
|
2017-06-10 14:21:58 +00:00
|
|
|
+asmPlasmNoRet 1
|
2016-07-07 21:22:48 +00:00
|
|
|
sta pTmp
|
2017-06-02 15:25:29 +00:00
|
|
|
lda #'!'
|
|
|
|
ldx #1
|
2023-09-03 21:41:10 +00:00
|
|
|
tya
|
2017-06-02 15:25:29 +00:00
|
|
|
beq + ; safety: print '!' instead of null string
|
2023-09-03 21:41:10 +00:00
|
|
|
sty pTmp+1
|
2016-07-07 21:22:48 +00:00
|
|
|
ldy #0
|
|
|
|
lda (pTmp),y
|
|
|
|
tax
|
2017-07-16 19:34:24 +00:00
|
|
|
beq ++ ; handle empty string
|
2016-07-07 21:22:48 +00:00
|
|
|
iny
|
|
|
|
- lda (pTmp),y
|
2017-06-02 15:25:29 +00:00
|
|
|
+ ora #$80
|
2016-08-08 14:47:01 +00:00
|
|
|
+safeCout
|
2016-07-07 21:22:48 +00:00
|
|
|
iny
|
|
|
|
dex
|
|
|
|
bne -
|
2017-07-16 19:34:24 +00:00
|
|
|
++ rts
|
2014-07-06 14:40:57 +00:00
|
|
|
end
|
|
|
|
|
2015-09-05 18:01:45 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Get a character from the keyboard
|
2017-06-10 15:56:23 +00:00
|
|
|
export asm rdkey()#1
|
2017-06-10 14:21:58 +00:00
|
|
|
+asmPlasmRet 0
|
2016-08-08 15:09:46 +00:00
|
|
|
+safeRdkey
|
2016-08-08 14:47:01 +00:00
|
|
|
ldy #0
|
|
|
|
rts
|
2015-09-05 18:01:45 +00:00
|
|
|
end
|
|
|
|
|
2014-08-21 15:17:50 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2015-03-05 17:34:42 +00:00
|
|
|
// Print part of a string, until we hit the end or a '%' code. Return how far we got, or -1 for end.
|
2017-06-10 15:56:23 +00:00
|
|
|
asm partialPrintf(str, pos)#1
|
2015-04-20 15:32:02 +00:00
|
|
|
!zone {
|
2017-06-10 14:21:58 +00:00
|
|
|
+asmPlasmRet 2
|
2016-07-07 21:22:48 +00:00
|
|
|
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 +
|
2016-08-08 14:47:01 +00:00
|
|
|
+safeCout
|
2016-07-07 21:22:48 +00:00
|
|
|
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
|
2015-04-20 15:32:02 +00:00
|
|
|
}
|
2015-03-05 17:34:42 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Print a 16-bit hex value
|
2017-06-10 15:56:23 +00:00
|
|
|
export asm printHex(num)#0
|
|
|
|
+asmPlasmNoRet 1
|
2016-08-22 15:08:22 +00:00
|
|
|
pha
|
|
|
|
tya
|
|
|
|
+safePrbyte
|
|
|
|
pla
|
|
|
|
jmp _safePrbyte
|
2015-03-05 17:34:42 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Print a single character
|
2020-06-15 14:21:37 +00:00
|
|
|
export asm printChar(ch)#0
|
2017-06-10 15:56:23 +00:00
|
|
|
+asmPlasmNoRet 1
|
2016-07-07 21:22:48 +00:00
|
|
|
ora #$80
|
2016-08-08 14:47:01 +00:00
|
|
|
jmp _safeCout
|
2014-06-25 15:47:28 +00:00
|
|
|
end
|
2014-07-03 23:56:26 +00:00
|
|
|
|
2015-01-10 21:17:56 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Print a carriage return
|
2017-08-14 15:16:38 +00:00
|
|
|
export asm crout()#0
|
2017-06-10 15:56:23 +00:00
|
|
|
+asmPlasmNoRet 0
|
2016-08-08 14:47:01 +00:00
|
|
|
lda #$8D
|
|
|
|
jmp _safeCout
|
2014-07-06 23:02:52 +00:00
|
|
|
end
|
|
|
|
|
2014-08-21 15:17:50 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2015-01-10 21:17:56 +00:00
|
|
|
// Send a command to the memory manager
|
2017-06-10 15:56:23 +00:00
|
|
|
export asm mmgr(cmd, wordParam)#1
|
2017-06-10 14:21:58 +00:00
|
|
|
+asmPlasmRet 2
|
2017-09-20 15:44:35 +00:00
|
|
|
jsr .setmmgr+ABS_OFFSET
|
|
|
|
jsr mainLoader ; ret value in X=lo/Y=hi
|
|
|
|
txa ; to A=lo/Y=hi for asmPlasm
|
|
|
|
rts
|
|
|
|
.setmmgr
|
2016-07-07 21:22:48 +00:00
|
|
|
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
|
|
|
|
rts
|
2015-08-19 14:22:20 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
// Aux version of memory manager command
|
2017-06-10 15:56:23 +00:00
|
|
|
export asm auxMmgr(cmd, wordParam)#1
|
2017-06-10 14:21:58 +00:00
|
|
|
+asmPlasmRet 2
|
2017-09-20 15:44:35 +00:00
|
|
|
jsr .setmmgr+ABS_OFFSET
|
2016-07-07 21:22:48 +00:00
|
|
|
jsr auxLoader ; ret value in X=lo/Y=hi
|
|
|
|
txa ; to A=lo/Y=hi for asmPlasm
|
|
|
|
rts
|
2014-07-06 21:40:35 +00:00
|
|
|
end
|
|
|
|
|
2019-08-08 16:45:01 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Get mem mgr's flag telling if this is the floppy version or not
|
|
|
|
asm getFloppyFlg()#1
|
|
|
|
+asmPlasmRet 0
|
|
|
|
lda floppyFlg
|
|
|
|
ldy #0
|
|
|
|
rts
|
|
|
|
end
|
|
|
|
|
2015-03-03 17:03:22 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2021-10-10 16:30:40 +00:00
|
|
|
// Generate a sound on the Apple II speaker
|
|
|
|
export asm genSound(dnnoise, dnvelo, dndelay, upnoise, upvelo, updelay, dur)#0
|
|
|
|
+asmPlasmNoRet 7
|
|
|
|
jmp genSound
|
2015-03-03 17:03:22 +00:00
|
|
|
end
|
|
|
|
|
2015-06-07 22:50:06 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2015-11-30 16:02:32 +00:00
|
|
|
// Use the font engine to clear the current text window.
|
|
|
|
// Parameters: top, bottom, left, right
|
2017-06-10 15:56:23 +00:00
|
|
|
export asm setWindow(top, bottom, left, right)#0
|
|
|
|
+asmPlasmNoRet 4
|
2015-06-07 22:50:06 +00:00
|
|
|
jmp SetWindow
|
|
|
|
end
|
|
|
|
|
2017-09-11 18:16:10 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Get the font engine's current text window
|
|
|
|
// Returns: top, bottom, left, right
|
|
|
|
export asm getWindow()#4
|
|
|
|
bit setLcRW+lcBank2
|
|
|
|
jmp GetWindow
|
|
|
|
end
|
|
|
|
|
2015-12-05 23:01:44 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-07-08 23:36:08 +00:00
|
|
|
// Get the cursor position - returns X, Y
|
|
|
|
export asm getCursor()#2
|
|
|
|
bit setLcRW+lcBank2
|
|
|
|
jmp GetCursor
|
2015-12-05 23:01:44 +00:00
|
|
|
end
|
|
|
|
|
2016-03-22 15:22:26 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// 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.
|
2017-06-10 15:56:23 +00:00
|
|
|
asm rawGetStr()#1
|
2017-06-10 14:21:58 +00:00
|
|
|
+asmPlasmRet 0
|
2016-03-22 15:22:26 +00:00
|
|
|
jmp GetStr
|
|
|
|
end
|
|
|
|
|
2014-08-21 15:17:50 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Use the font engine to clear the current text window
|
2017-06-10 15:56:23 +00:00
|
|
|
export asm clearWindow()#0
|
|
|
|
+asmPlasmNoRet 0
|
2015-06-07 22:50:06 +00:00
|
|
|
jmp ClearWindow
|
2014-07-09 02:11:03 +00:00
|
|
|
end
|
|
|
|
|
2015-06-11 18:02:02 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2018-11-15 17:00:20 +00:00
|
|
|
// Use the font engine to copy the current text window to hi-res page 2 (or vice-versa)
|
2018-01-05 17:17:01 +00:00
|
|
|
// If flip=0, copies page 1 to page 2
|
|
|
|
// If flip=$60, copies page 2 to page 1
|
2018-11-15 17:00:20 +00:00
|
|
|
export asm copyWindow(flip)#0
|
2018-01-05 17:17:01 +00:00
|
|
|
+asmPlasmNoRet 1
|
|
|
|
tay
|
2015-06-11 18:02:02 +00:00
|
|
|
jmp CopyWindow
|
|
|
|
end
|
|
|
|
|
2015-06-08 15:07:04 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Display a character using the font engine.
|
2017-06-10 15:56:23 +00:00
|
|
|
export asm displayChar(ch)#0
|
|
|
|
+asmPlasmNoRet 1
|
2015-06-08 15:07:04 +00:00
|
|
|
jmp DisplayChar
|
|
|
|
end
|
|
|
|
|
2015-05-27 15:13:09 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2015-06-07 20:40:04 +00:00
|
|
|
// Display a string using the font engine.
|
2019-05-02 15:06:36 +00:00
|
|
|
asm internalDisplayStr(str)#0
|
2017-06-10 15:56:23 +00:00
|
|
|
+asmPlasmNoRet 1
|
2015-06-07 22:50:06 +00:00
|
|
|
jmp DisplayStr
|
2014-07-08 16:39:31 +00:00
|
|
|
end
|
|
|
|
|
2015-11-05 17:09:45 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Calculate string width using the font engine.
|
2017-06-10 15:56:23 +00:00
|
|
|
export asm calcWidth(pStr)#1
|
2017-06-10 14:21:58 +00:00
|
|
|
+asmPlasmRet 1
|
2015-11-05 17:09:45 +00:00
|
|
|
jmp CalcWidth
|
|
|
|
end
|
|
|
|
|
2017-03-22 16:46:17 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Get the address of the given hi-res screen line
|
2017-06-10 15:56:23 +00:00
|
|
|
export asm getScreenLine(n)#1
|
2017-06-10 14:21:58 +00:00
|
|
|
+asmPlasmRet 1
|
2017-03-22 16:46:17 +00:00
|
|
|
jmp GetScreenLine
|
|
|
|
end
|
|
|
|
|
2015-07-11 15:58:00 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-07-14 15:41:10 +00:00
|
|
|
// Display a string using the font engine but not its parser. Also, interpret "^A" as ctrl chars.
|
2017-06-10 15:56:23 +00:00
|
|
|
export asm rawDisplayStr(pStr)#0
|
|
|
|
+asmPlasmNoRet 1
|
2015-07-11 15:58:00 +00:00
|
|
|
sta pTmp
|
|
|
|
sty pTmp+1
|
|
|
|
ldy #0
|
|
|
|
lda (pTmp),y
|
|
|
|
sta tmp
|
|
|
|
- cpy tmp
|
2023-09-03 21:41:10 +00:00
|
|
|
bcs ++
|
|
|
|
iny
|
2015-07-11 15:58:00 +00:00
|
|
|
lda (pTmp),y
|
|
|
|
ora #$80
|
|
|
|
cmp #"^"
|
|
|
|
bne +
|
|
|
|
iny
|
|
|
|
lda (pTmp),y
|
2017-07-14 15:41:10 +00:00
|
|
|
cmp #"^"
|
|
|
|
beq +
|
2015-07-11 15:58:00 +00:00
|
|
|
and #$1F
|
|
|
|
ora #$80
|
|
|
|
+ sty tmp+1
|
|
|
|
jsr DisplayChar
|
|
|
|
ldy tmp+1
|
2023-09-03 21:41:10 +00:00
|
|
|
bne - ; always taken
|
|
|
|
++rts
|
2015-07-11 15:58:00 +00:00
|
|
|
end
|
|
|
|
|
2015-08-21 15:57:15 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2019-04-24 13:33:18 +00:00
|
|
|
// Random number generator. A standard linear congruential prng featuring a full cycle (i.e.
|
|
|
|
// every value from 1..MAGIC-1 is part of the sequence).
|
|
|
|
asm internal_rand16()#1
|
|
|
|
!zone {
|
2017-06-10 14:21:58 +00:00
|
|
|
+asmPlasmRet 0
|
2019-04-24 16:22:44 +00:00
|
|
|
ldx #6 ; start check = 1, plus 5 loops to mul by 32 = 6 total
|
|
|
|
ldy seed
|
2015-08-21 15:57:15 +00:00
|
|
|
lda seed+1
|
2019-04-24 13:33:18 +00:00
|
|
|
and #$7F ; in case kbd routine has advanced past MAGIC
|
2015-08-21 15:57:15 +00:00
|
|
|
sta seed+1
|
2019-04-24 16:22:44 +00:00
|
|
|
bne .chk ; zero check - hi byte
|
|
|
|
tya
|
|
|
|
bne .chk ; zero check - lo byte
|
|
|
|
lda #1 ; force 0 seed to go to 1
|
|
|
|
bne .ret ; always taken
|
2019-04-24 13:33:18 +00:00
|
|
|
.shift
|
2019-04-24 16:22:44 +00:00
|
|
|
asl
|
|
|
|
tay
|
2019-04-24 13:33:18 +00:00
|
|
|
rol seed+1
|
|
|
|
.chk
|
2019-04-24 16:22:44 +00:00
|
|
|
cpy #<MAGIC
|
2015-08-21 15:57:15 +00:00
|
|
|
lda seed+1
|
2019-04-24 13:33:18 +00:00
|
|
|
sbc #>MAGIC
|
|
|
|
bcc .next
|
2015-08-21 15:57:15 +00:00
|
|
|
sta seed+1
|
2019-04-24 16:22:44 +00:00
|
|
|
tya
|
|
|
|
sbc #<MAGIC
|
|
|
|
!byte $C9 ; CMP imm - to skip next opcode
|
2019-04-24 13:33:18 +00:00
|
|
|
.next
|
2019-04-24 16:22:44 +00:00
|
|
|
tya
|
2019-04-24 13:33:18 +00:00
|
|
|
dex
|
2019-04-24 16:22:44 +00:00
|
|
|
bne .shift ; 32 / 25
|
2019-04-24 13:33:18 +00:00
|
|
|
.ret
|
2019-04-24 16:22:44 +00:00
|
|
|
sta seed
|
2019-04-24 13:33:18 +00:00
|
|
|
ldy seed+1
|
2015-08-21 15:57:15 +00:00
|
|
|
rts
|
2019-04-24 13:33:18 +00:00
|
|
|
}
|
2015-08-21 15:57:15 +00:00
|
|
|
end
|
|
|
|
|
2016-09-24 18:10:17 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Clear the text-mode screen, and put the text cursor at the top of it.
|
2017-06-10 15:56:23 +00:00
|
|
|
export asm textHome()#0
|
|
|
|
+asmPlasmNoRet 0
|
2016-09-24 18:10:17 +00:00
|
|
|
+safeHome
|
|
|
|
rts
|
|
|
|
end
|
|
|
|
|
2017-06-24 18:43:16 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Compare two strings for equality, ignoring case.
|
|
|
|
export asm streqi(a, b)#1
|
|
|
|
+asmPlasmRet 2
|
|
|
|
sta tmp
|
|
|
|
sty tmp+1
|
2021-10-19 14:58:11 +00:00
|
|
|
tya
|
|
|
|
beq .null
|
2017-06-24 18:43:16 +00:00
|
|
|
lda evalStkL+1,x
|
|
|
|
sta pTmp
|
|
|
|
lda evalStkH+1,x
|
2021-10-19 14:58:11 +00:00
|
|
|
beq .null
|
2023-09-03 21:42:51 +00:00
|
|
|
sta pTmp+1
|
2017-06-24 18:43:16 +00:00
|
|
|
ldy #0
|
|
|
|
lda (tmp),y
|
|
|
|
cmp (pTmp),y
|
2017-09-13 15:06:52 +00:00
|
|
|
beq + ; if lengths not equal, strings can't be equal
|
|
|
|
.noteqi
|
2017-06-24 18:43:16 +00:00
|
|
|
lda #0
|
|
|
|
tay
|
|
|
|
rts
|
2017-09-13 15:06:52 +00:00
|
|
|
+ tax ; count up to (verified same) length of the strings
|
2017-06-24 18:43:16 +00:00
|
|
|
- iny
|
|
|
|
lda (tmp),y
|
2023-09-03 22:08:16 +00:00
|
|
|
eor (pTmp),y
|
2023-09-03 21:41:10 +00:00
|
|
|
beq + ; matched
|
2023-09-03 22:08:16 +00:00
|
|
|
cmp #$20 ; check for case bit
|
2023-09-03 21:41:10 +00:00
|
|
|
bne .noteqi ; abort on alpha inequality
|
2023-09-03 22:08:16 +00:00
|
|
|
ora (tmp),y ; convert to lower case
|
|
|
|
cmp #('z'&$7F)+1
|
|
|
|
bcs .noteqi ; abort on inequality
|
2017-06-24 18:43:16 +00:00
|
|
|
cmp #'a'&$7F
|
2023-09-03 22:08:16 +00:00
|
|
|
bcc .noteqi ; abort on inequality
|
2023-09-03 21:41:10 +00:00
|
|
|
+ dex
|
2017-06-24 18:43:16 +00:00
|
|
|
bne -
|
2017-08-30 16:27:10 +00:00
|
|
|
lda #1
|
2017-09-13 15:06:52 +00:00
|
|
|
ldy #0 ; okay, they're equal. Return 1 (not just any char; so that PLASMA when...is can work)
|
2017-06-24 18:43:16 +00:00
|
|
|
rts
|
2021-10-19 14:58:11 +00:00
|
|
|
.null
|
|
|
|
lda #$13 ; inverse 'S'
|
|
|
|
sta $7D0
|
|
|
|
brk
|
2017-06-24 18:43:16 +00:00
|
|
|
end
|
|
|
|
|
2017-06-30 15:11:34 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Multiply 16 bit number by 8-bit ratio, and drop lower 8 bits of result. In effect this scales
|
|
|
|
// num by the approximate ratio 0=0% .. 128=50% .. 255=99%
|
|
|
|
asm mulRatio(num, ratio)#1
|
|
|
|
+asmPlasmRet 2
|
|
|
|
.ratio = evalStkL
|
|
|
|
.numL = evalStkL+1
|
|
|
|
.numH = evalStkH+1
|
|
|
|
.numHH = tmp
|
|
|
|
.accLL = tmp+1
|
|
|
|
.accL = pTmp
|
|
|
|
.accH = pTmp+1
|
|
|
|
lda .ratio,x ; save ratio
|
|
|
|
ldy #0
|
|
|
|
sty .numHH ; zero what will become upper 8 bits of num
|
|
|
|
sty .accLL ; clear accumulation area
|
|
|
|
sty .accL
|
|
|
|
sty .accH
|
|
|
|
ldy #8 ; loop over 8 bits of ratio
|
|
|
|
- lsr ; get next bit
|
|
|
|
bcc + ; skip add if clear
|
|
|
|
pha
|
|
|
|
clc
|
|
|
|
lda .numL,x ; 24-bit add
|
|
|
|
adc .accLL
|
|
|
|
sta .accLL
|
|
|
|
lda .numH,x
|
|
|
|
adc .accL
|
|
|
|
sta .accL
|
|
|
|
lda .numHH
|
|
|
|
adc .accH
|
|
|
|
sta .accH
|
|
|
|
pla
|
|
|
|
+ asl .numL,x ; shift number up
|
|
|
|
rol .numH,x
|
|
|
|
rol .numHH
|
|
|
|
dey ; and loop again
|
|
|
|
bne -
|
|
|
|
ldy .accH ; final result in Y/A
|
|
|
|
lda .accL
|
|
|
|
rts
|
|
|
|
end
|
|
|
|
|
2018-02-14 16:58:22 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Check for script(s) attached to the given location, and establish the array of map scripts.
|
|
|
|
// Does not call any of them -- that's the job of scriptEvent().
|
|
|
|
// Structure of trigger table:
|
|
|
|
// Y (1 byte)
|
|
|
|
// entry length (1 byte, includes Y and this length)
|
|
|
|
// X (1 byte)
|
|
|
|
// script ptr (2 bytes)
|
|
|
|
// X
|
|
|
|
// script ptr...
|
|
|
|
// Y (or $FF for end of table)
|
|
|
|
// ...
|
|
|
|
asm scanScripts(x, y, triggerTbl, mapScripts)#1
|
|
|
|
!zone {
|
|
|
|
.pMapScripts = tmp
|
|
|
|
.pTriggerTbl = pTmp
|
|
|
|
.outOffset = ysav
|
|
|
|
.limit = ysav1
|
|
|
|
.xCoord = evalStkL+3
|
|
|
|
.yCoord = evalStkL+2
|
|
|
|
+asmPlasmRet 4
|
|
|
|
sta .pMapScripts ; pointer to output mapScripts
|
|
|
|
sty .pMapScripts+1
|
|
|
|
lda evalStkL+1,x ; pointer to trigger tbl
|
|
|
|
sta .pTriggerTbl
|
|
|
|
lda evalStkH+1,x
|
|
|
|
sta .pTriggerTbl+1
|
|
|
|
lda #0 ; zero initial output offset
|
|
|
|
sta .outOffset
|
|
|
|
.ylup
|
|
|
|
ldy #0
|
|
|
|
lda (.pTriggerTbl),y
|
|
|
|
cmp #$FF ; Y=$FF marks end of trigger table
|
|
|
|
bne +
|
|
|
|
.done
|
|
|
|
lda .outOffset
|
|
|
|
lsr ; divide offset by 2 to get # of output scripts
|
|
|
|
ldy #0
|
|
|
|
rts ; all done
|
|
|
|
+ iny
|
|
|
|
cmp .yCoord,x ; check for Y coordinate we want
|
|
|
|
beq .goty
|
|
|
|
lda (.pTriggerTbl),y ; not found, add offset to find next Y section
|
2023-10-15 19:44:34 +00:00
|
|
|
bne +
|
|
|
|
brk
|
|
|
|
+ clc
|
2018-02-14 16:58:22 +00:00
|
|
|
adc .pTriggerTbl
|
|
|
|
sta .pTriggerTbl
|
|
|
|
bcc .ylup
|
|
|
|
inc .pTriggerTbl+1
|
|
|
|
bne .ylup ; always taken
|
|
|
|
.goty
|
|
|
|
lda (.pTriggerTbl),y ; get # of bytes in X table
|
|
|
|
sta .limit
|
|
|
|
iny
|
|
|
|
.xlup
|
|
|
|
lda (.pTriggerTbl),y
|
|
|
|
iny
|
|
|
|
cmp .xCoord,x
|
|
|
|
bne .nextx
|
|
|
|
tya
|
|
|
|
pha ; save trigger table pointer
|
|
|
|
iny
|
|
|
|
lda (.pTriggerTbl),y ; hi byte of script
|
|
|
|
pha
|
|
|
|
dey
|
|
|
|
lda (.pTriggerTbl),y ; lo byte of script
|
|
|
|
ldy .outOffset
|
|
|
|
sta (.pMapScripts),y
|
|
|
|
iny
|
|
|
|
pla
|
|
|
|
sta (.pMapScripts),y
|
|
|
|
iny
|
|
|
|
sty .outOffset
|
|
|
|
pla ; back to scanning trigger tbl
|
|
|
|
tay
|
|
|
|
.nextx
|
|
|
|
iny
|
|
|
|
iny
|
|
|
|
cpy .limit ; stop at end of X/ptr list
|
|
|
|
bcs .done
|
|
|
|
bcc .xlup ; always taken
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2020-08-22 17:50:45 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Params: cmd | open<<8, filename, addr, size. Returns: status (for open only)
|
|
|
|
export asm callProRWTS(cmdPlusOpenFlg, filename, addr, size)#1
|
|
|
|
+asmPlasmRet 4
|
2021-03-03 01:41:14 +00:00
|
|
|
sei ; inhibit interrupts during aux operations
|
2020-08-22 17:50:45 +00:00
|
|
|
; Params come to us from PLASMA in reverse order
|
|
|
|
sta setAuxZP
|
|
|
|
sta sizelo
|
|
|
|
sty sizehi
|
|
|
|
|
|
|
|
sta clrAuxZP
|
2022-04-07 15:46:27 +00:00
|
|
|
lda evalStkL+1,x ; addr
|
2020-08-22 17:50:45 +00:00
|
|
|
ldy evalStkH+1,x
|
|
|
|
sta setAuxZP
|
|
|
|
sta ldrlo
|
|
|
|
sty ldrhi
|
|
|
|
|
|
|
|
sta clrAuxZP
|
2022-04-07 15:46:27 +00:00
|
|
|
lda evalStkL+2,x ; filename
|
2020-08-22 17:50:45 +00:00
|
|
|
ldy evalStkH+2,x
|
|
|
|
sta setAuxZP
|
|
|
|
sta namlo
|
|
|
|
sty namhi
|
|
|
|
|
|
|
|
sta clrAuxZP
|
2022-04-07 15:46:27 +00:00
|
|
|
lda evalStkL+3,x ; openFlg
|
|
|
|
ldy evalStkH+3,x ; cmd
|
2020-08-22 17:50:45 +00:00
|
|
|
sta setAuxZP
|
|
|
|
sta reqcmd
|
|
|
|
lda #0
|
|
|
|
sta auxreq
|
|
|
|
|
|
|
|
bit setLcRW+lcBank1
|
|
|
|
bit setLcRW+lcBank1
|
2022-01-14 16:22:26 +00:00
|
|
|
lda #$4C ; set up JMP to the requested proRWTS vector
|
|
|
|
sta $44
|
|
|
|
sty $45
|
|
|
|
lda #>proRWTS
|
|
|
|
sta $46
|
|
|
|
jsr $44
|
|
|
|
lda status
|
2020-08-22 17:50:45 +00:00
|
|
|
ldy #0
|
|
|
|
bit setLcRW+lcBank2
|
|
|
|
bit setLcRW+lcBank2
|
|
|
|
sta clrAuxZP
|
2021-03-03 01:41:14 +00:00
|
|
|
cli ; interrupts ok again
|
2020-08-22 17:50:45 +00:00
|
|
|
rts
|
|
|
|
end
|
|
|
|
|
2018-02-14 16:58:22 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// The following only used for speed testing
|
2018-11-15 17:00:20 +00:00
|
|
|
//asm readNoSlotClock(dstBuf)#0 // param: dstBuf (will receive 8 BCD bytes)
|
|
|
|
//!zone {
|
|
|
|
// +asmPlasmNoRet 1
|
|
|
|
// ; record dst ptr
|
|
|
|
// sta tmp
|
|
|
|
// sty tmp+1
|
|
|
|
// ; obtain a pointer to our little table of magic values
|
|
|
|
// lda #$60
|
|
|
|
// sta pTmp
|
|
|
|
//.base = *+2
|
|
|
|
// jsr pTmp
|
|
|
|
// tsx
|
|
|
|
// dex
|
|
|
|
// dex
|
|
|
|
// txs
|
|
|
|
// pla
|
|
|
|
// sta pTmp
|
|
|
|
// pla
|
|
|
|
// sta pTmp+1
|
|
|
|
// ldy #<(.tbl - .base)
|
|
|
|
// ; record state of slot ROM, then turn on C3 ROM
|
|
|
|
// sei
|
|
|
|
// lda $CFFF
|
|
|
|
// pha
|
|
|
|
// sta $C300
|
|
|
|
// lda $C304
|
|
|
|
// ldx #8
|
|
|
|
//.wr1:
|
|
|
|
// lda (pTmp),y
|
|
|
|
// sec
|
|
|
|
// ror
|
|
|
|
//.wr2:
|
|
|
|
// bcs +
|
|
|
|
// bit $C300
|
|
|
|
// bcc ++
|
|
|
|
//+ bit $C301
|
|
|
|
//++ lsr
|
|
|
|
// bne .wr2
|
|
|
|
// iny
|
|
|
|
// dex
|
|
|
|
// bne .wr1
|
|
|
|
// ldy #7
|
|
|
|
//.rd1:
|
|
|
|
// ldx #8
|
|
|
|
//.rd2:
|
|
|
|
// lda $C304
|
|
|
|
// lsr
|
|
|
|
// ror pTmp
|
|
|
|
// dex
|
|
|
|
// bne .rd2
|
|
|
|
// lda pTmp
|
|
|
|
// sta (tmp),y
|
|
|
|
// dey
|
|
|
|
// bpl .rd1
|
|
|
|
// ; restore slot ROM state
|
|
|
|
// pla
|
|
|
|
// bmi +
|
|
|
|
// sta $CFFF
|
|
|
|
//+ cli
|
|
|
|
// rts
|
|
|
|
//.tbl !byte $C5,$3A,$A3,$5C,$C5,$3A,$A3,$5C
|
|
|
|
//} ; end zone
|
|
|
|
//end
|
|
|
|
//
|
|
|
|
//def getTick()
|
|
|
|
// byte timeBuf[8]
|
|
|
|
// word tick
|
|
|
|
// readNoSlotClock(@timeBuf)
|
|
|
|
// tick = (timeBuf[7] & $F) + ((timeBuf[7] >> 4) * 10)
|
|
|
|
// tick = tick + ((timeBuf[6] & $F) * 100) + ((timeBuf[6] >> 4) * 1000)
|
|
|
|
// return tick + (((timeBuf[5] & $F) % 5) * 6000)
|
|
|
|
//end
|
|
|
|
//
|
|
|
|
//def tickDiff(tStart, tEnd)
|
|
|
|
// word diff
|
|
|
|
// diff = tEnd - tStart
|
|
|
|
// if diff >= 0; return diff; fin
|
|
|
|
// return diff + 30000
|
|
|
|
//end
|
|
|
|
//
|
|
|
|
//def prtick(str)#0
|
|
|
|
// if lastTick
|
|
|
|
// printf2("%s:%d ", str, tickDiff(lastTick, getTick()))
|
|
|
|
// fin
|
|
|
|
// lastTick = getTick()
|
|
|
|
//end
|
2018-02-26 14:45:15 +00:00
|
|
|
|
2014-08-21 15:17:50 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// General methods
|
2014-07-06 23:45:27 +00:00
|
|
|
|
2021-10-12 15:45:49 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Generate a sound that indicates being blocked or failing
|
|
|
|
export def beep()#0
|
|
|
|
genSound(0, 600, 60200, 0, 0, 60200, 30)
|
|
|
|
end
|
|
|
|
|
2019-04-24 13:33:18 +00:00
|
|
|
export def rand16()#1
|
|
|
|
word result
|
|
|
|
if recordMode
|
|
|
|
*seed = recordSeed
|
2019-04-24 16:22:44 +00:00
|
|
|
//printf1("%x:", recordSeed)
|
2019-04-24 13:33:18 +00:00
|
|
|
result = internal_rand16()
|
|
|
|
recordSeed = *seed
|
2019-04-24 16:22:44 +00:00
|
|
|
//printf1("%x ", recordSeed)
|
2019-04-24 13:33:18 +00:00
|
|
|
return result
|
|
|
|
fin
|
|
|
|
return internal_rand16()
|
|
|
|
end
|
|
|
|
|
2015-01-10 21:17:56 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Fatal error: print message and stop the system.
|
2017-06-10 15:56:23 +00:00
|
|
|
// Doesn't really return a value, but can be handy for chaining.
|
|
|
|
export def fatal(msg)#1
|
|
|
|
return mmgr(FATAL_ERROR, msg)
|
2014-07-19 22:55:27 +00:00
|
|
|
end
|
|
|
|
|
2015-12-05 23:01:44 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2015-12-17 14:48:30 +00:00
|
|
|
// Return the max of two signed 16-bit numbers
|
2017-06-10 15:56:23 +00:00
|
|
|
export def max(a, b)#1
|
2018-11-15 17:00:20 +00:00
|
|
|
return a > b ?? a :: b
|
2015-12-05 23:01:44 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2015-12-17 14:48:30 +00:00
|
|
|
// Return the min of two signed 16-bit numbers
|
2017-06-10 15:56:23 +00:00
|
|
|
export def min(a, b)#1
|
2018-11-15 17:00:20 +00:00
|
|
|
return a < b ?? a :: b
|
2015-12-05 23:01:44 +00:00
|
|
|
end
|
|
|
|
|
2016-03-29 22:17:42 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Return the absolute value of a number
|
2017-07-07 17:18:43 +00:00
|
|
|
export def abs(n)#1
|
2018-11-15 17:00:20 +00:00
|
|
|
return n < 0 ?? -n :: n
|
2016-03-29 22:17:42 +00:00
|
|
|
end
|
|
|
|
|
2017-05-24 14:47:47 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Convert a lower-case character to upper-case (or return unchanged if it's not lower-case)
|
2017-06-10 15:56:23 +00:00
|
|
|
export def charToUpper(c)#1
|
2018-11-15 17:00:20 +00:00
|
|
|
return (c >= 'a' and c <= 'z') ?? (c - $20) :: c
|
2017-05-24 14:47:47 +00:00
|
|
|
end
|
|
|
|
|
2019-05-02 15:06:36 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
def recordChar(ch)#0
|
|
|
|
if ch < $20
|
|
|
|
printChar('^')
|
|
|
|
ch = ch + $40
|
|
|
|
fin
|
|
|
|
printChar(ch)
|
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
def recordDisplay(str)#0
|
|
|
|
word i
|
|
|
|
puts("DISP:")
|
|
|
|
for i = 1 to ^str
|
|
|
|
recordChar(^(str + i) & $7F)
|
|
|
|
next
|
|
|
|
crout
|
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
export def displayStr(str)#0
|
|
|
|
if recordMode; recordDisplay(str); fin
|
|
|
|
internalDisplayStr(str)
|
|
|
|
end
|
|
|
|
|
2016-03-22 15:22:26 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Read a string from the keyboard using the font manager, and intern it to the heap.
|
2017-06-10 15:56:23 +00:00
|
|
|
export def getStringResponse()#1
|
2016-03-22 15:22:26 +00:00
|
|
|
word p
|
|
|
|
rawGetStr()
|
|
|
|
rawDisplayStr("\n") // so Outlaw user doesn't have to remember to make a newline
|
2019-04-30 13:35:41 +00:00
|
|
|
if recordMode
|
|
|
|
puts("STRING:"); puts($200); crout
|
|
|
|
fin
|
2016-03-22 15:22:26 +00:00
|
|
|
return mmgr(HEAP_INTERN, $200)
|
|
|
|
end
|
|
|
|
|
2015-03-25 14:40:46 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-01-15 17:10:43 +00:00
|
|
|
// Convert signed decimal to string in decimalBuf (@decimalBuf returned)
|
2017-06-10 15:56:23 +00:00
|
|
|
def convertDec(n)#1
|
2015-05-03 16:25:57 +00:00
|
|
|
word n0
|
2015-11-05 17:09:45 +00:00
|
|
|
word p
|
|
|
|
p = @decimalBuf + 1
|
|
|
|
if n < 0; ^p = '-'; p=p+1; n = -n; fin
|
2015-05-03 16:25:57 +00:00
|
|
|
n0 = n
|
2015-11-05 17:09:45 +00:00
|
|
|
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
|
2015-03-25 14:40:46 +00:00
|
|
|
end
|
|
|
|
|
2017-03-25 14:23:10 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Convert byte to 3-char string in decimalBuf, suitable for font engine, e.g. ^T065
|
2017-04-02 16:25:11 +00:00
|
|
|
// (@fontPosBuf returned so as to not disturb decimalBuf)
|
2017-06-10 15:56:23 +00:00
|
|
|
def convert3Dec(n)#1
|
2017-04-02 16:25:11 +00:00
|
|
|
fontPosBuf[0] = 3
|
|
|
|
fontPosBuf[1] = '0' + (n / 100); n = n%100
|
|
|
|
fontPosBuf[2] = '0' + (n / 10); n = n%10
|
|
|
|
fontPosBuf[3] = '0' + n
|
|
|
|
return @fontPosBuf
|
2017-03-25 14:23:10 +00:00
|
|
|
end
|
|
|
|
|
2015-03-05 17:34:42 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2015-12-29 16:59:13 +00:00
|
|
|
// Print a formatted string a'la C printf, with up to three parameters.
|
2017-06-09 14:14:53 +00:00
|
|
|
export def printf3(str, arg1, arg2, arg3)#0
|
2015-03-05 17:34:42 +00:00
|
|
|
word pos
|
|
|
|
word curArg
|
|
|
|
word p
|
2017-06-02 15:25:29 +00:00
|
|
|
if !str
|
|
|
|
printChar('!') // Safety valve for NULL string pointer
|
|
|
|
return
|
|
|
|
fin
|
2015-03-05 17:34:42 +00:00
|
|
|
pos = 0
|
|
|
|
curArg = @arg1
|
|
|
|
while TRUE
|
|
|
|
pos = partialPrintf(str, pos)
|
|
|
|
if pos < 0
|
|
|
|
break
|
|
|
|
fin
|
|
|
|
p = str + pos + 2
|
|
|
|
when ^p
|
2017-03-25 14:23:10 +00:00
|
|
|
is 'd' // %d = decimal
|
2015-11-05 17:09:45 +00:00
|
|
|
puts(convertDec(*curArg)); break
|
2017-03-25 14:23:10 +00:00
|
|
|
is 's' // %s = string
|
2015-03-25 14:40:46 +00:00
|
|
|
puts(*curArg); break
|
2017-03-25 14:23:10 +00:00
|
|
|
is 'D' // %D = 3-char decimal suitable for font engine ctrl codes
|
|
|
|
puts(convert3Dec(*curArg)); break
|
|
|
|
is 'c' // %c = character
|
|
|
|
printChar(*curArg); break
|
|
|
|
is 'x' // %x = hex with '$'
|
2015-09-02 16:55:21 +00:00
|
|
|
printHex(*curArg); break
|
2017-03-25 14:23:10 +00:00
|
|
|
is '%' // %% = perfect
|
2015-03-25 14:40:46 +00:00
|
|
|
printChar('%'); break
|
2015-03-05 17:34:42 +00:00
|
|
|
otherwise
|
2015-03-25 14:40:46 +00:00
|
|
|
printHex(^p); fatal("Unknown % code")
|
2015-03-05 17:34:42 +00:00
|
|
|
wend
|
|
|
|
curArg = curArg + 2
|
|
|
|
pos = pos + 2
|
|
|
|
loop
|
|
|
|
end
|
|
|
|
|
2017-06-09 14:14:53 +00:00
|
|
|
export def printf1(str, arg1)#0; printf3(str, arg1, 0, 0); end
|
|
|
|
export def printf2(str, arg1, arg2)#0; printf3(str, arg1, arg2, 0); end
|
2015-03-25 14:40:46 +00:00
|
|
|
|
2015-11-11 16:55:16 +00:00
|
|
|
// Like printf, but displays text using font engine
|
2017-06-09 14:14:53 +00:00
|
|
|
export def displayf3(str, arg1, arg2, arg3)#0
|
2019-04-12 14:21:57 +00:00
|
|
|
buildString()
|
2015-12-29 16:59:13 +00:00
|
|
|
printf3(str, arg1, arg2, arg3)
|
2015-11-19 16:27:04 +00:00
|
|
|
displayStr(finishString(isPlural))
|
2015-11-11 16:55:16 +00:00
|
|
|
end
|
|
|
|
|
2017-06-09 14:14:53 +00:00
|
|
|
export def displayf1(str, arg1)#0; displayf3(str, arg1, 0, 0); end
|
|
|
|
export def displayf2(str, arg1, arg2)#0; displayf3(str, arg1, arg2, 0); end
|
2015-11-11 16:55:16 +00:00
|
|
|
|
2017-05-24 14:47:47 +00:00
|
|
|
// Like printf, but buffers string in $200
|
2017-06-10 15:56:23 +00:00
|
|
|
export def sprintf3(str, arg1, arg2, arg3)#1
|
2019-04-12 14:21:57 +00:00
|
|
|
buildString()
|
2017-03-23 14:07:36 +00:00
|
|
|
printf3(str, arg1, arg2, arg3)
|
2017-05-24 14:47:47 +00:00
|
|
|
return finishString(isPlural)
|
2017-03-23 14:07:36 +00:00
|
|
|
end
|
|
|
|
|
2017-05-24 14:47:47 +00:00
|
|
|
export def sprintf1(str, arg1); return sprintf3(str, arg1, 0, 0); end
|
|
|
|
export def sprintf2(str, arg1, arg2); return sprintf3(str, arg1, arg2, 0); end
|
|
|
|
|
|
|
|
// Like printf, but displays text using font engine
|
2017-06-09 14:14:53 +00:00
|
|
|
export def rawDisplayf1(str, arg1)#0; rawDisplayStr(sprintf3(str, arg1, 0, 0)); end
|
|
|
|
export def rawDisplayf2(str, arg1, arg2)#0; rawDisplayStr(sprintf3(str, arg1, arg2, 0)); end
|
|
|
|
export def rawDisplayf3(str, arg1, arg2, arg3)#0; rawDisplayStr(sprintf3(str, arg1, arg2, arg3)); end
|
2017-03-23 14:07:36 +00:00
|
|
|
|
2017-07-08 23:36:08 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Set the cursor position in the font engine
|
|
|
|
export def setCursor(x, y)#0
|
|
|
|
rawDisplayf2("^V%D^T%D", y, x)
|
|
|
|
end
|
|
|
|
|
2015-05-03 16:25:57 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
export def parseDec(str)#1
|
2015-05-03 16:25:57 +00:00
|
|
|
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
|
|
|
|
|
2019-04-30 13:35:41 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Used in record mode to print out a keystroke. Also handles caret -> ctrl translation during
|
|
|
|
// playback of recordings.
|
|
|
|
export def recordKey()#1
|
|
|
|
byte key
|
|
|
|
key = ^kbd & $7F
|
|
|
|
^kbdStrobe
|
2019-05-02 16:24:19 +00:00
|
|
|
if recordMode
|
2019-04-30 13:35:41 +00:00
|
|
|
if key == '^'
|
|
|
|
while ^kbd < 128; loop
|
|
|
|
key = (^kbd & $7F) - $40
|
|
|
|
^kbdStrobe
|
|
|
|
fin
|
|
|
|
puts("KEY:")
|
|
|
|
recordChar(key)
|
2019-05-02 15:06:36 +00:00
|
|
|
puts(" SEED:$")
|
|
|
|
printHex(recordSeed)
|
2019-04-30 13:35:41 +00:00
|
|
|
crout
|
|
|
|
fin
|
|
|
|
return key
|
|
|
|
end
|
|
|
|
|
2015-01-10 21:17:56 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Get a keystroke and convert it to upper case
|
2017-06-10 15:56:23 +00:00
|
|
|
export def getUpperKey()#1
|
2014-07-22 01:28:39 +00:00
|
|
|
byte key
|
2018-01-17 16:50:29 +00:00
|
|
|
|
|
|
|
// Make sure same text displays on both hi-res while animating
|
|
|
|
if anyAnims and textDrawn and mapIs3D and texturesLoaded
|
|
|
|
copyWindow(0)
|
|
|
|
fin
|
|
|
|
|
|
|
|
// Now wait for a key, and animate while doing so.
|
2015-12-17 17:19:49 +00:00
|
|
|
while ^kbd < 128
|
2019-06-13 17:08:20 +00:00
|
|
|
if recordMode
|
|
|
|
pause(30000)
|
|
|
|
else
|
|
|
|
// pause() will terminate on keypress, returning the count it did
|
|
|
|
*seed = *seed + pause(30000)
|
|
|
|
fin
|
2015-12-17 17:19:49 +00:00
|
|
|
loop
|
2019-04-30 13:35:41 +00:00
|
|
|
key = recordKey
|
|
|
|
return charToUpper(key)
|
2014-07-22 01:28:39 +00:00
|
|
|
end
|
|
|
|
|
2016-12-13 17:07:36 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Read a single char from the keyboard, and intern it (as a string) to the heap.
|
2017-06-10 15:56:23 +00:00
|
|
|
export def getCharResponse()#1
|
2017-02-24 16:49:12 +00:00
|
|
|
if needShowParty; showParty(); fin
|
2016-12-13 17:07:36 +00:00
|
|
|
^$200 = 1
|
|
|
|
^$201 = getUpperKey()
|
|
|
|
return mmgr(HEAP_INTERN, $200)
|
|
|
|
end
|
|
|
|
|
2017-09-13 15:06:52 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2019-12-19 17:38:14 +00:00
|
|
|
// Select the num'th entry in the list for which the selector returns TRUE. A NULL selector
|
|
|
|
// is considered to always be TRUE.
|
2017-09-13 15:06:52 +00:00
|
|
|
export def select(p, sel, num)#1
|
|
|
|
while p
|
2019-11-14 17:36:39 +00:00
|
|
|
if !sel
|
|
|
|
if !num; break; fin
|
|
|
|
num--
|
|
|
|
elsif sel(p)
|
2017-09-13 15:06:52 +00:00
|
|
|
if !num; break; fin
|
|
|
|
num--
|
|
|
|
fin
|
|
|
|
p = p=>p_nextObj
|
|
|
|
loop
|
|
|
|
return p
|
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
export def first(p, sel)#1
|
|
|
|
return select(p, sel, 0)
|
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
export def index(p, num)#1
|
2019-11-14 17:36:39 +00:00
|
|
|
return select(p, NULL, num)
|
2017-09-13 15:06:52 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
export def sum(p, sel, func)#1
|
|
|
|
word sum
|
|
|
|
sum = 0
|
|
|
|
while p
|
2019-11-14 17:36:39 +00:00
|
|
|
if !sel
|
|
|
|
sum = sum + func(p)
|
|
|
|
elsif sel(p)
|
|
|
|
sum = sum + func(p)
|
|
|
|
fin
|
2017-09-13 15:06:52 +00:00
|
|
|
p = p=>p_nextObj
|
|
|
|
loop
|
|
|
|
return sum
|
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
export def forSome(p, sel, do)#0
|
|
|
|
while p
|
2019-11-14 17:36:39 +00:00
|
|
|
if !sel
|
|
|
|
do(p)#0
|
|
|
|
elsif sel(p)
|
|
|
|
do(p)#0
|
|
|
|
fin
|
2017-09-13 15:06:52 +00:00
|
|
|
p = p=>p_nextObj
|
|
|
|
loop
|
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
export def forEach(p, do)#0
|
2019-11-14 17:36:39 +00:00
|
|
|
forSome(p, NULL, do)
|
2017-09-13 15:06:52 +00:00
|
|
|
end
|
|
|
|
|
2018-02-21 17:54:30 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Pause for a specified count period, advancing the animation periodically. Terminates early
|
|
|
|
// if a key is pressed. In either case, returns the number of counts waited.
|
|
|
|
export def pause(count)#1
|
2019-05-02 15:06:36 +00:00
|
|
|
word i, prevSeed
|
|
|
|
if recordMode; prevSeed = recordSeed; fin
|
2018-02-21 17:54:30 +00:00
|
|
|
for i = 0 to count
|
|
|
|
if ^kbd >= 128; break; fin
|
|
|
|
animPauseCt--
|
2016-04-28 15:20:28 +00:00
|
|
|
if animPauseCt < 0
|
2023-10-15 19:44:34 +00:00
|
|
|
nextAnimFrame() // also resets animPauseCt
|
2016-04-28 15:20:28 +00:00
|
|
|
fin
|
2018-02-21 17:54:30 +00:00
|
|
|
next
|
2019-05-02 15:06:36 +00:00
|
|
|
if recordMode; recordSeed = prevSeed; fin
|
2018-02-21 17:54:30 +00:00
|
|
|
return i
|
2016-04-28 15:20:28 +00:00
|
|
|
end
|
|
|
|
|
2017-06-30 15:11:34 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Calculate markup on a price, where ratio is an 8.8 fixed-point number. Some approx ratios:
|
|
|
|
// $0000 = 0%
|
|
|
|
// $0026 = 15%
|
|
|
|
// $0080 = 50%
|
|
|
|
// $0100 = 100%
|
|
|
|
// $0180 = 150%
|
|
|
|
export def addRatio(start, ratio)#1
|
|
|
|
word markup
|
|
|
|
markup = 0
|
|
|
|
while ratio > 255
|
|
|
|
markup = markup + start
|
|
|
|
ratio = ratio - 256
|
|
|
|
loop
|
|
|
|
return markup + mulRatio(start, ratio)
|
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
export def percentToRatio(pct)#1
|
|
|
|
return addRatio(pct, 656) // Scale 0..100 to 0..255
|
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
export def addPercent(start, pct)#1
|
|
|
|
return addRatio(start, percentToRatio(pct))
|
|
|
|
end
|
|
|
|
|
2015-12-09 18:16:05 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
export def encodeDice(nDice, dieSize, add)#1 // ndice=0..15, dieSize=0..15, add=0..255
|
2015-12-09 18:16:05 +00:00
|
|
|
return (nDice << 12) | (dieSize << 8) | add
|
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-09-20 15:44:35 +00:00
|
|
|
export def rollDice(encoded)#1
|
|
|
|
byte nDice, dieSize
|
|
|
|
word result
|
2017-08-30 17:33:01 +00:00
|
|
|
nDice = (encoded >> 12) & $F // must mask off replicated hi-bits
|
2015-12-09 18:16:05 +00:00
|
|
|
dieSize = (encoded >> 8) & $F
|
2019-10-10 16:48:21 +00:00
|
|
|
result = encoded & $FF // initial add
|
2017-09-20 15:44:35 +00:00
|
|
|
while nDice
|
|
|
|
result = result + (rand16() % dieSize) + 1
|
|
|
|
nDice--
|
|
|
|
loop
|
2017-07-15 18:41:29 +00:00
|
|
|
return result
|
|
|
|
end
|
|
|
|
|
2017-02-06 18:01:00 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Look up the partition for a resource.
|
2019-02-21 17:23:34 +00:00
|
|
|
// sectioNum: 0=version num (do not use here), 1=map2d, 2=map3d, 3=portrait, 4=story
|
2017-08-12 17:25:24 +00:00
|
|
|
export def lookupResourcePart(sectionNum, resourceNum)#1
|
2017-02-06 18:01:00 +00:00
|
|
|
word ptr
|
2018-04-28 16:38:24 +00:00
|
|
|
byte n, i
|
2017-02-06 18:01:00 +00:00
|
|
|
|
2021-10-10 16:30:40 +00:00
|
|
|
// Special case: main frame img laods before resource index
|
|
|
|
if !pResourceIndex; return 1; fin
|
|
|
|
|
2017-08-11 18:17:38 +00:00
|
|
|
// Skip to the requested section (starting just after version num)
|
2017-02-06 18:01:00 +00:00
|
|
|
ptr = pResourceIndex
|
2017-08-11 18:17:38 +00:00
|
|
|
while sectionNum > 0
|
2017-02-06 18:01:00 +00:00
|
|
|
ptr = ptr + readAuxByte(ptr) + 1
|
|
|
|
sectionNum--
|
|
|
|
loop
|
|
|
|
|
|
|
|
// And grab the number from that section's table
|
|
|
|
n = readAuxByte(ptr)
|
2021-10-10 16:30:40 +00:00
|
|
|
if resourceNum > n; fatal("lkup1"); fin
|
2017-02-06 18:01:00 +00:00
|
|
|
n = readAuxByte(ptr + resourceNum)
|
2018-04-28 16:38:24 +00:00
|
|
|
|
|
|
|
// If resource is on the current map's disk, prefer that
|
|
|
|
if n & (1<<(curMapPartition-1))
|
|
|
|
return curMapPartition
|
2017-04-10 15:35:27 +00:00
|
|
|
fin
|
2018-04-28 16:38:24 +00:00
|
|
|
|
|
|
|
// Otherwise return the first disk it's on
|
2018-09-29 16:46:19 +00:00
|
|
|
for i = 0 to 7
|
2018-04-28 16:38:24 +00:00
|
|
|
if n & (1<<i); return i+1; fin
|
|
|
|
next
|
|
|
|
return 0
|
2017-02-06 18:01:00 +00:00
|
|
|
end
|
|
|
|
|
2015-01-10 21:17:56 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Set the sky color (relevant to 3D display only)
|
2017-06-10 14:21:58 +00:00
|
|
|
export def setSky(num)#0
|
2018-01-10 16:47:20 +00:00
|
|
|
if num <> skyNum
|
|
|
|
skyNum = num
|
|
|
|
setColor(0, skyNum)
|
|
|
|
needRender = TRUE
|
2019-07-15 17:21:12 +00:00
|
|
|
showingLamp = mapIs3D and texturesLoaded and (skyNum == 0 or skyNum == 8)
|
|
|
|
if showingLamp and util3d; util3d=>util3d_nextLampFrame(); fin // to update image
|
2018-01-10 16:47:20 +00:00
|
|
|
fin
|
2014-07-23 19:50:16 +00:00
|
|
|
end
|
|
|
|
|
2015-01-10 21:17:56 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Set the ground color (relevant to 3D display only)
|
2017-06-10 14:21:58 +00:00
|
|
|
export def setGround(num)#0
|
2018-01-10 16:47:20 +00:00
|
|
|
if num <> groundNum
|
|
|
|
groundNum = num
|
|
|
|
setColor(1, groundNum)
|
|
|
|
needRender = TRUE
|
|
|
|
fin
|
2014-07-23 19:50:16 +00:00
|
|
|
end
|
|
|
|
|
2017-09-11 18:16:10 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
export def heapCollect()#0
|
|
|
|
word pct
|
|
|
|
mmgr(CHECK_MEM, 0)
|
|
|
|
global=>w_heapSize = mmgr(HEAP_COLLECT, 0) - HEAP_BOTTOM
|
2020-06-15 14:21:37 +00:00
|
|
|
curHeapPct = min(99, max(0, ((global=>w_heapSize / 10) * 100) / (HEAP_SIZE / 10)))
|
2017-08-11 18:17:38 +00:00
|
|
|
end
|
|
|
|
|
2018-02-21 17:54:30 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
def resetAnimPause()#0
|
2019-01-24 16:52:02 +00:00
|
|
|
anyAnims = TRUE // for now; might get cleared if we discover otherwise on advance
|
|
|
|
animDirCt = 1
|
2018-02-21 17:54:30 +00:00
|
|
|
animPauseCt = ANIM_PAUSE_MAX
|
2019-07-15 17:21:12 +00:00
|
|
|
showingLamp = mapIs3D and texturesLoaded and (skyNum == 0 or skyNum == 8)
|
2018-02-21 17:54:30 +00:00
|
|
|
lampFrame = 0
|
|
|
|
lampDir = 1
|
|
|
|
end
|
|
|
|
|
2015-01-10 21:17:56 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Load the Frame Image, and lock it.
|
2017-06-10 15:56:23 +00:00
|
|
|
export def loadFrameImg(img)#0
|
2016-10-11 16:03:57 +00:00
|
|
|
// Skip redundant reload
|
|
|
|
if frameLoaded == img; return; fin
|
|
|
|
|
2016-10-23 17:42:11 +00:00
|
|
|
// Free prev img and/or portrait (if any)
|
2021-09-03 15:46:24 +00:00
|
|
|
if curFullscreenImg
|
|
|
|
// must clear img directly, to avoid loading main frame img only to replace it
|
|
|
|
auxMmgr(FREE_MEMORY, curFullscreenImg)
|
|
|
|
curFullscreenImg = NULL
|
|
|
|
fin
|
2016-10-23 17:42:11 +00:00
|
|
|
clearPortrait()
|
|
|
|
|
2016-10-24 14:08:37 +00:00
|
|
|
// Make room in aux mem by throwing out textures
|
2019-07-15 17:21:12 +00:00
|
|
|
textureControl(FALSE)
|
2016-10-24 14:08:37 +00:00
|
|
|
|
2016-10-23 17:42:11 +00:00
|
|
|
// Load the image data into aux mem
|
2016-11-07 15:58:46 +00:00
|
|
|
if img
|
2021-09-03 15:46:24 +00:00
|
|
|
auxMmgr(START_LOAD, lookupResourcePart(4, img))
|
2017-01-12 17:08:05 +00:00
|
|
|
if img == 1
|
|
|
|
auxMmgr(SET_MEM_TARGET, $4000) // well above where expander loads at startup
|
|
|
|
fin
|
2016-11-07 15:58:46 +00:00
|
|
|
curFullscreenImg = auxMmgr(QUEUE_LOAD, img<<8 | RES_TYPE_SCREEN)
|
|
|
|
auxMmgr(FINISH_LOAD, 0)
|
2016-10-23 17:42:11 +00:00
|
|
|
|
2018-02-21 17:54:30 +00:00
|
|
|
resetAnimPause()
|
2016-10-11 16:03:57 +00:00
|
|
|
|
2016-11-07 15:58:46 +00:00
|
|
|
// And show the first frame of the screen image
|
2018-03-17 15:52:29 +00:00
|
|
|
^EMUSIG_FULL_COLOR
|
2016-11-07 15:58:46 +00:00
|
|
|
showAnimFrame()
|
|
|
|
else
|
|
|
|
curFullscreenImg = NULL
|
|
|
|
anyAnims = FALSE
|
|
|
|
fin
|
|
|
|
|
|
|
|
frameLoaded = img
|
2016-10-11 16:03:57 +00:00
|
|
|
|
2016-10-23 17:42:11 +00:00
|
|
|
// Do not render over the image
|
|
|
|
needRender = FALSE
|
2015-01-10 21:17:56 +00:00
|
|
|
end
|
|
|
|
|
2015-06-01 17:38:22 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Window for the map name bar
|
2017-06-10 14:32:31 +00:00
|
|
|
export def setWindow1()#0
|
2017-04-10 17:01:36 +00:00
|
|
|
setWindow(8, 17, 35, 119) // Top, Bottom, Left, Right
|
2016-06-09 14:39:09 +00:00
|
|
|
mapNameHash = 0 // on the assumption that it's being set because somebody's going to print there
|
2015-06-01 17:38:22 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Window for the large upper right bar
|
2017-06-10 14:32:31 +00:00
|
|
|
export def setWindow2()#0
|
2016-07-11 00:33:48 +00:00
|
|
|
setWindow(24, 132, 154, 267) // Top, Bottom, Left, Right
|
2017-07-08 23:36:08 +00:00
|
|
|
displayChar('N'-$40) // Set normal mode - clear all special modes (like underline, etc.)
|
2015-06-01 17:38:22 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Window for the mid-size lower right bar
|
2017-06-10 14:32:31 +00:00
|
|
|
export def setWindow3()#0
|
2017-09-03 16:28:46 +00:00
|
|
|
setWindow(144, 181, 154, 267) // Top, Bottom, Left, Right
|
2015-06-01 17:38:22 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-09-09 14:56:37 +00:00
|
|
|
// Clear map window, then set a text area within it.
|
|
|
|
export def useMapWindow()#0
|
2017-09-20 15:44:35 +00:00
|
|
|
byte bottom1, bottom2
|
2016-06-29 14:42:58 +00:00
|
|
|
if frameLoaded == 3 // don't check mapIs3D, since we might be in an engine
|
2017-09-20 15:44:35 +00:00
|
|
|
bottom1 = 154; bottom2 = 150
|
2015-06-01 17:38:22 +00:00
|
|
|
else
|
2017-09-20 15:44:35 +00:00
|
|
|
bottom1 = 169; bottom2 = 168
|
2015-06-01 17:38:22 +00:00
|
|
|
fin
|
2017-09-20 15:44:35 +00:00
|
|
|
setWindow(24, bottom1, 14, 140) // Top, Bottom, Left, Right
|
|
|
|
clearWindow()
|
|
|
|
setWindow(24, bottom2, 14, 140) // Top, Bottom, Left, Right
|
2015-06-01 17:38:22 +00:00
|
|
|
end
|
2016-11-07 15:58:46 +00:00
|
|
|
|
2017-08-02 17:20:22 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
def clearTextWindow()#0
|
2017-09-12 15:21:23 +00:00
|
|
|
if textDrawn or textClearCountdown
|
|
|
|
setWindow2(); clearWindow()
|
2018-01-05 17:17:01 +00:00
|
|
|
if mapIs3D and texturesLoaded; copyWindow(0); fin
|
2017-09-12 15:21:23 +00:00
|
|
|
textDrawn = FALSE
|
|
|
|
textClearCountdown = 0
|
2018-01-10 15:10:43 +00:00
|
|
|
snoozeX1 = -1
|
2017-09-12 15:21:23 +00:00
|
|
|
fin
|
2017-08-02 17:20:22 +00:00
|
|
|
end
|
|
|
|
|
2021-04-22 15:52:57 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
export def soundPlayEmu(numAndFlgs)#0
|
2021-09-07 16:08:28 +00:00
|
|
|
if mapEmuSound == $FF // if in initMap...
|
|
|
|
mapEmuSound = numAndFlgs // ... then record the music for resume after combat
|
|
|
|
fin
|
2021-04-22 15:52:57 +00:00
|
|
|
^EMUSOUND_PLAY = numAndFlgs
|
|
|
|
end
|
|
|
|
|
2017-03-22 16:46:17 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
def hline(addr, startByte, midByte, midSize, endByte)#0
|
2017-03-22 16:46:17 +00:00
|
|
|
^addr = startByte
|
|
|
|
memset(addr+1, midByte, midSize)
|
|
|
|
^(addr+midSize+1) = endByte
|
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Window that covers the entire inner area (destroys frame image, so be sure to loadMainFrame
|
|
|
|
// afterwards)
|
2017-06-10 15:56:23 +00:00
|
|
|
export def setBigWindow()#0
|
2017-05-24 14:47:47 +00:00
|
|
|
// Draw border (if not already drawn)
|
|
|
|
if frameLoaded
|
2017-05-25 14:50:51 +00:00
|
|
|
hline(getScreenLine(BIGWIN_TOP-4)+1, 0, 0, 36, 0)
|
|
|
|
hline(getScreenLine(BIGWIN_TOP-3)+1, $F8, $FF, 36, $8F)
|
|
|
|
hline(getScreenLine(BIGWIN_TOP-2)+1, $FC, $FF, 36, $9F)
|
|
|
|
hline(getScreenLine(BIGWIN_TOP-1)+1, $8C, 0, 36, $98)
|
|
|
|
vline(getScreenLine(BIGWIN_TOP)+1, $8C, 174)
|
|
|
|
vline(getScreenLine(BIGWIN_TOP)+38, $98, 174)
|
|
|
|
hline(getScreenLine(BIGWIN_BOTTOM-1)+1, $8C, 0, 36, $98)
|
|
|
|
hline(getScreenLine(BIGWIN_BOTTOM)+1, $FC, $FF, 36, $9F)
|
|
|
|
hline(getScreenLine(BIGWIN_BOTTOM+1)+1, $F8, $FF, 36, $8F)
|
|
|
|
hline(getScreenLine(BIGWIN_BOTTOM+2)+1, 0, 0, 36, 0)
|
2017-05-24 14:47:47 +00:00
|
|
|
frameLoaded = 0 // since we just destroyed it
|
2017-09-14 16:53:01 +00:00
|
|
|
textDrawn = FALSE
|
|
|
|
textClearCountdown = 0
|
2018-03-17 15:52:29 +00:00
|
|
|
^EMUSIG_FULL_TEXT
|
2017-05-24 14:47:47 +00:00
|
|
|
fin
|
2017-05-25 14:50:51 +00:00
|
|
|
setWindow(BIGWIN_TOP, BIGWIN_BOTTOM, BIGWIN_LEFT, BIGWIN_RIGHT)
|
2017-03-22 16:46:17 +00:00
|
|
|
end
|
|
|
|
|
2016-11-07 15:58:46 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
export def rightJustifyStr(str, rightX)#0
|
2017-05-24 14:47:47 +00:00
|
|
|
word space
|
|
|
|
space = rightX - calcWidth(str)
|
|
|
|
if (space > 0)
|
|
|
|
rawDisplayStr("^T") // do not use printf variants, since it might overwrite str
|
|
|
|
rawDisplayStr(convert3Dec(space))
|
|
|
|
fin
|
|
|
|
rawDisplayStr(str)
|
2015-11-05 17:09:45 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
export def rightJustifyNum(num, rightX)#0
|
2015-11-05 17:09:45 +00:00
|
|
|
rightJustifyStr(convertDec(num), rightX)
|
|
|
|
end
|
|
|
|
|
2017-09-09 16:25:14 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
export def centerStr(str, windowWidth)#0
|
|
|
|
word x
|
|
|
|
x = (windowWidth - calcWidth(str)) >> 1
|
|
|
|
if (x >= 0)
|
|
|
|
rawDisplayStr("^T") // do not use printf variants, since it might overwrite str
|
|
|
|
rawDisplayStr(convert3Dec(x))
|
|
|
|
fin
|
|
|
|
rawDisplayStr(str)
|
|
|
|
end
|
|
|
|
|
2015-11-05 17:09:45 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Display the party data on the screen
|
2017-09-13 15:06:52 +00:00
|
|
|
def showPartyLine(p)#0
|
|
|
|
if p <> global=>p_players; displayChar('\n'); fin
|
|
|
|
if p->b_skillPoints; rawDisplayStr("^I"); fin // inverse for chars needing lvl-up
|
|
|
|
rawDisplayf1("%s^N", p=>s_name)
|
|
|
|
rightJustifyStr(sprintf2("%d/%d", p=>w_health, p=>w_maxHealth), CHAR_WND_HEALTH_X)
|
|
|
|
end
|
2017-06-10 14:32:31 +00:00
|
|
|
export def showParty()#0
|
2017-07-08 23:36:08 +00:00
|
|
|
word p, cursX, cursY
|
2015-12-05 23:01:44 +00:00
|
|
|
|
2017-07-08 23:36:08 +00:00
|
|
|
cursX, cursY = getCursor()
|
2015-06-01 17:38:22 +00:00
|
|
|
setWindow3()
|
2015-12-05 23:01:44 +00:00
|
|
|
clearWindow()
|
2015-11-05 17:09:45 +00:00
|
|
|
|
2017-09-13 15:06:52 +00:00
|
|
|
// Display header (or LEVEL UP message if any player has un-applied skill pts)
|
|
|
|
if first(global=>p_players, &(p) p->b_skillPoints)
|
2017-07-08 23:36:08 +00:00
|
|
|
rawDisplayStr("^Y^I LEVEL U)P ^N\n")
|
2017-06-14 16:22:05 +00:00
|
|
|
else
|
2017-06-30 15:11:34 +00:00
|
|
|
rawDisplayStr("^LName")
|
|
|
|
rightJustifyStr(@S_HEALTH, CHAR_WND_HEALTH_X) // begin underline mode
|
|
|
|
rawDisplayStr("^N\n")
|
2017-06-14 16:22:05 +00:00
|
|
|
fin
|
2015-11-05 17:09:45 +00:00
|
|
|
|
2017-03-25 14:23:10 +00:00
|
|
|
// Display each character's name and health
|
2017-09-13 15:06:52 +00:00
|
|
|
forEach(global=>p_players, @showPartyLine)
|
2015-11-05 17:09:45 +00:00
|
|
|
|
|
|
|
// Finish up
|
2018-01-05 17:17:01 +00:00
|
|
|
if mapIs3D and texturesLoaded; copyWindow(0); fin
|
2015-06-03 16:12:15 +00:00
|
|
|
setWindow2()
|
2017-07-08 23:36:08 +00:00
|
|
|
setCursor(cursX, cursY)
|
2016-07-16 12:39:22 +00:00
|
|
|
needShowParty = FALSE
|
2015-06-01 17:38:22 +00:00
|
|
|
end
|
|
|
|
|
2016-09-15 18:08:43 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
def getArgCount(pFunc)#1
|
2016-09-15 18:08:43 +00:00
|
|
|
word pBytecode
|
|
|
|
|
|
|
|
// skip over JMP to plasma interp, get addr in aux mem
|
2016-09-20 13:15:49 +00:00
|
|
|
pBytecode = pFunc=>3
|
|
|
|
|
2016-09-15 18:08:43 +00:00
|
|
|
// Check if the function starts with ENTER op
|
|
|
|
if readAuxByte(pBytecode) == $58
|
|
|
|
return readAuxByte(pBytecode+2)
|
|
|
|
fin
|
|
|
|
|
|
|
|
// Zero-arg functions sometimes omit ENTER altogether.
|
|
|
|
return 0
|
|
|
|
end
|
|
|
|
|
2020-06-20 23:09:20 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
def setTextCountdown()#0
|
|
|
|
textClearCountdown = 3
|
|
|
|
if mapIs3D and texturesLoaded
|
|
|
|
copyWindow(0)
|
|
|
|
fin
|
|
|
|
textDrawn = FALSE
|
|
|
|
end
|
|
|
|
|
2016-09-15 18:08:43 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Send an event to the scripts on the current map square
|
2017-06-10 15:56:23 +00:00
|
|
|
export def scriptEvent(event, param)#0
|
2016-09-20 13:15:49 +00:00
|
|
|
byte i, argCount
|
2016-09-15 18:08:43 +00:00
|
|
|
word script
|
2018-09-29 17:10:25 +00:00
|
|
|
if !nMapScripts or q_playerDeath; return; fin
|
2016-09-15 18:08:43 +00:00
|
|
|
|
2017-08-10 17:51:20 +00:00
|
|
|
if inScript; return; fin // avoid doing scripted events inside other scripts
|
|
|
|
inScript = TRUE
|
|
|
|
|
2016-09-15 18:08:43 +00:00
|
|
|
setWindow2()
|
|
|
|
|
2017-08-02 17:20:22 +00:00
|
|
|
textDrawn = FALSE
|
2016-09-15 18:08:43 +00:00
|
|
|
for i = 0 to nMapScripts-1
|
|
|
|
script = mapScripts[i]
|
2016-09-20 13:15:49 +00:00
|
|
|
argCount = getArgCount(script)
|
|
|
|
if argCount == 0 and event == @S_ENTER // zero-param scripts are assumed to be strictly 'enter' handlers
|
2016-09-15 18:08:43 +00:00
|
|
|
script()
|
2016-09-20 13:15:49 +00:00
|
|
|
elsif argCount == 1
|
|
|
|
script(event)
|
|
|
|
elsif argCount == 2
|
|
|
|
script(event, param)
|
2016-09-15 18:08:43 +00:00
|
|
|
fin
|
|
|
|
next
|
2017-08-10 17:51:20 +00:00
|
|
|
|
|
|
|
inScript = FALSE
|
2016-09-15 18:08:43 +00:00
|
|
|
|
2020-06-20 23:09:20 +00:00
|
|
|
if textDrawn; setTextCountdown; fin
|
2016-09-15 18:08:43 +00:00
|
|
|
clearPortrait()
|
|
|
|
if needShowParty; showParty(); fin
|
2018-09-29 17:10:25 +00:00
|
|
|
if global=>p_players=>w_health == 0; q_playerDeath = TRUE; fin
|
2016-09-15 18:08:43 +00:00
|
|
|
end
|
|
|
|
|
2016-10-24 14:08:37 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
export def loadMainFrameImg()#0
|
2016-10-24 15:20:52 +00:00
|
|
|
if curFullscreenImg
|
|
|
|
auxMmgr(FREE_MEMORY, curFullscreenImg)
|
|
|
|
curFullscreenImg = NULL
|
|
|
|
fin
|
2017-08-14 15:01:31 +00:00
|
|
|
loadFrameImg(mapIs3D+2)
|
2018-03-17 17:04:10 +00:00
|
|
|
if mapIs3D
|
|
|
|
^EMUSIG_3D_MAP
|
|
|
|
else
|
|
|
|
^EMUSIG_2D_MAP
|
|
|
|
fin
|
2017-08-14 16:25:38 +00:00
|
|
|
if curFullscreenImg
|
|
|
|
auxMmgr(FREE_MEMORY, curFullscreenImg) // we don't allow animated main frames, so save memory
|
|
|
|
curFullscreenImg = NULL
|
|
|
|
fin
|
2016-10-24 14:08:37 +00:00
|
|
|
end
|
|
|
|
|
2018-01-04 16:41:48 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2019-07-15 17:21:12 +00:00
|
|
|
def loadUtil3d()#0
|
|
|
|
if !pModUtil3d and mapIs3D
|
|
|
|
mmgr(START_LOAD, 1) // code is in partition 1
|
|
|
|
pModUtil3d = mmgr(QUEUE_LOAD, MOD_UTIL3D<<8 | RES_TYPE_MODULE)
|
|
|
|
mmgr(FINISH_LOAD, 0)
|
|
|
|
util3d = pModUtil3d()
|
2019-07-16 18:03:40 +00:00
|
|
|
initCmds() // rebuild the command table with new function ptrs
|
2018-01-09 18:02:28 +00:00
|
|
|
fin
|
2017-08-04 15:15:40 +00:00
|
|
|
end
|
|
|
|
|
2015-01-10 21:17:56 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2015-12-28 17:13:18 +00:00
|
|
|
// Load code and data, set up everything to display a 2D or 3D map
|
2017-06-10 15:56:23 +00:00
|
|
|
def initMap(x, y, dir)#0
|
2020-07-07 15:33:59 +00:00
|
|
|
//AUTOMAP_CHECK// word pDiskOps
|
2019-04-24 13:33:18 +00:00
|
|
|
|
2018-03-28 22:29:49 +00:00
|
|
|
// If we have a renderer loaded, let it know to flush automap marks
|
2019-07-15 17:21:12 +00:00
|
|
|
textureControl(FALSE)
|
2018-03-28 22:29:49 +00:00
|
|
|
|
2014-08-21 15:17:50 +00:00
|
|
|
// Reset memory (our module will stay since memory manager locked it upon load)
|
2015-08-19 14:22:20 +00:00
|
|
|
mmgr(RESET_MEMORY, 0)
|
2020-05-22 21:35:25 +00:00
|
|
|
|
|
|
|
// Reset all pointers to non-locked memory, since the stuff might go away
|
|
|
|
pModUtil3d = NULL
|
|
|
|
util3d = NULL
|
|
|
|
pCurMap = NULL
|
|
|
|
curEngine = NULL
|
|
|
|
pGlobalTileset = NULL
|
|
|
|
curPortrait = NULL
|
|
|
|
curPortraitNum = 0
|
|
|
|
curFullscreenImg = NULL
|
2018-03-06 16:58:47 +00:00
|
|
|
renderLoaded = FALSE // leave it this way until all scripts done, else scriptDisplayStr renders
|
2014-07-06 23:45:27 +00:00
|
|
|
|
2016-10-13 16:26:51 +00:00
|
|
|
// Load the frame image, then raycaster or tile engine
|
2016-10-24 14:08:37 +00:00
|
|
|
loadMainFrameImg()
|
2017-03-29 16:36:14 +00:00
|
|
|
mmgr(START_LOAD, 1)
|
2015-08-19 14:22:20 +00:00
|
|
|
mmgr(SET_MEM_TARGET, displayEngine)
|
2015-04-05 16:52:38 +00:00
|
|
|
if mapIs3D
|
2015-12-10 17:43:21 +00:00
|
|
|
mmgr(QUEUE_LOAD, CODE_RENDER<<8 | RES_TYPE_CODE)
|
2018-04-27 15:03:21 +00:00
|
|
|
// Reserve memory for the raycaster's tables
|
|
|
|
mmgr(SET_MEM_TARGET, RAY_TABLE_START)
|
|
|
|
mmgr(REQUEST_MEMORY, RAY_TABLE_SIZE)
|
2015-04-05 16:52:38 +00:00
|
|
|
else
|
2015-12-10 17:43:21 +00:00
|
|
|
mmgr(QUEUE_LOAD, CODE_TILE_ENGINE<<8 | RES_TYPE_CODE)
|
2015-04-05 16:52:38 +00:00
|
|
|
fin
|
2018-05-06 16:25:42 +00:00
|
|
|
pGlobalTileset = mmgr(QUEUE_LOAD, 1<<8 | RES_TYPE_TILESET) // even in 3d, need tiles for lamp/etc.
|
2020-07-07 15:33:59 +00:00
|
|
|
//AUTOMAP_CHECK// pDiskOps = mmgr(QUEUE_LOAD, MOD_DISKOPS<<8 | RES_TYPE_MODULE)
|
2016-09-08 15:39:41 +00:00
|
|
|
mmgr(FINISH_LOAD, 0)
|
2019-07-15 17:21:12 +00:00
|
|
|
if mapIs3D; loadUtil3d(); fin
|
2016-06-18 23:31:21 +00:00
|
|
|
|
2020-07-07 15:33:59 +00:00
|
|
|
//AUTOMAP_CHECK// pDiskOps()=>diskops_checkAutomap()
|
|
|
|
//AUTOMAP_CHECK// mmgr(FREE_MEMORY, pDiskOps)
|
2019-04-24 13:33:18 +00:00
|
|
|
|
2017-02-19 18:08:03 +00:00
|
|
|
// Set up the command table
|
2019-07-16 18:03:40 +00:00
|
|
|
initCmds() // must be after loading util3d
|
2017-02-19 18:08:03 +00:00
|
|
|
|
2016-06-18 23:31:21 +00:00
|
|
|
// Load the map
|
2017-02-06 18:01:00 +00:00
|
|
|
curMapPartition = lookupResourcePart(mapIs3D+1, mapNum)
|
2021-10-10 16:30:40 +00:00
|
|
|
if !curMapPartition; fatal("lkup2"); fin
|
2017-02-06 18:01:00 +00:00
|
|
|
mmgr(START_LOAD, curMapPartition)
|
2017-09-03 16:28:46 +00:00
|
|
|
pCurMap = mmgr(QUEUE_LOAD, mapNum<<8 | (RES_TYPE_2D_MAP+mapIs3D))
|
2016-09-08 15:39:41 +00:00
|
|
|
mmgr(FINISH_LOAD, 0)
|
2014-07-22 01:28:39 +00:00
|
|
|
|
2016-05-19 07:31:28 +00:00
|
|
|
// Clear all the windows to the background color (hi-bit set)
|
2017-08-02 17:20:22 +00:00
|
|
|
// except window3 because showParty does it for us
|
|
|
|
setWindow1(); clearWindow()
|
|
|
|
setWindow2(); clearWindow()
|
2016-05-19 07:31:28 +00:00
|
|
|
|
2016-03-31 17:01:04 +00:00
|
|
|
// Clear the list of encounter zones from any previous maps
|
2018-06-07 16:11:07 +00:00
|
|
|
clearEncounterZones
|
2016-03-29 22:17:42 +00:00
|
|
|
|
2015-04-09 17:31:36 +00:00
|
|
|
// Start up the display engine with map data and starting position. This will also load and
|
2015-04-20 15:32:02 +00:00
|
|
|
// init the script module, if any, which will end up calling us back at the setScriptInfo
|
2015-02-27 13:33:11 +00:00
|
|
|
triggerTbl = NULL
|
2015-05-07 16:12:52 +00:00
|
|
|
setWindow2()
|
2018-01-10 16:47:20 +00:00
|
|
|
skyNum = 9 // default
|
|
|
|
groundNum = 10 // default
|
2018-01-16 17:42:31 +00:00
|
|
|
timeEventFunc = NULL
|
2021-09-07 16:08:28 +00:00
|
|
|
mapEmuSound = $FF // special value to mark init
|
2017-09-03 16:28:46 +00:00
|
|
|
initDisplay(curMapPartition, mapNum, pCurMap, x, y, dir)
|
2021-09-07 16:08:28 +00:00
|
|
|
if mapEmuSound == $FF // if no music assigned...
|
|
|
|
mapEmuSound = 0 // ...then record that fact.
|
|
|
|
fin
|
2016-09-08 15:30:08 +00:00
|
|
|
texturesLoaded = TRUE
|
2015-05-15 15:19:18 +00:00
|
|
|
needRender = FALSE
|
2016-06-19 14:42:11 +00:00
|
|
|
textDrawn = FALSE
|
2017-08-02 17:20:22 +00:00
|
|
|
textClearCountdown = 0
|
2016-09-22 15:04:24 +00:00
|
|
|
curEngine = NULL
|
2017-04-03 15:22:32 +00:00
|
|
|
curPortrait = NULL
|
|
|
|
curPortraitNum = 0
|
2016-10-23 17:42:11 +00:00
|
|
|
curFullscreenImg = NULL
|
2018-01-09 18:02:28 +00:00
|
|
|
prevClockColor = 99
|
2018-01-10 15:10:43 +00:00
|
|
|
snoozeX1 = -1
|
2018-01-16 17:42:31 +00:00
|
|
|
if timeEventFunc; timeEventFunc(global->b_hour); fin
|
2018-01-04 18:36:21 +00:00
|
|
|
if mapIs3D
|
2019-07-15 17:21:12 +00:00
|
|
|
if util3d
|
|
|
|
util3d=>util3d_showCompassDir(dir)
|
|
|
|
util3d=>util3d_showClock()
|
|
|
|
fin
|
2018-01-04 18:36:21 +00:00
|
|
|
elsif global->b_curAvatar <> 0
|
2016-07-14 21:01:54 +00:00
|
|
|
setAvatar(global->b_curAvatar)
|
|
|
|
doRender()
|
2016-07-15 05:24:09 +00:00
|
|
|
fin
|
2015-01-10 21:17:56 +00:00
|
|
|
|
2019-08-08 16:45:01 +00:00
|
|
|
// Init the floppy flag after the first full load, which will have certainly
|
|
|
|
// hit some disk besides 1 if there is such a disk.
|
|
|
|
isFloppyVer = getFloppyFlg
|
|
|
|
|
2016-10-24 15:20:52 +00:00
|
|
|
// Assume there might be animations until we learn otherwise
|
2018-02-21 17:54:30 +00:00
|
|
|
resetAnimPause
|
2016-10-24 15:20:52 +00:00
|
|
|
|
2016-09-15 18:08:43 +00:00
|
|
|
// Populate script handlers for the current square, so that leave handlers will trigger right.
|
2018-02-14 16:58:22 +00:00
|
|
|
nMapScripts = scanScripts(x-triggerOriginX, y-triggerOriginY, triggerTbl, @mapScripts)
|
2016-09-15 18:08:43 +00:00
|
|
|
|
2015-11-05 17:09:45 +00:00
|
|
|
// Display the party characters
|
|
|
|
showParty()
|
2018-03-06 16:58:47 +00:00
|
|
|
|
|
|
|
// All done.
|
|
|
|
renderLoaded = TRUE
|
2014-07-08 16:39:31 +00:00
|
|
|
end
|
|
|
|
|
2016-07-14 20:52:23 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
export def scriptSetAvatar(avatarTileNum)#0
|
2016-07-14 21:01:54 +00:00
|
|
|
global->b_curAvatar = avatarTileNum
|
2016-07-28 16:46:38 +00:00
|
|
|
if renderLoaded; setAvatar(avatarTileNum); fin
|
2016-07-14 20:52:23 +00:00
|
|
|
end
|
|
|
|
|
2016-09-08 15:30:08 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2019-07-15 17:21:12 +00:00
|
|
|
export def textureControl(flg)#0
|
2020-06-05 20:18:16 +00:00
|
|
|
if renderLoaded
|
|
|
|
flipToPage1
|
|
|
|
if flg <> texturesLoaded
|
|
|
|
if flg
|
|
|
|
_texControl(1)
|
|
|
|
if mapIs3D; loadUtil3d; fin
|
|
|
|
showingLamp = mapIs3D and (skyNum == 0 or skyNum == 8)
|
|
|
|
else
|
|
|
|
_texControl(0)
|
2023-10-15 19:44:34 +00:00
|
|
|
if !mapIs3D
|
|
|
|
pCurMap = NULL // tile engine frees all maps, even current one
|
|
|
|
fin
|
2020-06-05 20:18:16 +00:00
|
|
|
if pModUtil3d and mapIs3D and !inScript // e.g. strafe -> script -> combat: need strafe to stay in mem!
|
|
|
|
mmgr(FREE_MEMORY, pModUtil3d)
|
|
|
|
pModUtil3d = NULL
|
|
|
|
util3d = NULL
|
|
|
|
fin
|
|
|
|
showingLamp = FALSE
|
2020-08-22 17:50:45 +00:00
|
|
|
needRender = FALSE
|
2020-03-05 15:22:22 +00:00
|
|
|
fin
|
2019-07-15 17:21:12 +00:00
|
|
|
fin
|
2020-06-05 20:18:16 +00:00
|
|
|
texturesLoaded = flg
|
|
|
|
else
|
|
|
|
texturesLoaded = FALSE
|
2016-09-08 15:30:08 +00:00
|
|
|
fin
|
|
|
|
end
|
|
|
|
|
2015-12-17 17:19:49 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2015-12-28 17:13:18 +00:00
|
|
|
// Display a portrait drawing (typically called from scripts)
|
2020-01-22 17:44:18 +00:00
|
|
|
export def clearPortrait()#0
|
2015-12-17 17:19:49 +00:00
|
|
|
if curPortrait
|
|
|
|
auxMmgr(FREE_MEMORY, curPortrait)
|
2016-10-23 17:42:11 +00:00
|
|
|
curPortrait = NULL
|
2017-04-03 15:22:32 +00:00
|
|
|
curPortraitNum = 0
|
2018-03-05 16:15:43 +00:00
|
|
|
needRender = TRUE
|
2016-10-23 17:42:11 +00:00
|
|
|
fin
|
|
|
|
if curFullscreenImg
|
|
|
|
auxMmgr(FREE_MEMORY, curFullscreenImg)
|
|
|
|
curFullscreenImg = NULL
|
2016-10-24 14:08:37 +00:00
|
|
|
loadMainFrameImg()
|
2018-03-05 16:15:43 +00:00
|
|
|
needRender = TRUE
|
2015-12-17 17:19:49 +00:00
|
|
|
fin
|
2015-12-28 17:13:18 +00:00
|
|
|
end
|
2015-12-17 17:19:49 +00:00
|
|
|
|
2015-12-28 17:13:18 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Perform rendering, copy if necessary, clear appropriate flags
|
2017-06-10 14:32:31 +00:00
|
|
|
def doRender()#0
|
2015-12-28 17:13:18 +00:00
|
|
|
if curPortrait; clearPortrait(); fin
|
2019-07-15 17:21:12 +00:00
|
|
|
textureControl(TRUE)
|
|
|
|
if showingLamp; util3d=>util3d_nextLampFrame(); fin
|
2018-03-03 18:22:10 +00:00
|
|
|
render(0) // Don't interrupt on kbd - must finish the render
|
2015-12-17 17:19:49 +00:00
|
|
|
needRender = FALSE
|
|
|
|
end
|
|
|
|
|
2015-01-10 21:17:56 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2018-01-09 18:02:28 +00:00
|
|
|
def advTime(hours, mins, secs)#0
|
2020-07-07 15:33:59 +00:00
|
|
|
byte redrawClock, runScript, prevHour, add
|
|
|
|
word player
|
2018-01-09 18:02:28 +00:00
|
|
|
|
|
|
|
if secs
|
|
|
|
global->b_second = global->b_second + secs
|
|
|
|
while global->b_second >= 60
|
|
|
|
global->b_second = global->b_second - 60
|
|
|
|
mins++
|
|
|
|
loop
|
|
|
|
fin
|
|
|
|
|
|
|
|
if mins or hours
|
|
|
|
redrawClock = FALSE
|
2018-01-10 16:47:20 +00:00
|
|
|
runScript = FALSE
|
2018-01-09 18:02:28 +00:00
|
|
|
global->b_minute = global->b_minute + mins
|
|
|
|
if global->b_minute >= nextSignificantMinute; redrawClock = TRUE; fin
|
|
|
|
while global->b_minute >= 60
|
|
|
|
global->b_minute = global->b_minute - 60
|
|
|
|
hours++
|
|
|
|
loop
|
|
|
|
|
|
|
|
if hours
|
|
|
|
redrawClock = TRUE
|
2020-07-07 15:33:59 +00:00
|
|
|
prevHour = global->b_hour
|
2018-01-09 18:02:28 +00:00
|
|
|
global->b_hour = global->b_hour + hours
|
|
|
|
while global->b_hour >= 24
|
|
|
|
global->b_hour = global->b_hour - 24
|
|
|
|
loop
|
|
|
|
runScript = TRUE
|
2020-07-07 15:33:59 +00:00
|
|
|
|
|
|
|
// Heal over time every 12 hours
|
|
|
|
if (prevHour / 12) <> (global->b_hour / 12)
|
|
|
|
player = global=>p_players
|
|
|
|
while player
|
2020-07-07 16:13:59 +00:00
|
|
|
add = (rand16 % (player->b_stamina+1))/3
|
2020-07-07 15:33:59 +00:00
|
|
|
setStat(player, @S_HEALTH, player=>w_health + add)
|
|
|
|
player = player=>p_nextObj
|
|
|
|
loop
|
|
|
|
if needShowParty; showParty(); fin
|
|
|
|
fin
|
2018-01-09 18:02:28 +00:00
|
|
|
fin
|
|
|
|
|
2020-08-24 16:57:22 +00:00
|
|
|
if mapIs3D and redrawClock and renderLoaded and texturesLoaded and util3d
|
|
|
|
util3d=>util3d_showClock()
|
2018-01-09 18:02:28 +00:00
|
|
|
fin
|
|
|
|
|
2018-01-10 16:47:20 +00:00
|
|
|
if runScript and timeEventFunc
|
|
|
|
timeEventFunc(global->b_hour)
|
|
|
|
fin
|
2018-01-09 18:02:28 +00:00
|
|
|
fin
|
|
|
|
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.
|
2019-08-08 16:45:01 +00:00
|
|
|
def _scriptDisplayStr(str)#0
|
2020-08-22 17:50:45 +00:00
|
|
|
if needRender and renderLoaded and texturesLoaded and !curPortrait and !curFullscreenImg
|
2019-07-15 17:21:12 +00:00
|
|
|
doRender()
|
|
|
|
flipToPage1()
|
|
|
|
needRender = FALSE
|
2018-01-09 18:02:28 +00:00
|
|
|
fin
|
2019-07-15 17:21:12 +00:00
|
|
|
if textClearCountdown; clearTextWindow(); fin
|
2020-08-22 17:50:45 +00:00
|
|
|
if forceRawScrDisp
|
|
|
|
rawDisplayStr(str)
|
|
|
|
else
|
|
|
|
displayStr(str)
|
|
|
|
fin
|
2019-07-15 17:21:12 +00:00
|
|
|
textDrawn = TRUE
|
2018-03-06 16:58:47 +00:00
|
|
|
anyInteraction = TRUE
|
2018-01-09 18:02:28 +00:00
|
|
|
end
|
|
|
|
|
2019-08-08 16:45:01 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2019-09-12 14:21:09 +00:00
|
|
|
export def promptAnyKey(clearAfter)#0
|
2019-08-08 16:45:01 +00:00
|
|
|
scriptDisplayStr("\n(press any key)")
|
|
|
|
getUpperKey
|
2019-09-12 14:21:09 +00:00
|
|
|
if clearAfter; clearWindow; fin
|
2019-08-08 16:45:01 +00:00
|
|
|
end
|
|
|
|
|
2018-01-09 18:02:28 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
def snooze()#1
|
2018-01-10 15:10:43 +00:00
|
|
|
word cursX, cursY
|
|
|
|
cursX, cursY = getCursor()
|
2018-01-10 16:47:20 +00:00
|
|
|
if cursX == snoozeX1 and cursY == snoozeY
|
2018-01-10 15:10:43 +00:00
|
|
|
if mapIs3D
|
|
|
|
advTime(0, 14 - (global->b_minute % 15), 60 - global->b_second) // next 15 min mark
|
|
|
|
else
|
|
|
|
advTime(0, 59 - global->b_minute, 60 - global->b_second) // start of next hour
|
|
|
|
fin
|
2018-01-10 16:47:20 +00:00
|
|
|
fin
|
|
|
|
|
2023-10-15 19:44:34 +00:00
|
|
|
nextAnimFrame()
|
|
|
|
|
2018-01-10 16:47:20 +00:00
|
|
|
cursX, cursY = getCursor()
|
|
|
|
if cursX == snoozeX1 and cursY == snoozeY
|
2018-01-10 15:10:43 +00:00
|
|
|
rawDisplayf1("^T%D", snoozeX0)
|
|
|
|
else
|
2018-01-10 16:47:20 +00:00
|
|
|
if cursY; rawDisplayStr("\n"); fin
|
|
|
|
if cursX; rawDisplayStr("\n"); fin
|
2018-01-10 15:10:43 +00:00
|
|
|
rawDisplayStr("The time: ")
|
2018-01-10 16:47:20 +00:00
|
|
|
snoozeX0, snoozeY = getCursor()
|
2018-01-10 15:10:43 +00:00
|
|
|
fin
|
|
|
|
rawDisplayf1("^C%d:", global->b_hour == 0 ?? 12 :: global->b_hour == 12 ?? 12 :: global->b_hour % 12)
|
|
|
|
rawDisplayf2("%s%d ", global->b_minute < 10 ?? "0" :: "", global->b_minute)
|
|
|
|
rawDisplayf1("%s.", global->b_hour < 12 ?? "am" :: "pm")
|
2018-01-10 16:47:20 +00:00
|
|
|
snoozeX1, snoozeY = getCursor()
|
2020-06-20 23:09:20 +00:00
|
|
|
setTextCountdown
|
2018-01-09 18:02:28 +00:00
|
|
|
return 0
|
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2019-07-16 18:03:40 +00:00
|
|
|
export def moveInternal(facingDir, moveDir, beepOK, shouldAdvTime)#1
|
2019-06-27 15:59:17 +00:00
|
|
|
byte val, i
|
2016-03-29 22:17:42 +00:00
|
|
|
word x, y
|
2017-08-10 17:51:20 +00:00
|
|
|
|
|
|
|
setDir(moveDir)
|
2019-06-27 15:59:17 +00:00
|
|
|
val = advance(global->b_moveMode)
|
2018-03-10 16:31:09 +00:00
|
|
|
lastMoveDir = moveDir
|
2017-08-10 17:51:20 +00:00
|
|
|
setDir(facingDir)
|
2015-05-15 15:19:18 +00:00
|
|
|
|
|
|
|
// If not blocked, render at the new position.
|
2016-07-04 01:20:07 +00:00
|
|
|
if val == 0
|
2020-06-20 23:09:20 +00:00
|
|
|
if beepOK and !inScript // don't beep for scripted moves
|
|
|
|
clearTextWindow
|
|
|
|
rawDisplayStr("Blocked!\n")
|
2021-10-19 15:01:00 +00:00
|
|
|
beep
|
2020-06-20 23:09:20 +00:00
|
|
|
setTextCountdown
|
|
|
|
fin
|
2016-07-04 01:20:07 +00:00
|
|
|
else
|
2015-06-01 02:03:38 +00:00
|
|
|
if !mapIs3D
|
2015-12-17 17:19:49 +00:00
|
|
|
doRender()
|
2015-06-01 02:03:38 +00:00
|
|
|
else
|
|
|
|
needRender = TRUE
|
|
|
|
fin
|
2015-05-15 15:19:18 +00:00
|
|
|
fin
|
|
|
|
|
2018-01-09 18:02:28 +00:00
|
|
|
// Advance time if requested
|
|
|
|
if shouldAdvTime
|
|
|
|
if mapIs3D
|
2020-07-07 15:33:59 +00:00
|
|
|
for i = 1 to global->b_moveMode
|
2019-06-27 15:59:17 +00:00
|
|
|
advTime(CLOCK_ADV_3D_HOURS, CLOCK_ADV_3D_MINS, CLOCK_ADV_3D_SECS)
|
|
|
|
next
|
2018-01-09 18:02:28 +00:00
|
|
|
else
|
|
|
|
advTime(CLOCK_ADV_2D_HOURS, CLOCK_ADV_2D_MINS, CLOCK_ADV_2D_SECS)
|
|
|
|
fin
|
|
|
|
fin
|
|
|
|
|
2017-08-10 17:51:20 +00:00
|
|
|
// If we're on a new map tile, run leave handlers from old tile.
|
|
|
|
if val >= 2
|
|
|
|
scriptEvent(@S_LEAVE, NULL)
|
|
|
|
nMapScripts = 0
|
2017-08-10 16:56:46 +00:00
|
|
|
|
2018-02-14 16:58:22 +00:00
|
|
|
// If there are script(s) on the new tile, run them.
|
2017-08-10 17:51:20 +00:00
|
|
|
getPos(@x, @y)
|
2018-02-14 16:58:22 +00:00
|
|
|
nMapScripts = scanScripts(x-triggerOriginX, y-triggerOriginY, triggerTbl, @mapScripts)
|
2017-08-10 17:51:20 +00:00
|
|
|
if nMapScripts
|
|
|
|
scriptEvent(@S_ENTER, NULL)
|
|
|
|
elsif global=>p_encounterZones
|
2016-09-15 18:08:43 +00:00
|
|
|
checkEncounter(x, y, FALSE)
|
2016-03-29 22:17:42 +00:00
|
|
|
fin
|
2014-07-06 23:09:10 +00:00
|
|
|
fin
|
2019-06-27 15:59:17 +00:00
|
|
|
|
2017-08-10 17:51:20 +00:00
|
|
|
return val
|
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Advance one step forward (works for either 3D or 2D maps)
|
|
|
|
def moveForward()#1
|
|
|
|
byte dir
|
|
|
|
dir = getDir()
|
2018-01-09 18:02:28 +00:00
|
|
|
return moveInternal(dir, dir, TRUE, TRUE) // beep ok, adv time
|
2014-07-06 23:09:10 +00:00
|
|
|
end
|
|
|
|
|
2016-07-01 23:32:57 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2019-06-27 15:59:17 +00:00
|
|
|
// Move backward two quarter-steps (3D mode), or one step (2D mode). This is often used when exiting a
|
2016-09-22 15:13:17 +00:00
|
|
|
// building or fleeing combat, so we don't want to generate any random encounters.
|
2017-06-11 15:25:15 +00:00
|
|
|
export def moveWayBackward()#1
|
2017-08-10 17:51:20 +00:00
|
|
|
byte facingDir, moveDir
|
|
|
|
facingDir = getDir()
|
2018-04-16 15:19:38 +00:00
|
|
|
if lastMoveDir <> $FF
|
|
|
|
moveDir = (lastMoveDir + 8) & 15 // reverse of last move
|
|
|
|
moveInternal(facingDir, moveDir, FALSE, TRUE) // no beep, but do adv time
|
2019-06-27 15:59:17 +00:00
|
|
|
if global->b_moveMode == 1
|
|
|
|
moveInternal(facingDir, moveDir, FALSE, TRUE) // no beep, but do adv time
|
2018-04-16 15:19:38 +00:00
|
|
|
fin
|
2016-07-01 23:32:57 +00:00
|
|
|
fin
|
2018-04-16 15:19:38 +00:00
|
|
|
lastMoveDir = $FF // avoid problems if moveWayBackward called repeatedly
|
2017-08-10 17:51:20 +00:00
|
|
|
return 0
|
2016-07-01 23:32:57 +00:00
|
|
|
end
|
|
|
|
|
2015-01-18 16:59:56 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2018-03-10 16:31:09 +00:00
|
|
|
// Move to the north/east/south/west (2D mode)
|
2017-06-11 15:25:15 +00:00
|
|
|
def moveNorth()#1
|
2015-07-01 17:04:19 +00:00
|
|
|
word x, y
|
|
|
|
getPos(@x, @y)
|
|
|
|
if y > 4
|
|
|
|
setDir(0)
|
|
|
|
moveForward()
|
|
|
|
else
|
|
|
|
beep()
|
|
|
|
fin
|
2017-06-11 15:25:15 +00:00
|
|
|
return 0
|
2015-01-18 16:59:56 +00:00
|
|
|
end
|
|
|
|
|
2017-06-11 15:25:15 +00:00
|
|
|
def moveEast()#1
|
2015-07-01 17:04:19 +00:00
|
|
|
word x, y
|
|
|
|
getPos(@x, @y)
|
|
|
|
if x < totalMapWidth-5
|
|
|
|
setDir(4)
|
|
|
|
moveForward()
|
|
|
|
else
|
|
|
|
beep()
|
|
|
|
fin
|
2017-06-11 15:25:15 +00:00
|
|
|
return 0
|
2015-01-18 16:59:56 +00:00
|
|
|
end
|
|
|
|
|
2017-06-11 15:25:15 +00:00
|
|
|
def moveSouth()#1
|
2015-07-01 17:04:19 +00:00
|
|
|
word x, y
|
|
|
|
getPos(@x, @y)
|
|
|
|
if y < totalMapHeight-5
|
|
|
|
setDir(8)
|
|
|
|
moveForward()
|
|
|
|
else
|
|
|
|
beep()
|
|
|
|
fin
|
2017-06-11 15:25:15 +00:00
|
|
|
return 0
|
2015-01-18 16:59:56 +00:00
|
|
|
end
|
|
|
|
|
2017-06-11 15:25:15 +00:00
|
|
|
def moveWest()#1
|
2015-07-01 17:04:19 +00:00
|
|
|
word x, y
|
|
|
|
getPos(@x, @y)
|
|
|
|
if x > 4
|
|
|
|
setDir(12)
|
|
|
|
moveForward()
|
|
|
|
else
|
|
|
|
beep()
|
|
|
|
fin
|
2017-06-11 15:25:15 +00:00
|
|
|
return 0
|
2015-03-25 14:40:46 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2015-05-20 16:47:35 +00:00
|
|
|
// Switch to a new map (2D or 3D) and establish position on it
|
2017-06-10 15:56:23 +00:00
|
|
|
export def setMap(is3D, num, x, y, dir)#0
|
2015-05-20 16:47:35 +00:00
|
|
|
if is3D == mapIs3D and num == mapNum
|
|
|
|
setPos(x, y)
|
|
|
|
setDir(dir)
|
|
|
|
needRender = TRUE
|
|
|
|
else
|
2015-05-27 15:13:09 +00:00
|
|
|
flipToPage1()
|
2016-06-20 15:49:01 +00:00
|
|
|
showMapName("Traveling...")
|
2017-09-09 14:56:37 +00:00
|
|
|
useMapWindow()
|
2015-05-20 16:47:35 +00:00
|
|
|
mapIs3D = is3D
|
|
|
|
mapNum = num
|
|
|
|
initMap(x, y, dir)
|
2020-08-22 17:50:45 +00:00
|
|
|
saveGame
|
2015-05-20 16:47:35 +00:00
|
|
|
fin
|
2016-09-15 18:08:43 +00:00
|
|
|
// Don't send enter event, because we often land on an "Exit to wilderness?" script
|
|
|
|
//NO:scriptEvent(S_ENTER, NULL)
|
2014-07-20 13:02:10 +00:00
|
|
|
end
|
|
|
|
|
2020-06-17 19:42:43 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
export def callGlobalFunc(moduleNum, arg1, arg2, arg3)#1
|
|
|
|
word pModule, pFunc, ret
|
|
|
|
|
|
|
|
// First load the module
|
|
|
|
if renderLoaded; flipToPage1(); fin
|
2021-01-25 15:06:24 +00:00
|
|
|
mmgr(START_LOAD, moduleNum <= LAST_REQ_GS_MOD ?? 1 :: curMapPartition)
|
2020-06-17 19:42:43 +00:00
|
|
|
pModule = mmgr(QUEUE_LOAD, moduleNum<<8 | RES_TYPE_MODULE)
|
|
|
|
mmgr(FINISH_LOAD, 0)
|
|
|
|
|
|
|
|
// Call the function, passing it the number of args it expects
|
|
|
|
pFunc = pModule()
|
|
|
|
when getArgCount(pFunc)
|
|
|
|
is 0; ret = pFunc(); break
|
|
|
|
is 1; ret = pFunc(arg1); break
|
|
|
|
is 2; ret = pFunc(arg1, arg2); break
|
|
|
|
is 3; ret = pFunc(arg1, arg2, arg3); break
|
|
|
|
otherwise fatal("maxGlobParams")
|
|
|
|
wend
|
|
|
|
|
|
|
|
// Unload the module and we're done.
|
|
|
|
mmgr(FREE_MEMORY, pModule)
|
|
|
|
return ret
|
|
|
|
end
|
|
|
|
|
2015-05-20 16:47:35 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 14:21:58 +00:00
|
|
|
export def queue_setMap(is3D, num, x, y, dir)#0
|
2020-06-17 19:42:43 +00:00
|
|
|
byte part
|
|
|
|
part = lookupResourcePart(is3d+1, num)
|
2020-07-07 15:33:59 +00:00
|
|
|
if diskLimit and part > diskLimit
|
2020-06-17 19:42:43 +00:00
|
|
|
clearWindow
|
|
|
|
callGlobalFunc(GS_DISK_LIMIT, 1, 0, 0)
|
|
|
|
return
|
|
|
|
fin
|
2015-05-20 16:47:35 +00:00
|
|
|
q_mapIs3D = is3D
|
|
|
|
q_mapNum = num
|
|
|
|
q_x = x
|
|
|
|
q_y = y
|
|
|
|
q_dir = dir
|
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 14:21:58 +00:00
|
|
|
export def queue_teleport(x, y, dir)#0
|
2015-05-20 16:47:35 +00:00
|
|
|
queue_setMap(mapIs3D, mapNum, x, y, dir)
|
2015-05-03 16:25:57 +00:00
|
|
|
end
|
|
|
|
|
2015-01-10 21:17:56 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Get a key and dispatch it to a command. Then do it again, forever.
|
2017-06-10 15:56:23 +00:00
|
|
|
def kbdLoop()#0
|
2019-12-19 17:38:14 +00:00
|
|
|
word func, tmp
|
2018-04-08 17:08:50 +00:00
|
|
|
byte xreg
|
2015-04-20 15:32:02 +00:00
|
|
|
xreg = getXReg()
|
2014-07-06 23:02:52 +00:00
|
|
|
while TRUE
|
2017-01-15 17:10:43 +00:00
|
|
|
// If the asm routines all work correctly, by the time we get to the top of this loop
|
|
|
|
// the X register should always have the same value.
|
2015-04-20 15:32:02 +00:00
|
|
|
tmp = getXReg()
|
2018-04-08 17:08:50 +00:00
|
|
|
if tmp <> xreg; printHex(xreg<<8 | tmp); fatal("xRegChg"); fin
|
2019-12-19 17:38:14 +00:00
|
|
|
cmdKey = getUpperKey()
|
|
|
|
if cmdKey >= 0 and cmdKey < $60
|
|
|
|
func = cmdTbl[cmdKey]
|
2017-08-02 17:20:22 +00:00
|
|
|
if func
|
|
|
|
if textClearCountdown
|
2017-09-12 15:21:23 +00:00
|
|
|
if textClearCountdown == 1; clearTextWindow(); fin
|
2017-08-02 17:20:22 +00:00
|
|
|
textClearCountdown--
|
|
|
|
fin
|
|
|
|
func()
|
|
|
|
fin
|
2014-07-06 23:02:52 +00:00
|
|
|
fin
|
2018-09-29 17:10:25 +00:00
|
|
|
if q_playerDeath; playerDeath(); continue; fin
|
2018-04-08 17:08:50 +00:00
|
|
|
if q_mapNum; setMap(q_mapIs3D, q_mapNum, q_x, q_y, q_dir); q_mapNum = 0; fin
|
|
|
|
if needRender; doRender(); fin
|
|
|
|
if prevScriptModule
|
|
|
|
if prevScriptModule <> scriptModule
|
|
|
|
tmp = mmgr(FIND_IN_MEM, prevScriptModule<<8 | RES_TYPE_MODULE)
|
|
|
|
if tmp; mmgr(FREE_MEMORY, tmp); fin
|
|
|
|
fin
|
|
|
|
prevScriptModule = 0
|
2015-05-15 15:19:18 +00:00
|
|
|
fin
|
2014-07-06 23:02:52 +00:00
|
|
|
loop
|
|
|
|
end
|
|
|
|
|
2016-08-05 16:27:11 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
def hashString(str)#1
|
2016-08-05 16:27:11 +00:00
|
|
|
return hashBuffer(str+1, ^str)
|
|
|
|
end
|
|
|
|
|
2015-12-09 15:35:02 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 14:32:31 +00:00
|
|
|
export def showMapName(mapName)#0
|
2015-12-09 15:35:02 +00:00
|
|
|
word newNameHash
|
|
|
|
|
|
|
|
newNameHash = hashString(mapName)
|
|
|
|
if newNameHash <> mapNameHash
|
|
|
|
setWindow1()
|
|
|
|
clearWindow()
|
2020-08-22 17:50:45 +00:00
|
|
|
centerStr(mapName, 84)
|
2018-01-05 17:17:01 +00:00
|
|
|
if mapIs3D and texturesLoaded; copyWindow(0); fin
|
2015-12-09 15:35:02 +00:00
|
|
|
mapNameHash = newNameHash
|
|
|
|
fin
|
|
|
|
end
|
|
|
|
|
2015-01-10 21:17:56 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2015-07-01 17:04:19 +00:00
|
|
|
// 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.
|
2018-04-08 17:08:50 +00:00
|
|
|
export def setScriptInfo(mapName, moduleNum, timeFn, trigTbl, wdt, hgt)#0
|
|
|
|
word tmp
|
2018-01-10 16:47:20 +00:00
|
|
|
|
|
|
|
// Record the time event function, if any
|
|
|
|
timeEventFunc = timeFn
|
2015-04-20 15:32:02 +00:00
|
|
|
|
2018-04-08 17:08:50 +00:00
|
|
|
// Record new script, and prepare to free old one. While theoretically there could be a
|
|
|
|
// circumstance where the old prev is still un-freed, it's rare enough that it'd be hard to
|
2018-04-16 12:35:40 +00:00
|
|
|
// test freeing it here correctly. So we leave freeing to the main keyboard loop.
|
2018-04-08 17:08:50 +00:00
|
|
|
if scriptModule <> moduleNum
|
|
|
|
prevScriptModule = scriptModule
|
|
|
|
scriptModule = moduleNum
|
|
|
|
fin
|
|
|
|
|
2015-05-21 14:19:22 +00:00
|
|
|
// Grab the trigger table origins (used so the table can be more compact)
|
|
|
|
triggerOriginX = trigTbl=>0
|
|
|
|
triggerOriginY = trigTbl=>2
|
|
|
|
|
2015-04-20 15:32:02 +00:00
|
|
|
// Record the trigger table pointer
|
2015-05-21 14:19:22 +00:00
|
|
|
triggerTbl = trigTbl + 4
|
2015-04-20 15:32:02 +00:00
|
|
|
|
2015-07-01 17:04:19 +00:00
|
|
|
// Record the maximum width and height
|
|
|
|
totalMapWidth = wdt
|
|
|
|
totalMapHeight = hgt
|
|
|
|
|
2015-04-20 15:32:02 +00:00
|
|
|
// Display map name
|
2016-09-22 14:32:54 +00:00
|
|
|
global=>s_mapName = mmgr(HEAP_INTERN, mapName)
|
2015-12-09 15:35:02 +00:00
|
|
|
showMapName(mapName)
|
2015-04-20 15:32:02 +00:00
|
|
|
|
2016-04-26 13:39:44 +00:00
|
|
|
// Get ready for new encounter zones
|
2018-06-07 16:11:07 +00:00
|
|
|
clearEncounterZones
|
2016-04-26 13:39:44 +00:00
|
|
|
|
2015-05-15 15:19:18 +00:00
|
|
|
// Back to the main text window.
|
2015-04-20 15:32:02 +00:00
|
|
|
setWindow2()
|
2018-01-10 16:47:20 +00:00
|
|
|
|
|
|
|
// If there's a time script, run it so it can set sky color, etc.
|
|
|
|
if timeFn; timeFn(global->b_hour); fin
|
2015-02-27 13:33:11 +00:00
|
|
|
end
|
|
|
|
|
2016-11-01 16:25:36 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Called by scripts to swap a map tile. We set a flag noting we need to re-render, then use an
|
|
|
|
// assembly routine to do the work.
|
2018-03-07 16:49:21 +00:00
|
|
|
export def scriptCopyTile(fromX, fromY, toX, toY)#0
|
2016-11-01 16:25:36 +00:00
|
|
|
needRender = TRUE
|
2018-03-07 16:49:21 +00:00
|
|
|
copyTile(fromX, fromY, toX, toY)
|
2016-11-01 16:25:36 +00:00
|
|
|
end
|
|
|
|
|
2015-01-10 21:17:56 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Get a key, and don't return until it's Y or N (or lower-case of those). Returns 1 for Y.
|
2017-06-10 15:56:23 +00:00
|
|
|
export def getYN()#1
|
2014-07-19 22:55:27 +00:00
|
|
|
byte key
|
|
|
|
while TRUE
|
|
|
|
key = getUpperKey()
|
|
|
|
if key == 'Y'
|
|
|
|
return 1
|
|
|
|
elsif key == 'N'
|
2017-09-02 15:23:24 +00:00
|
|
|
if frameLoaded
|
|
|
|
clearTextWindow()
|
|
|
|
fin
|
2017-06-10 15:56:23 +00:00
|
|
|
break
|
2014-07-19 22:55:27 +00:00
|
|
|
fin
|
|
|
|
beep()
|
|
|
|
loop
|
2017-06-10 15:56:23 +00:00
|
|
|
return 0
|
2014-07-19 22:55:27 +00:00
|
|
|
end
|
|
|
|
|
2015-12-17 17:19:49 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Show the current animation frame
|
2017-06-10 14:32:31 +00:00
|
|
|
def showAnimFrame()#0
|
2017-09-20 15:44:35 +00:00
|
|
|
byte top
|
2016-10-23 17:42:11 +00:00
|
|
|
if curPortrait
|
2019-01-24 16:52:02 +00:00
|
|
|
if storyMode
|
|
|
|
blit(1, curPortrait + 2, getScreenLine(0)+20, 128, 18)
|
|
|
|
else
|
|
|
|
// Blit portrait to the appropriate area on the screen
|
|
|
|
top = 32 // start at 4th text line in 2D
|
|
|
|
if frameLoaded == 3 // 3D-mode frame? Note: don't check mapIs3D, because we might be in an engine
|
|
|
|
top = 24 // start at 4th text line in 3D
|
|
|
|
fin
|
|
|
|
blit(1, curPortrait + 2, getScreenLine(top)+2, 128, 18)
|
2016-10-23 17:42:11 +00:00
|
|
|
fin
|
|
|
|
needRender = FALSE // suppress display of map for this frame
|
2019-07-15 17:21:12 +00:00
|
|
|
if showingLamp and util3d; util3d=>util3d_nextLampFrame(); fin
|
2016-10-23 17:42:11 +00:00
|
|
|
elsif curFullscreenImg
|
2017-08-04 15:15:40 +00:00
|
|
|
blit(1, curFullscreenImg + 2, getScreenLine(0), 192, 40) // the +2 is to skip anim hdr offset
|
2016-10-23 17:42:11 +00:00
|
|
|
needRender = FALSE // suppress display of map for this frame
|
2020-03-05 15:22:22 +00:00
|
|
|
elsif texturesLoaded
|
|
|
|
if mapIs3D
|
|
|
|
if showingLamp and util3d; util3d=>util3d_nextLampFrame(); fin
|
|
|
|
render($FF) // it's only animation, so do interrupt if a key is pressed
|
|
|
|
if showingLamp and util3d; util3d=>util3d_nextLampFrame(); fin
|
|
|
|
else
|
|
|
|
render(0) // 2d map view
|
|
|
|
fin
|
2015-12-17 17:19:49 +00:00
|
|
|
fin
|
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Advance to next frame of current animation, if any
|
2017-06-10 14:32:31 +00:00
|
|
|
def nextAnimFrame()#0
|
2020-02-26 16:34:22 +00:00
|
|
|
word flags, param
|
|
|
|
byte dir, randNum
|
2020-03-05 15:22:22 +00:00
|
|
|
|
2023-10-15 19:44:34 +00:00
|
|
|
animPauseCt = ANIM_PAUSE_MAX
|
|
|
|
|
2016-10-23 17:42:11 +00:00
|
|
|
if !anyAnims; return; fin
|
2015-12-17 17:19:49 +00:00
|
|
|
|
2015-12-23 23:42:37 +00:00
|
|
|
// Choose a new direction based on the flags. Do this the first time, and once every 3-7 frames.
|
|
|
|
animDirCt = animDirCt - 1
|
2016-10-22 15:46:37 +00:00
|
|
|
if animDirCt <= 0
|
2015-12-23 23:42:37 +00:00
|
|
|
animDirCt = (rand16() % 5) + 3
|
2015-12-17 17:19:49 +00:00
|
|
|
fin
|
|
|
|
|
2016-11-08 16:39:18 +00:00
|
|
|
// Advance animations.
|
2016-10-22 15:46:37 +00:00
|
|
|
// First part is whether to switch directions on fwd/back anims.
|
|
|
|
// Second part is how many frames to advance random anims.
|
2020-02-26 16:34:22 +00:00
|
|
|
param = ((animDirCt==1) & 1) | (((rand16() % 10)+1)<<8)
|
|
|
|
flags = auxMmgr(ADVANCE_ANIMS, param) | mmgr(ADVANCE_ANIMS, param)
|
2016-10-23 16:09:43 +00:00
|
|
|
if (flags >> 8)
|
|
|
|
// An animation was changed -- display it
|
2015-12-17 17:19:49 +00:00
|
|
|
showAnimFrame()
|
2016-10-23 16:09:43 +00:00
|
|
|
elsif (flags & $FF) == 0
|
|
|
|
// No animations in memory; turn off future checking
|
2016-10-22 15:46:37 +00:00
|
|
|
anyAnims = FALSE
|
2015-12-17 17:19:49 +00:00
|
|
|
fin
|
|
|
|
end
|
|
|
|
|
2015-05-27 15:13:09 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2015-05-28 15:14:01 +00:00
|
|
|
// Display a portrait drawing (typically called from scripts)
|
2017-06-10 15:56:23 +00:00
|
|
|
export def setPortrait(portraitNum)#0
|
2015-05-27 15:13:09 +00:00
|
|
|
word srcData
|
2017-07-08 23:36:08 +00:00
|
|
|
byte part, cx, cy, cursX, cursY
|
2015-05-27 15:13:09 +00:00
|
|
|
|
2018-09-13 15:27:57 +00:00
|
|
|
if portraitNum == curPortraitNum; return; fin
|
|
|
|
|
2015-12-28 17:13:18 +00:00
|
|
|
clearPortrait()
|
2016-06-03 16:18:25 +00:00
|
|
|
|
2015-05-28 15:14:01 +00:00
|
|
|
// We're going to switch windows. Save the cursor pos in the text window.
|
2017-07-08 23:36:08 +00:00
|
|
|
cursX, cursY = getCursor()
|
2015-05-27 15:13:09 +00:00
|
|
|
|
2015-12-29 16:59:13 +00:00
|
|
|
// Make room by unloading the textures (only if renderer is loaded)
|
2019-07-15 17:21:12 +00:00
|
|
|
textureControl(FALSE)
|
2015-12-28 18:20:28 +00:00
|
|
|
|
2015-05-28 15:14:01 +00:00
|
|
|
// Now clear out the map area
|
2017-09-09 14:56:37 +00:00
|
|
|
useMapWindow()
|
2015-05-27 15:13:09 +00:00
|
|
|
|
2015-05-28 15:14:01 +00:00
|
|
|
// Restore the cursor position
|
|
|
|
setWindow2()
|
2017-07-08 23:36:08 +00:00
|
|
|
setCursor(cursX, cursY)
|
2015-05-28 15:14:01 +00:00
|
|
|
|
|
|
|
// Load the portrait image and display it
|
2017-02-20 15:42:23 +00:00
|
|
|
part = lookupResourcePart(3, portraitNum)
|
2021-10-10 16:30:40 +00:00
|
|
|
if !part; fatal("lkup3"); fin
|
2017-08-12 17:25:24 +00:00
|
|
|
// Commented out below, because it prevents cycling thru all portraits (in god mode)
|
|
|
|
// NO: if part > 1; part = curMapPartition; fin // Look on disk 1 or current disk only
|
2017-02-20 15:42:23 +00:00
|
|
|
mmgr(START_LOAD, part)
|
2015-12-17 17:19:49 +00:00
|
|
|
curPortrait = auxMmgr(QUEUE_LOAD, portraitNum<<8 | RES_TYPE_PORTRAIT)
|
2017-04-03 15:22:32 +00:00
|
|
|
curPortraitNum = portraitNum
|
2016-09-08 15:39:41 +00:00
|
|
|
mmgr(FINISH_LOAD, 0)
|
2018-02-21 17:54:30 +00:00
|
|
|
resetAnimPause
|
2015-05-27 15:13:09 +00:00
|
|
|
|
2015-12-17 17:19:49 +00:00
|
|
|
// And show the first frame
|
|
|
|
showAnimFrame()
|
2015-12-28 18:20:28 +00:00
|
|
|
|
|
|
|
// Do not render over the portrait
|
|
|
|
needRender = FALSE
|
2015-05-28 15:14:01 +00:00
|
|
|
end
|
2015-05-27 15:13:09 +00:00
|
|
|
|
2015-11-11 16:55:16 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
export def countList(p)#1
|
2019-11-14 17:36:39 +00:00
|
|
|
return sum(p, NULL, &(p) 1)
|
2015-12-05 23:01:44 +00:00
|
|
|
end
|
|
|
|
|
2015-11-11 16:55:16 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2015-12-29 16:59:13 +00:00
|
|
|
// Call like this: addToList(@player=>p_items, itemToAdd)
|
2017-06-09 14:14:53 +00:00
|
|
|
export def addToList(addTo, p)#0
|
2015-12-09 15:35:02 +00:00
|
|
|
// Get to the end of the list
|
|
|
|
while *addTo
|
|
|
|
addTo = (*addTo) + p_nextObj
|
|
|
|
loop
|
2015-11-11 16:55:16 +00:00
|
|
|
p=>p_nextObj = *addTo
|
|
|
|
*addTo = p
|
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2016-06-19 15:15:51 +00:00
|
|
|
// Call like this: removeFromList(@player=>items, itemToRemove)
|
2017-06-10 15:56:23 +00:00
|
|
|
export def removeFromList(pList, toRemove)#0
|
2015-11-11 16:55:16 +00:00
|
|
|
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
|
2017-09-14 15:09:21 +00:00
|
|
|
fatal("unlink")
|
2015-11-11 16:55:16 +00:00
|
|
|
fin
|
|
|
|
end
|
|
|
|
|
2015-11-29 17:21:03 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-09-09 17:08:06 +00:00
|
|
|
export def saveMapPos()#0
|
2016-06-01 16:03:14 +00:00
|
|
|
global->b_mapIs3D = mapIs3D
|
|
|
|
global->b_mapNum = mapNum
|
|
|
|
getPos(@global=>w_mapX, @global=>w_mapY)
|
|
|
|
global->b_mapDir = getDir()
|
|
|
|
end
|
|
|
|
|
2020-07-28 14:31:07 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
def showMoveMode()#0
|
|
|
|
rawDisplayStr("Move mode:\n")
|
|
|
|
if global->b_moveMode == 1; rawDisplayStr("^I"); fin
|
2020-08-21 15:06:19 +00:00
|
|
|
rawDisplayStr("Default^N ")
|
2020-07-28 14:31:07 +00:00
|
|
|
if global->b_moveMode == 2; rawDisplayStr("^I"); fin
|
2020-08-21 15:06:19 +00:00
|
|
|
rawDisplayStr("Fast^N ")
|
2020-07-28 14:31:07 +00:00
|
|
|
if global->b_moveMode == 4; rawDisplayStr("^I"); fin
|
2020-08-21 15:06:19 +00:00
|
|
|
rawDisplayStr("Classic^N\n")
|
2020-07-28 14:31:07 +00:00
|
|
|
setTextCountdown
|
|
|
|
end
|
|
|
|
|
2016-06-01 16:03:14 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
def restoreMapPos()#0
|
2016-06-01 16:03:14 +00:00
|
|
|
mapIs3D = global->b_mapIs3D
|
|
|
|
mapNum = global->b_mapNum
|
2016-07-12 15:09:55 +00:00
|
|
|
initMap(global=>w_mapX, global=>w_mapY, global->b_mapDir)
|
2016-06-01 16:03:14 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
def loadEngine(moduleNum)#1
|
2016-09-22 14:32:54 +00:00
|
|
|
if curEngine; fatal("dblEng"); fin
|
2018-03-10 16:31:09 +00:00
|
|
|
preEnginePortraitNum = curPortraitNum
|
2016-09-22 14:32:54 +00:00
|
|
|
clearPortrait()
|
2020-03-05 15:22:22 +00:00
|
|
|
textureControl(FALSE) // also flips to page 1
|
2016-06-19 14:42:11 +00:00
|
|
|
mmgr(START_LOAD, 1) // code is in partition 1
|
2016-09-22 14:32:54 +00:00
|
|
|
curEngine = mmgr(QUEUE_LOAD, moduleNum<<8 | RES_TYPE_MODULE)
|
2016-09-08 15:39:41 +00:00
|
|
|
mmgr(FINISH_LOAD, 0)
|
2016-09-22 14:32:54 +00:00
|
|
|
return curEngine() // return function table
|
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
def returnFromEngine(render)#0
|
2016-09-22 14:32:54 +00:00
|
|
|
if curEngine
|
|
|
|
mmgr(FREE_MEMORY, curEngine)
|
|
|
|
curEngine = NULL
|
2017-03-22 16:46:17 +00:00
|
|
|
loadMainFrameImg()
|
2018-03-10 16:31:09 +00:00
|
|
|
if preEnginePortraitNum
|
|
|
|
setPortrait(preEnginePortraitNum)
|
|
|
|
else
|
|
|
|
clearPortrait()
|
|
|
|
fin
|
2019-07-15 17:21:12 +00:00
|
|
|
textureControl(TRUE)
|
2016-09-22 14:32:54 +00:00
|
|
|
mapNameHash = 0; showMapName(global=>s_mapName)
|
2017-08-02 17:20:22 +00:00
|
|
|
clearTextWindow()
|
2017-08-29 16:06:53 +00:00
|
|
|
if render
|
|
|
|
doRender()
|
|
|
|
else
|
|
|
|
needRender = TRUE
|
|
|
|
fin
|
2018-03-17 15:52:29 +00:00
|
|
|
if mapIs3D
|
|
|
|
prevClockColor = 99
|
2019-07-15 17:21:12 +00:00
|
|
|
if util3d
|
|
|
|
util3d=>util3d_showCompassDir(getDir())
|
|
|
|
util3d=>util3d_showClock()
|
|
|
|
fin
|
2018-03-17 15:52:29 +00:00
|
|
|
fin
|
2016-09-22 14:32:54 +00:00
|
|
|
showParty()
|
2016-09-22 17:55:34 +00:00
|
|
|
setWindow2() // in case we're mid-script
|
2016-09-22 14:32:54 +00:00
|
|
|
fin
|
2016-06-19 14:42:11 +00:00
|
|
|
end
|
2015-12-09 15:35:02 +00:00
|
|
|
|
2018-02-24 18:20:01 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Load the automap engine and display the map
|
|
|
|
def showAutomap()#1
|
2018-11-15 17:00:20 +00:00
|
|
|
loadEngine(MOD_AUTOMAP)=>automap_show(9999, 9999) // 9999 = no quest target
|
2018-02-24 18:20:01 +00:00
|
|
|
returnFromEngine(TRUE)
|
|
|
|
return 0
|
|
|
|
end
|
|
|
|
|
2016-12-06 17:38:24 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2016-06-19 14:42:11 +00:00
|
|
|
// Load the Party engine and show data for the given player
|
2017-06-11 15:25:15 +00:00
|
|
|
def showPlayerSheet(num)#1
|
2019-06-14 16:45:15 +00:00
|
|
|
word sNameToUse, oldFlg, pItemutilModule
|
2017-06-24 18:43:16 +00:00
|
|
|
if num+1 > countList(global=>p_players); beep; return 0; fin
|
2018-05-12 14:58:27 +00:00
|
|
|
sNameToUse = loadEngine(MOD_PARTY)=>party_showPlayerSheet(num)
|
2017-04-10 14:27:10 +00:00
|
|
|
returnFromEngine(TRUE)
|
2016-11-04 15:27:22 +00:00
|
|
|
// General 'use' handled here in case it triggers graphical effects
|
2018-05-12 14:58:27 +00:00
|
|
|
if sNameToUse
|
2018-03-06 16:58:47 +00:00
|
|
|
anyInteraction = FALSE
|
2018-05-12 14:58:27 +00:00
|
|
|
scriptEvent(@S_USE, sNameToUse)
|
2018-03-06 16:58:47 +00:00
|
|
|
if !anyInteraction
|
2021-09-04 15:05:13 +00:00
|
|
|
scriptDisplayStr("Nothing happens.")
|
2020-06-20 23:09:20 +00:00
|
|
|
setTextCountdown
|
2017-08-30 16:27:10 +00:00
|
|
|
fin
|
2016-11-04 15:27:22 +00:00
|
|
|
fin
|
2021-09-14 15:24:16 +00:00
|
|
|
playerUsing = NULL
|
2017-06-11 15:25:15 +00:00
|
|
|
return 0
|
2015-12-09 15:35:02 +00:00
|
|
|
end
|
|
|
|
|
2017-06-11 15:25:15 +00:00
|
|
|
def showPlayer1()#1
|
|
|
|
return showPlayerSheet(0)
|
2015-12-09 15:35:02 +00:00
|
|
|
end
|
2017-06-11 15:25:15 +00:00
|
|
|
def showPlayer2()#1
|
|
|
|
return showPlayerSheet(1)
|
2015-12-09 15:35:02 +00:00
|
|
|
end
|
2017-06-11 15:25:15 +00:00
|
|
|
def showPlayer3()#1
|
|
|
|
return showPlayerSheet(2)
|
2015-12-09 18:16:05 +00:00
|
|
|
end
|
|
|
|
|
2017-07-08 23:36:08 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Level up the first character that's applicable
|
|
|
|
def levelUp()#1
|
|
|
|
word player
|
|
|
|
byte n
|
|
|
|
player = global=>p_players
|
|
|
|
n = 0
|
|
|
|
while player
|
|
|
|
if player->b_skillPoints
|
|
|
|
showPlayerSheet(n)
|
|
|
|
return 0
|
|
|
|
fin
|
|
|
|
player = player=>p_nextObj
|
2017-09-03 14:00:30 +00:00
|
|
|
n++
|
2017-07-08 23:36:08 +00:00
|
|
|
loop
|
|
|
|
beep
|
|
|
|
return 0
|
|
|
|
end
|
|
|
|
|
2016-03-29 22:17:42 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-09 14:14:53 +00:00
|
|
|
export def addEncounterZone(code, x, y, dist, chance)#0
|
2016-07-14 20:52:23 +00:00
|
|
|
word p
|
2018-06-07 16:11:07 +00:00
|
|
|
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)
|
2016-03-29 22:17:42 +00:00
|
|
|
end
|
|
|
|
|
2016-07-11 00:18:03 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
export def clearEncounterZones()#0
|
2016-07-11 00:18:03 +00:00
|
|
|
global=>p_encounterZones = NULL
|
|
|
|
end
|
|
|
|
|
2016-02-15 22:43:25 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2016-06-30 15:36:46 +00:00
|
|
|
// Called by user-defined map scripts to initiate a combat encounter.
|
2017-06-10 15:56:23 +00:00
|
|
|
export def scriptCombat(mapCode)#1
|
2016-09-22 17:55:34 +00:00
|
|
|
return doCombat(mapCode, TRUE)
|
2016-06-29 14:42:58 +00:00
|
|
|
end
|
|
|
|
|
2016-07-18 23:47:49 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 14:32:31 +00:00
|
|
|
def playerDeath()#0
|
2018-09-29 17:10:25 +00:00
|
|
|
setWindow2; clearWindow
|
2017-06-02 15:38:31 +00:00
|
|
|
callGlobalFunc(GS_DEATH, 0, 0, 0)
|
2018-03-29 17:12:22 +00:00
|
|
|
startGame(FALSE, FALSE) // don't ask, just load
|
2016-07-18 23:47:49 +00:00
|
|
|
end
|
|
|
|
|
2021-09-07 16:08:28 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
def resumeMapMusic()#0
|
|
|
|
if mapEmuSound > 0 and mapEmuSound <> $FF
|
|
|
|
soundPlayEmu(mapEmuSound)
|
|
|
|
fin
|
|
|
|
end
|
|
|
|
|
2016-06-29 14:42:58 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
def doCombat(mapCode, backUpOnFlee)#1
|
2016-06-30 15:36:46 +00:00
|
|
|
word result
|
2018-04-27 16:23:24 +00:00
|
|
|
|
|
|
|
// Give a clue as to what's happening
|
2018-05-10 15:07:19 +00:00
|
|
|
if needRender; doRender; fin
|
|
|
|
flipToPage1
|
2018-04-27 16:23:24 +00:00
|
|
|
setWindow2()
|
|
|
|
clearWindow()
|
|
|
|
callGlobalFunc(GS_COMBAT_INTRO, 0, 0, 0)
|
|
|
|
|
|
|
|
// Remainder handled in a separate module. Clear enemies out of the heap when finished.
|
2016-12-08 15:05:28 +00:00
|
|
|
result = loadEngine(MOD_COMBAT)=>combat_zoneEncounter(mapCode)
|
2015-12-10 17:43:21 +00:00
|
|
|
global=>p_enemyGroups = NULL
|
2017-09-11 18:16:10 +00:00
|
|
|
heapCollect()
|
2016-07-18 23:47:49 +00:00
|
|
|
|
|
|
|
if (result == -99)
|
2018-09-29 17:10:25 +00:00
|
|
|
q_playerDeath = TRUE
|
2017-06-10 14:32:31 +00:00
|
|
|
return 0
|
2016-07-18 23:47:49 +00:00
|
|
|
fin
|
2017-08-29 16:06:53 +00:00
|
|
|
returnFromEngine(!inScript) // only re-render if outside script
|
2021-09-07 16:08:28 +00:00
|
|
|
resumeMapMusic
|
2016-06-30 16:31:29 +00:00
|
|
|
|
2020-07-07 16:13:59 +00:00
|
|
|
// Advance time a little. It's tricky advancing during combat; this is an ok substitute.
|
|
|
|
advTime(CLOCK_ADV_COMBAT_HOURS, CLOCK_ADV_COMBAT_MINS, CLOCK_ADV_COMBAT_SECS)
|
|
|
|
|
2016-06-30 16:31:29 +00:00
|
|
|
// If the party fled the combat instead of winning, back up to previous square.
|
2016-09-22 17:55:34 +00:00
|
|
|
if !result and backUpOnFlee
|
2016-07-01 23:32:57 +00:00
|
|
|
moveWayBackward()
|
2016-06-30 16:31:29 +00:00
|
|
|
fin
|
2016-06-30 15:36:46 +00:00
|
|
|
return result
|
2015-12-09 15:35:02 +00:00
|
|
|
end
|
|
|
|
|
2016-03-31 17:01:04 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Check for a random encounter at this position
|
2017-06-10 14:32:31 +00:00
|
|
|
export def checkEncounter(x, y, force)#0
|
2016-03-31 17:01:04 +00:00
|
|
|
word p
|
|
|
|
word p_bestZone, bestDist
|
|
|
|
word d
|
|
|
|
|
2017-08-10 17:51:20 +00:00
|
|
|
// Don't check for encounter during scripted move
|
|
|
|
if inScript; return; fin
|
|
|
|
|
2016-03-31 17:01:04 +00:00
|
|
|
// 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))
|
2018-06-07 16:11:07 +00:00
|
|
|
// Using '<=' below so that later-added zones added by a scripted event take precedence
|
|
|
|
if d <= bestDist and (p=>w_encMaxDist == 0 or d <= p=>w_encMaxDist)
|
2016-03-31 17:01:04 +00:00
|
|
|
p_bestZone = p
|
|
|
|
bestDist = d
|
|
|
|
fin
|
|
|
|
p = p=>p_nextObj
|
|
|
|
loop
|
|
|
|
|
|
|
|
// Roll for an encounter in the zone.
|
|
|
|
d = rand16() % 1000
|
2017-07-11 15:56:14 +00:00
|
|
|
if p_bestZone
|
|
|
|
if (d < p_bestZone=>w_encChance or force)
|
|
|
|
// Encounter!
|
|
|
|
doCombat(p_bestZone=>s_name, !force)
|
|
|
|
fin
|
2016-03-31 17:01:04 +00:00
|
|
|
fin
|
|
|
|
end
|
|
|
|
|
2020-08-22 17:50:45 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
export def rwGame(cmd)#0
|
|
|
|
while TRUE
|
|
|
|
if callProRWTS(cmd | RWTS_OPENDIR, @S_GAME1_FILENAME, LOAD_SAVE_BUF, HEAP_SIZE) == 0
|
|
|
|
break
|
|
|
|
fin
|
|
|
|
textHome()
|
|
|
|
^$c051
|
|
|
|
puts("Insert disk 1")
|
|
|
|
rdkey()
|
|
|
|
^$c050
|
|
|
|
loop
|
|
|
|
end
|
|
|
|
|
2016-06-19 15:38:40 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-11 15:25:15 +00:00
|
|
|
def saveGame()#1
|
2020-08-22 17:50:45 +00:00
|
|
|
word cursX, cursY
|
|
|
|
|
|
|
|
cursX, cursY = getCursor()
|
2016-09-22 15:04:24 +00:00
|
|
|
saveMapPos()
|
2017-09-09 17:08:06 +00:00
|
|
|
showMapName("Saving game...")
|
2020-08-22 17:50:45 +00:00
|
|
|
|
|
|
|
textureControl(FALSE) // also flips to page 1
|
|
|
|
|
|
|
|
// Perform garbage collection and record the size of the heap so we can restore it correctly
|
|
|
|
// (also does a CHECK_MEM to be sure we never save corrupted heap)
|
|
|
|
heapCollect()
|
|
|
|
|
|
|
|
//AUTOMAP_CHECK// readDiskMarks; checkMarks
|
|
|
|
|
|
|
|
// Copy data to main memory, and write it out.
|
|
|
|
memcpy(HEAP_BOTTOM, LOAD_SAVE_BUF, HEAP_SIZE, 0) // LC to low mem
|
|
|
|
rwGame(RWTS_WRITE)
|
|
|
|
|
|
|
|
mapNameHash = 0; showMapName(global=>s_mapName)
|
|
|
|
doRender
|
|
|
|
setWindow2()
|
|
|
|
setCursor(cursX, cursY)
|
|
|
|
|
2017-06-11 15:25:15 +00:00
|
|
|
return 0
|
2016-06-19 15:38:40 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-11 15:25:15 +00:00
|
|
|
def loadGame()#1
|
2017-09-03 16:28:46 +00:00
|
|
|
// Since old map may have been modified by scripts, force it to really be gone.
|
|
|
|
if pCurMap
|
|
|
|
mmgr(FREE_MEMORY, pCurMap)
|
|
|
|
mmgr(SET_MEM_TARGET, pCurMap)
|
|
|
|
mmgr(REQUEST_MEMORY, 5)
|
|
|
|
mmgr(FREE_MEMORY, pCurMap)
|
|
|
|
pCurMap = NULL
|
|
|
|
fin
|
2016-12-08 15:05:28 +00:00
|
|
|
loadEngine(MOD_DISKOPS)=>diskops_loadGame()
|
2016-06-19 15:38:40 +00:00
|
|
|
restoreMapPos()
|
2017-06-11 15:25:15 +00:00
|
|
|
return 0
|
2016-06-19 15:38:40 +00:00
|
|
|
end
|
|
|
|
|
2016-07-02 00:19:12 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-11 15:25:15 +00:00
|
|
|
def help()#1
|
2019-06-20 14:38:44 +00:00
|
|
|
flipToPage1
|
|
|
|
setBigWindow; clearWindow
|
2020-08-22 17:50:45 +00:00
|
|
|
forceRawScrDisp = TRUE
|
2017-06-02 15:25:29 +00:00
|
|
|
loadEngine(GS_HELP)()
|
2020-08-22 17:50:45 +00:00
|
|
|
forceRawScrDisp = FALSE
|
2017-04-10 14:27:10 +00:00
|
|
|
returnFromEngine(TRUE)
|
2017-06-11 15:25:15 +00:00
|
|
|
return 0
|
2016-07-02 00:19:12 +00:00
|
|
|
end
|
2016-07-16 17:27:28 +00:00
|
|
|
|
2016-07-02 00:19:12 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-16 18:31:42 +00:00
|
|
|
def toggleGodMode()#1
|
2019-04-24 13:33:18 +00:00
|
|
|
byte key
|
|
|
|
key = getUpperKey
|
|
|
|
if key == 15 // ctrl-O
|
|
|
|
key = getUpperKey
|
2019-07-15 17:21:12 +00:00
|
|
|
if key == 4 // ctrl-D
|
2017-08-14 15:16:38 +00:00
|
|
|
flipToPage1()
|
2017-08-02 17:20:22 +00:00
|
|
|
clearTextWindow()
|
2019-04-24 13:33:18 +00:00
|
|
|
if key == 4 // ctrl-D
|
|
|
|
global->b_godmode = !global->b_godmode
|
|
|
|
displayf1("gm:%d\n", global->b_godmode & 1)
|
|
|
|
initCmds() // rebuild the command table with new commands
|
|
|
|
fin
|
2017-09-12 15:21:23 +00:00
|
|
|
textDrawn = TRUE
|
2017-07-11 15:56:14 +00:00
|
|
|
beep; beep
|
2017-08-02 17:20:22 +00:00
|
|
|
clearTextWindow()
|
2017-02-19 18:08:03 +00:00
|
|
|
fin
|
2016-07-16 17:27:28 +00:00
|
|
|
fin
|
2017-06-24 18:43:16 +00:00
|
|
|
return 0
|
2016-07-16 17:27:28 +00:00
|
|
|
end
|
2017-02-19 18:08:03 +00:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2018-04-20 16:21:52 +00:00
|
|
|
def cheatCmd()#1
|
|
|
|
byte key
|
|
|
|
word pModule
|
|
|
|
|
|
|
|
// We don't use laodEngine, since godmode may use it too, and we'd get double modules
|
2019-07-15 17:21:12 +00:00
|
|
|
textureControl(FALSE) // seems to be necessary for teleport-to-3d to work right
|
|
|
|
flipToPage1
|
2018-04-20 16:21:52 +00:00
|
|
|
mmgr(START_LOAD, 1) // code is in partition 1
|
|
|
|
pModule = mmgr(QUEUE_LOAD, MOD_GODMODE<<8 | RES_TYPE_MODULE)
|
|
|
|
mmgr(FINISH_LOAD, 0)
|
|
|
|
|
2019-12-19 17:38:14 +00:00
|
|
|
pModule()=>godmode_cheatCmd(cmdKey)
|
2018-04-20 16:21:52 +00:00
|
|
|
|
|
|
|
mmgr(FREE_MEMORY, pModule)
|
2019-07-15 17:21:12 +00:00
|
|
|
textureControl(TRUE)
|
2018-04-20 16:21:52 +00:00
|
|
|
return 0
|
2016-07-16 17:27:28 +00:00
|
|
|
end
|
2016-07-02 00:19:12 +00:00
|
|
|
|
2018-04-27 15:03:21 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// This one godmode command needs to not load a module -- print memory without disturbing it
|
2018-04-27 16:23:24 +00:00
|
|
|
export def printMem()#1
|
2018-04-27 15:03:21 +00:00
|
|
|
flipToPage1
|
|
|
|
^$c051
|
|
|
|
mmgr(DEBUG_MEM, 0)
|
|
|
|
rdkey
|
|
|
|
auxMmgr(DEBUG_MEM, 0)
|
|
|
|
rdkey
|
|
|
|
^$c050
|
|
|
|
return 0
|
|
|
|
end
|
|
|
|
|
2018-09-13 15:27:57 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
def showQuests()#1
|
|
|
|
loadEngine(MOD_QUESTLOG)=>questlog_showQuests()
|
|
|
|
returnFromEngine(TRUE) // do render
|
|
|
|
return 0
|
|
|
|
end
|
|
|
|
|
2019-06-27 15:59:17 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
def switchMoveMode()#1
|
|
|
|
byte dir
|
|
|
|
word x, y
|
|
|
|
|
|
|
|
dir = getDir()
|
|
|
|
if global->b_moveMode == 1
|
|
|
|
global->b_moveMode = 2
|
|
|
|
dir = dir & ~1
|
|
|
|
elsif global->b_moveMode == 2
|
|
|
|
global->b_moveMode = 4
|
|
|
|
dir = dir & ~3
|
|
|
|
else
|
|
|
|
global->b_moveMode = 1
|
|
|
|
fin
|
|
|
|
|
|
|
|
setDir(dir)
|
|
|
|
getPos(@x, @y)
|
|
|
|
setPos(x, y) // to round it
|
|
|
|
needRender = TRUE
|
|
|
|
|
2020-07-28 14:31:07 +00:00
|
|
|
flipToPage1()
|
|
|
|
clearTextWindow()
|
2019-06-27 15:59:17 +00:00
|
|
|
showMoveMode
|
|
|
|
|
|
|
|
return 0
|
|
|
|
end
|
|
|
|
|
2015-01-10 21:17:56 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Set up the command table for 3D mode
|
2017-06-10 14:32:31 +00:00
|
|
|
def initCmds()#0
|
2015-04-05 16:52:38 +00:00
|
|
|
|
|
|
|
// Clear the command table
|
|
|
|
byte i
|
2016-06-19 15:38:40 +00:00
|
|
|
for i = 0 to 95
|
2015-04-05 16:52:38 +00:00
|
|
|
cmdTbl[i] = 0
|
|
|
|
next
|
|
|
|
|
2015-05-03 16:25:57 +00:00
|
|
|
// Commands common to both 2D and 3D
|
2016-06-19 15:38:40 +00:00
|
|
|
cmdTbl['1'] = @showPlayer1
|
|
|
|
cmdTbl['2'] = @showPlayer2
|
|
|
|
cmdTbl['3'] = @showPlayer3
|
2017-07-08 23:36:08 +00:00
|
|
|
cmdTbl['U'] = @levelUp
|
2016-06-19 15:38:40 +00:00
|
|
|
cmdTbl[$13] = @saveGame // ctrl-S
|
|
|
|
cmdTbl[$0c] = @loadGame // ctrl-L
|
2016-07-02 00:19:12 +00:00
|
|
|
cmdTbl['?'] = @help
|
2020-08-21 15:06:19 +00:00
|
|
|
cmdTbl['/'] = @help // in case they forget to hit shift
|
2017-07-11 15:56:14 +00:00
|
|
|
cmdTbl[$07] = @toggleGodMode // ctrl-G
|
2018-01-10 15:10:43 +00:00
|
|
|
cmdTbl[' '] = @snooze // "space out" (snooze)
|
2020-08-21 15:06:19 +00:00
|
|
|
cmdTbl['M'] = @showAutomap
|
2019-08-01 15:11:42 +00:00
|
|
|
cmdTbl['Q'] = @showQuests
|
2016-07-16 17:46:41 +00:00
|
|
|
if global->b_godmode
|
2018-04-20 16:21:52 +00:00
|
|
|
// install cheat commands
|
2019-08-01 15:11:42 +00:00
|
|
|
cmdTbl['T'] = @cheatCmd // teleport
|
|
|
|
cmdTbl[$10] = @cheatCmd // ctrl-P: show pos
|
|
|
|
cmdTbl['>'] = @cheatCmd // next portrait
|
|
|
|
cmdTbl['<'] = @cheatCmd // prev portrait
|
|
|
|
cmdTbl['!'] = @cheatCmd // test combat
|
|
|
|
cmdTbl['Y'] = @cheatCmd // next sky
|
|
|
|
cmdTbl['G'] = @cheatCmd // next ground
|
|
|
|
cmdTbl['&'] = @printMem // print mem
|
|
|
|
cmdTbl['^'] = @cheatCmd // edit flags
|
2021-10-10 16:30:40 +00:00
|
|
|
cmdTbl['*'] = @cheatCmd // soundGen test
|
2016-07-16 17:46:41 +00:00
|
|
|
fin
|
2015-05-03 16:25:57 +00:00
|
|
|
|
|
|
|
// Commands handled differently in 3D vs 2D
|
2015-04-05 16:52:38 +00:00
|
|
|
if mapIs3D
|
2019-06-27 15:59:17 +00:00
|
|
|
cmdTbl[3] = @switchMoveMode // ctrl-C
|
|
|
|
|
2016-06-19 15:38:40 +00:00
|
|
|
cmdTbl['W'] = @moveForward
|
2019-07-16 18:03:40 +00:00
|
|
|
cmdTbl['A'] = util3d=>util3d_rotateLeft
|
|
|
|
cmdTbl['D'] = util3d=>util3d_rotateRight
|
|
|
|
cmdTbl['S'] = util3d=>util3d_moveBackward
|
|
|
|
cmdTbl['Z'] = util3d=>util3d_strafeLeft
|
|
|
|
cmdTbl['C'] = util3d=>util3d_strafeRight
|
2016-06-19 15:38:40 +00:00
|
|
|
|
|
|
|
cmdTbl['I'] = @moveForward
|
2019-07-16 18:03:40 +00:00
|
|
|
cmdTbl['J'] = util3d=>util3d_rotateLeft
|
|
|
|
cmdTbl['L'] = util3d=>util3d_rotateRight
|
|
|
|
cmdTbl['K'] = util3d=>util3d_moveBackward
|
2020-08-21 15:06:19 +00:00
|
|
|
cmdTbl['H'] = util3d=>util3d_strafeLeft
|
|
|
|
cmdTbl[';'] = util3d=>util3d_strafeRight
|
2017-05-24 15:51:06 +00:00
|
|
|
|
|
|
|
cmdTbl[11] = @moveForward // up-arrow
|
2019-07-16 18:03:40 +00:00
|
|
|
cmdTbl[8] = util3d=>util3d_rotateLeft // left-arrow
|
|
|
|
cmdTbl[21] = util3d=>util3d_rotateRight // right-arrow
|
|
|
|
cmdTbl[10] = util3d=>util3d_moveBackward // down-arrow
|
2015-04-05 16:52:38 +00:00
|
|
|
else
|
2016-06-19 15:38:40 +00:00
|
|
|
cmdTbl['W'] = @moveNorth
|
|
|
|
cmdTbl['D'] = @moveEast
|
|
|
|
cmdTbl['S'] = @moveSouth
|
|
|
|
cmdTbl['A'] = @moveWest
|
|
|
|
|
|
|
|
cmdTbl['I'] = @moveNorth
|
|
|
|
cmdTbl['J'] = @moveWest
|
|
|
|
cmdTbl['L'] = @moveEast
|
|
|
|
cmdTbl['K'] = @moveSouth
|
2017-05-24 15:51:06 +00:00
|
|
|
|
|
|
|
cmdTbl[11] = @moveNorth // up-arrow
|
|
|
|
cmdTbl[8] = @moveWest // left-arrow
|
|
|
|
cmdTbl[21] = @moveEast // right-arrow
|
|
|
|
cmdTbl[10] = @moveSouth // down-arrow
|
2015-04-09 17:31:36 +00:00
|
|
|
fin
|
2015-01-10 21:17:56 +00:00
|
|
|
end
|
|
|
|
|
2015-09-19 20:27:57 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2016-06-24 12:58:30 +00:00
|
|
|
// Set up the small-object heap. Set loadedSize to zero on initial, or non-zero for loaded game.
|
2017-06-10 15:56:23 +00:00
|
|
|
export def initHeap(loadedSize)#0
|
2015-12-29 19:22:14 +00:00
|
|
|
byte i
|
|
|
|
|
2017-09-20 15:44:35 +00:00
|
|
|
if loadedSize
|
2016-06-24 12:58:30 +00:00
|
|
|
mmgr(SET_MEM_TARGET, HEAP_BOTTOM + loadedSize)
|
|
|
|
fin
|
|
|
|
mmgr(HEAP_SET, HEAP_BOTTOM)
|
2015-12-29 19:22:14 +00:00
|
|
|
i = 0
|
|
|
|
while typeTbls[i]
|
|
|
|
mmgr(HEAP_ADD_TYPE, typeTbls[i])
|
|
|
|
i = i+1
|
|
|
|
loop
|
2017-09-20 15:44:35 +00:00
|
|
|
if loadedSize
|
2016-06-24 13:12:45 +00:00
|
|
|
global = HEAP_BOTTOM
|
|
|
|
else
|
|
|
|
global = mmgr(HEAP_ALLOC, TYPE_GLOBAL)
|
2016-08-05 16:27:11 +00:00
|
|
|
global=>w_typeHash = typeHash
|
2016-06-24 13:12:45 +00:00
|
|
|
fin
|
2015-09-19 20:27:57 +00:00
|
|
|
end
|
|
|
|
|
2016-06-11 17:58:33 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2016-06-19 15:04:43 +00:00
|
|
|
// Constructor: create a modifier given its name and value
|
2017-06-10 15:56:23 +00:00
|
|
|
export def makeModifier(name, value)#1
|
2016-06-12 15:52:24 +00:00
|
|
|
word p; p = mmgr(HEAP_ALLOC, TYPE_MODIFIER)
|
2016-06-12 20:12:14 +00:00
|
|
|
p=>s_name = mmgr(HEAP_INTERN, name)
|
|
|
|
p=>w_modValue = value
|
2016-06-11 17:58:33 +00:00
|
|
|
return p
|
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2016-06-19 15:04:43 +00:00
|
|
|
// Recalculate player's armor score based on their currently equipped armor
|
2017-09-13 15:06:52 +00:00
|
|
|
export def calcPlayerArmor(pl)#0
|
|
|
|
pl->b_armor = sum(pl=>p_items, &(p) p->t_type == TYPE_ARMOR and p->b_flags & ITEM_FLAG_EQUIP, &(p) p->b_armorValue)
|
2016-06-17 00:42:18 +00:00
|
|
|
end
|
|
|
|
|
2016-07-04 16:31:33 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
// Add gold, return the amount
|
|
|
|
export def addGold(amount)#1
|
2020-02-13 16:01:27 +00:00
|
|
|
if amount <= 0; return 0; fin
|
|
|
|
amount = min(amount, GOLD_MAX - global=>w_gold)
|
2016-07-04 17:56:55 +00:00
|
|
|
global=>w_gold = global=>w_gold + amount
|
2016-07-04 16:31:33 +00:00
|
|
|
return amount
|
|
|
|
end
|
|
|
|
|
2017-06-10 15:56:23 +00:00
|
|
|
// Pay out gold, return the amount
|
|
|
|
export def payGold(amount)#1
|
2016-07-04 16:31:33 +00:00
|
|
|
if amount > global=>w_gold
|
2016-07-04 17:56:55 +00:00
|
|
|
return 0
|
2016-07-04 16:31:33 +00:00
|
|
|
fin
|
|
|
|
global=>w_gold = global=>w_gold - amount
|
2016-07-04 17:56:55 +00:00
|
|
|
return amount
|
2016-07-04 16:31:33 +00:00
|
|
|
end
|
|
|
|
|
2016-07-07 23:15:31 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-09-13 15:06:52 +00:00
|
|
|
export def scanForNamedObj(list, name)#1
|
2017-09-14 16:53:01 +00:00
|
|
|
// We don't use lambda here, to preserve context of outer lambdas
|
|
|
|
while list
|
|
|
|
if streqi(list=>s_name, name); break; fin
|
|
|
|
list = list=>p_nextObj
|
|
|
|
loop
|
|
|
|
return list
|
2016-07-07 23:15:31 +00:00
|
|
|
end
|
|
|
|
|
2017-02-20 14:43:47 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2020-01-22 17:10:25 +00:00
|
|
|
export def createStruct(moduleID, creationFuncNum)#1
|
2016-08-17 15:09:14 +00:00
|
|
|
word p_module, funcTbl, func, p_thing
|
2016-07-12 15:09:55 +00:00
|
|
|
|
2016-09-15 18:08:43 +00:00
|
|
|
// Unload textures to make room for the module (also flips to page 1 if needed)
|
2019-07-15 17:21:12 +00:00
|
|
|
textureControl(FALSE)
|
2016-08-31 15:17:02 +00:00
|
|
|
|
2016-08-17 15:09:14 +00:00
|
|
|
// Load the module that is capable of creating the thing
|
2016-07-07 20:59:07 +00:00
|
|
|
mmgr(START_LOAD, 1) // code is in partition 1
|
2016-08-17 15:09:14 +00:00
|
|
|
p_module = mmgr(QUEUE_LOAD, moduleID<<8 | RES_TYPE_MODULE)
|
2016-09-08 15:39:41 +00:00
|
|
|
mmgr(FINISH_LOAD, 0)
|
2016-07-07 20:59:07 +00:00
|
|
|
|
2016-08-17 15:09:14 +00:00
|
|
|
// Figure out which creation function to call there, and create the thing
|
2016-07-07 20:59:07 +00:00
|
|
|
funcTbl = p_module()
|
2016-08-17 15:09:14 +00:00
|
|
|
func = *(funcTbl + creationFuncNum)
|
2017-02-20 14:43:47 +00:00
|
|
|
p_thing = func() // full
|
2016-08-17 15:09:14 +00:00
|
|
|
|
|
|
|
// Finished with the module now.
|
2016-07-07 23:15:31 +00:00
|
|
|
mmgr(FREE_MEMORY, p_module)
|
2016-09-08 16:34:02 +00:00
|
|
|
return p_thing
|
2016-07-07 20:59:07 +00:00
|
|
|
end
|
|
|
|
|
2017-09-12 15:21:23 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
export def roomInPack(p_player)#1
|
2023-07-23 16:44:21 +00:00
|
|
|
// Note: initial pack size is established by gen_players.pla, by code generated in PackPartitions.
|
2017-09-12 15:21:23 +00:00
|
|
|
return countList(p_player=>p_items) < p_player->b_packSize
|
|
|
|
end
|
|
|
|
|
2016-07-07 23:15:31 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-09-14 15:09:21 +00:00
|
|
|
export def partyHasItem(itemName)#1
|
|
|
|
ctx = itemName; return first(global=>p_players, &(p) scanForNamedObj(p=>p_items, ctx))
|
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2020-01-22 17:10:25 +00:00
|
|
|
export def createItem(itemNum)#1
|
2021-09-12 17:05:40 +00:00
|
|
|
word pItem
|
2020-01-22 17:10:25 +00:00
|
|
|
if itemNum <= NUM_WEAPONS
|
2021-09-12 17:05:40 +00:00
|
|
|
pItem = createStruct(MOD_GEN_WEAPONS, (itemNum-1)<<1)
|
|
|
|
elsif itemNum <= NUM_WEAPONS+NUM_ARMORS
|
|
|
|
pItem = createStruct(MOD_GEN_ARMORS, (itemNum-NUM_WEAPONS-1)<<1)
|
|
|
|
else
|
|
|
|
pItem = createStruct(MOD_GEN_MISC_ITEMS, (itemNum-NUM_WEAPONS-NUM_ARMORS-1)<<1)
|
|
|
|
if pItem->t_type == TYPE_FANCY_ITEM
|
|
|
|
pItem=>w_count = pItem=>w_storeAmount
|
|
|
|
fin
|
2020-01-22 17:10:25 +00:00
|
|
|
fin
|
2021-09-12 17:05:40 +00:00
|
|
|
return pItem
|
2020-01-22 17:10:25 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2020-02-13 16:01:27 +00:00
|
|
|
// Returns: 0 = dupe
|
|
|
|
// -1 = no room
|
|
|
|
// 1 = added normally
|
|
|
|
// 2 = override-added quest item
|
|
|
|
// 3 = combined with existing stackable
|
|
|
|
export def fullAddItem(pItem, doit)#1
|
|
|
|
word pPlayer, pComp, addToPlayer
|
|
|
|
pPlayer = global=>p_players
|
|
|
|
addToPlayer = NULL
|
|
|
|
while pPlayer
|
|
|
|
pComp = scanForNamedObj(pPlayer=>p_items, pItem=>s_name)
|
|
|
|
if pComp
|
|
|
|
if pComp->t_type == TYPE_FANCY_ITEM and pComp=>w_count > 0
|
|
|
|
pComp=>w_count = min(30000, pComp=>w_count + pItem=>w_count)
|
|
|
|
return 3 // combined
|
|
|
|
fin
|
|
|
|
if !pComp=>w_price
|
|
|
|
return 0 // dupe quest item
|
|
|
|
fin
|
2017-09-14 15:09:21 +00:00
|
|
|
fin
|
2020-02-13 16:01:27 +00:00
|
|
|
if !addToPlayer and roomInPack(pPlayer); addToPlayer = pPlayer; fin
|
|
|
|
pPlayer = pPlayer=>p_nextObj
|
|
|
|
loop
|
|
|
|
|
|
|
|
if addToPlayer
|
|
|
|
if doit; addToList(@addToPlayer=>p_items, pItem); fin
|
|
|
|
return 1
|
2017-09-14 15:09:21 +00:00
|
|
|
fin
|
|
|
|
|
2020-02-13 16:01:27 +00:00
|
|
|
if pItem=>w_price
|
|
|
|
return -1 // no room, and item has a price so it's not a quest item
|
2017-09-14 15:09:21 +00:00
|
|
|
fin
|
2020-02-13 16:01:27 +00:00
|
|
|
|
|
|
|
// Override: quest items allowed to exceed pack limitations
|
|
|
|
if doit; addToList(@global=>p_players=>p_items, pItem); fin
|
|
|
|
return 2
|
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
export def giveItemToParty(p_item, displayFunc)#0
|
|
|
|
when fullAddItem(p_item, TRUE)
|
|
|
|
is 0
|
|
|
|
displayFunc("Duplicate item.\n")#0
|
|
|
|
beep
|
|
|
|
break
|
|
|
|
is -1
|
|
|
|
displayFunc(sprintf1("\nYou have no room; %s dropped.\n", p_item=>s_name))#0
|
|
|
|
beep
|
|
|
|
break
|
|
|
|
otherwise
|
|
|
|
heapCollect
|
|
|
|
wend
|
2016-08-17 15:09:14 +00:00
|
|
|
end
|
|
|
|
|
2017-06-24 20:28:28 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2021-09-02 16:22:17 +00:00
|
|
|
// Add XP to one player, leveling them up as appropriate
|
|
|
|
def addXP(player, val)#0
|
2018-03-06 16:58:47 +00:00
|
|
|
word n
|
|
|
|
if val < 0; return; fin
|
2021-09-02 16:22:17 +00:00
|
|
|
// Add the value plus an intelligence-based bonus
|
|
|
|
player=>w_curXP = player=>w_curXP + val + addPercent(val, 10 * player->b_intelligence)
|
2021-09-09 17:26:48 +00:00
|
|
|
// Enforce cap on number of points to stay within limit of 15 bits
|
|
|
|
if player=>w_curXP < 0 or player=>w_curXP >= 32760 // goes neg if wrapped around
|
|
|
|
player=>w_curXP = 32760
|
2018-03-06 16:58:47 +00:00
|
|
|
fin
|
2021-09-09 17:26:48 +00:00
|
|
|
while player=>w_curXP >= player=>w_nextXP and player=>w_curXP < 32761
|
2018-03-06 16:58:47 +00:00
|
|
|
// Level up!
|
|
|
|
player->b_level++
|
|
|
|
player->b_skillPoints = player->b_skillPoints + callGlobalFunc(GS_LEVEL_S_P, player->b_level, 0, 0)
|
2021-08-05 16:47:25 +00:00
|
|
|
player=>w_maxHealth = player=>w_maxHealth + ((player->b_stamina+1)>>1) + rollDice($2600) // roundup(stam/2) + 2d6
|
2018-03-06 16:58:47 +00:00
|
|
|
player=>w_health = player=>w_maxHealth // let's make leveling up an extra nice thing
|
|
|
|
needShowParty = TRUE
|
|
|
|
|
|
|
|
// Check XP for next level, and enforce level cap if any
|
|
|
|
n = callGlobalFunc(GS_LEVEL_X_P, player->b_level + 1, 0, 0)
|
2021-09-09 17:26:48 +00:00
|
|
|
if n > player=>w_nextXP
|
2018-03-06 16:58:47 +00:00
|
|
|
player=>w_nextXP = n
|
|
|
|
else
|
2021-09-09 17:26:48 +00:00
|
|
|
player=>w_nextXP = 32767 // XP cap reached
|
2018-03-06 16:58:47 +00:00
|
|
|
break
|
2017-06-24 20:28:28 +00:00
|
|
|
fin
|
|
|
|
loop
|
2018-03-06 16:58:47 +00:00
|
|
|
end
|
|
|
|
|
2017-07-08 23:36:08 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2018-09-13 15:27:57 +00:00
|
|
|
// Initialize XP (and skill pts) for newly created character (called by packer code)
|
2017-07-08 23:36:08 +00:00
|
|
|
export def initPlayerXP(player)#0
|
|
|
|
player->b_skillPoints = callGlobalFunc(GS_LEVEL_S_P, player->b_level, 0, 0)
|
|
|
|
player=>w_curXP = callGlobalFunc(GS_LEVEL_X_P, player->b_level, 0, 0)
|
|
|
|
player=>w_nextXP = callGlobalFunc(GS_LEVEL_X_P, player->b_level + 1, 0, 0)
|
|
|
|
end
|
|
|
|
|
2017-09-13 15:06:52 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
export def equipItem(item)#0
|
2017-09-13 15:29:44 +00:00
|
|
|
if item
|
|
|
|
item->b_flags = item->b_flags | ITEM_FLAG_EQUIP
|
|
|
|
fin
|
2017-09-13 15:06:52 +00:00
|
|
|
end
|
|
|
|
|
2017-07-11 15:56:14 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Arm newly created player (or NPC) with first weapon and all armor.
|
|
|
|
export def girdPlayer(player)#0
|
2017-09-13 15:06:52 +00:00
|
|
|
equipItem(first(player=>p_items, &(p) p->t_type == TYPE_WEAPON))
|
|
|
|
forSome(player=>p_items, &(p) p->t_type == TYPE_ARMOR, @equipItem)
|
2017-07-11 15:56:14 +00:00
|
|
|
calcPlayerArmor(player)
|
|
|
|
end
|
|
|
|
|
2016-08-17 15:09:14 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-09-14 16:53:01 +00:00
|
|
|
export def addPlayerToParty(playerFuncNum, displayFunc)#0
|
2017-09-14 15:09:21 +00:00
|
|
|
word p_player
|
|
|
|
|
2016-11-10 16:40:50 +00:00
|
|
|
if countList(global=>p_players) == MAX_PARTY
|
2017-09-14 16:53:01 +00:00
|
|
|
displayFunc("Party too large.\n")#0
|
2017-09-02 16:04:00 +00:00
|
|
|
beep
|
2016-11-10 16:40:50 +00:00
|
|
|
return
|
|
|
|
fin
|
2017-09-14 15:09:21 +00:00
|
|
|
|
2017-09-12 15:21:23 +00:00
|
|
|
// Create the player (NPC flag will be established inside the creation func)
|
2020-01-22 17:10:25 +00:00
|
|
|
p_player = createStruct(MOD_GEN_PLAYERS, playerFuncNum)
|
2017-09-14 15:09:21 +00:00
|
|
|
|
|
|
|
// Add if not dupe
|
|
|
|
if !scanForNamedObj(global=>p_players, p_player=>s_name)
|
|
|
|
addToList(@global=>p_players, p_player)
|
2021-09-02 16:22:17 +00:00
|
|
|
adjustPartyStat(@S_XP, 0) // to set initial skill pts
|
2017-09-14 16:53:01 +00:00
|
|
|
heapCollect
|
2017-09-14 15:09:21 +00:00
|
|
|
needShowParty = TRUE
|
|
|
|
fin
|
2016-08-17 15:09:14 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-09-14 15:09:21 +00:00
|
|
|
export def removeNamed(name, pList)#1
|
2016-08-17 15:09:14 +00:00
|
|
|
word p_thing
|
|
|
|
p_thing = scanForNamedObj(*pList, name)
|
|
|
|
if p_thing
|
2017-08-30 15:50:28 +00:00
|
|
|
// If it's stackable and there's more than one, just reduce the count. Otherwise take it all.
|
|
|
|
if p_thing->t_type == TYPE_FANCY_ITEM and p_thing=>w_count > 1
|
|
|
|
p_thing=>w_count--
|
|
|
|
else
|
|
|
|
removeFromList(pList, p_thing)
|
|
|
|
fin
|
2017-09-03 16:28:46 +00:00
|
|
|
return TRUE
|
2016-07-07 20:59:07 +00:00
|
|
|
fin
|
2017-09-03 16:28:46 +00:00
|
|
|
return FALSE
|
2016-07-07 20:59:07 +00:00
|
|
|
end
|
|
|
|
|
2016-08-17 15:09:14 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-09-14 15:09:21 +00:00
|
|
|
export def takeItemFromParty(itemName)#0
|
2017-09-13 15:06:52 +00:00
|
|
|
ctx = itemName; first(global=>p_players, &(p) removeNamed(ctx, @p=>p_items))
|
2016-08-17 15:09:14 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
export def removePlayerFromParty(playerName)#0
|
2016-08-17 15:09:14 +00:00
|
|
|
removeNamed(playerName, @global=>p_players)
|
2016-08-22 15:08:22 +00:00
|
|
|
needShowParty = TRUE
|
2016-08-17 15:09:14 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
export def partyHasPlayer(playerName)#1
|
2017-09-13 15:06:52 +00:00
|
|
|
return scanForNamedObj(global=>p_players, playerName)
|
2016-08-17 15:09:14 +00:00
|
|
|
end
|
|
|
|
|
2016-07-07 23:15:31 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
export def getStat(player, statName)#1
|
2017-08-06 18:23:40 +00:00
|
|
|
word pSkill
|
2017-08-30 16:27:10 +00:00
|
|
|
when 1
|
|
|
|
is streqi(statName, @S_INTELLIGENCE); return player->b_intelligence
|
|
|
|
is streqi(statName, @S_STRENGTH); return player->b_strength
|
|
|
|
is streqi(statName, @S_AGILITY); return player->b_agility
|
|
|
|
is streqi(statName, @S_STAMINA); return player->b_stamina
|
|
|
|
is streqi(statName, @S_CHARISMA); return player->b_charisma
|
|
|
|
is streqi(statName, @S_SPIRIT); return player->b_spirit
|
|
|
|
is streqi(statName, @S_LUCK); return player->b_luck
|
|
|
|
is streqi(statName, @S_HEALTH); return player=>w_health
|
|
|
|
is streqi(statName, @S_MAX_HEALTH); return player=>w_maxHealth
|
|
|
|
is streqi(statName, @S_AIMING); return player->b_aiming
|
|
|
|
is streqi(statName, @S_HAND_TO_HAND); return player->b_handToHand
|
|
|
|
is streqi(statName, @S_DODGING); return player->b_dodging
|
2021-09-03 15:46:24 +00:00
|
|
|
is streqi(statName, @S_GOLD); return global=>w_gold
|
|
|
|
is streqi(statName, @S_TIME); return global->b_hour
|
2021-09-04 15:05:13 +00:00
|
|
|
is streqi(statName, @S_XP); return player=>w_curXP
|
2017-08-30 16:27:10 +00:00
|
|
|
is streqi(statName, @S_SP); return player->b_skillPoints
|
2017-09-12 15:21:23 +00:00
|
|
|
is streqi(statName, @S_PACK_SIZE); return player->b_packSize
|
2021-09-03 15:46:24 +00:00
|
|
|
is streqi(statName, @S_BANK_BAL); return global=>w_bankBal
|
2016-07-07 21:22:48 +00:00
|
|
|
wend
|
2017-09-13 15:06:52 +00:00
|
|
|
pSkill = scanForNamedObj(player=>p_skills, statName)
|
|
|
|
if pSkill; return pSkill=>w_modValue; fin
|
2017-08-30 16:27:10 +00:00
|
|
|
puts(statName); return fatal("getStat")
|
2016-07-07 20:59:07 +00:00
|
|
|
end
|
|
|
|
|
2017-07-15 18:41:29 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Return an appropriate pronoun for the given gender (M/F/N...)
|
|
|
|
export def hisHerTheir(gender)#1
|
|
|
|
if gender == 'M'; return @S_HIS; fin
|
|
|
|
if gender == 'F'; return @S_HER; fin
|
|
|
|
return @S_THEIR
|
|
|
|
end
|
|
|
|
|
2016-07-07 23:15:31 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2021-10-10 16:30:40 +00:00
|
|
|
export def clampByte(val)#1
|
2016-07-07 23:15:31 +00:00
|
|
|
return max(0, min(255, val))
|
|
|
|
end
|
|
|
|
|
2018-01-16 17:15:52 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
def setHour(val) #0
|
|
|
|
val = val % 24 // clamp to range 0..23
|
|
|
|
if val <> global->b_hour or global->b_minute
|
|
|
|
if global->b_hour >= val; val = val + 24; fin // to ensure positive difference
|
|
|
|
advTime(val - global->b_hour - 1, 59 - global->b_minute, 60 - global->b_second)
|
|
|
|
fin
|
|
|
|
end
|
|
|
|
|
2021-09-02 16:22:17 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Adjust the given stat for every (living) player in the party
|
|
|
|
export def adjustPartyStat(statName, val)#0
|
|
|
|
word p_player
|
|
|
|
when 1
|
|
|
|
is streqi(statName, @S_GOLD); global=>w_gold = max(0, global=>w_gold + val); needShowParty = TRUE; break
|
|
|
|
is streqi(statName, @S_TIME); setHour(global->b_hour + val); break;
|
|
|
|
is streqi(statName, @S_BANK_BAL); global=>w_bankBal = global=>w_bankBal + val; break
|
|
|
|
otherwise
|
|
|
|
p_player = global=>p_players
|
|
|
|
while p_player
|
|
|
|
if p_player=>w_health > 0
|
|
|
|
setStat(p_player, statName, getStat(p_player, statName) + val)
|
|
|
|
fin
|
|
|
|
p_player = p_player=>p_nextObj
|
|
|
|
loop
|
|
|
|
wend
|
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2021-09-14 15:24:16 +00:00
|
|
|
// Get the max value of the stat for any (living) character in the party.
|
|
|
|
// Except if a particular player is using a skill or item in which case it's their stat only.
|
|
|
|
export def getStatInContext(statName)#1
|
2021-09-02 16:22:17 +00:00
|
|
|
word p_player, val
|
|
|
|
val = 0
|
|
|
|
when 1
|
|
|
|
is streqi(statName, @S_GOLD); return global=>w_gold
|
|
|
|
is streqi(statName, @S_TIME); return global->b_hour
|
|
|
|
is streqi(statName, @S_BANK_BAL); return global=>w_bankBal
|
2021-09-14 15:24:16 +00:00
|
|
|
is playerUsing <> NULL; return getStat(playerUsing, statName)
|
2021-09-02 16:22:17 +00:00
|
|
|
otherwise
|
|
|
|
p_player = global=>p_players
|
|
|
|
while p_player
|
|
|
|
val = max(val, getStat(p_player, statName))
|
|
|
|
p_player = p_player=>p_nextObj
|
|
|
|
loop
|
|
|
|
wend
|
|
|
|
return val
|
|
|
|
end
|
|
|
|
|
2016-07-07 23:15:31 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 14:32:31 +00:00
|
|
|
export def setStat(player, statName, val)#0
|
2017-08-06 18:23:40 +00:00
|
|
|
word pSkill
|
2017-08-30 16:27:10 +00:00
|
|
|
when 1
|
|
|
|
is streqi(statName, @S_INTELLIGENCE); player->b_intelligence = clampByte(val); break
|
|
|
|
is streqi(statName, @S_STRENGTH); player->b_strength = clampByte(val); break
|
|
|
|
is streqi(statName, @S_AGILITY); player->b_agility = clampByte(val); break
|
|
|
|
is streqi(statName, @S_STAMINA); player->b_stamina = clampByte(val); break
|
|
|
|
is streqi(statName, @S_CHARISMA); player->b_charisma = clampByte(val); break
|
|
|
|
is streqi(statName, @S_SPIRIT); player->b_spirit = clampByte(val); break
|
|
|
|
is streqi(statName, @S_LUCK); player->b_luck = clampByte(val); break
|
2018-02-26 14:45:15 +00:00
|
|
|
is streqi(statName, @S_HEALTH); player=>w_health = max(0, min(player=>w_maxHealth, val))
|
|
|
|
needShowParty = TRUE; break
|
2017-08-30 16:27:10 +00:00
|
|
|
is streqi(statName, @S_MAX_HEALTH); player=>w_maxHealth = max(0, val); break
|
|
|
|
is streqi(statName, @S_AIMING); player->b_aiming = clampByte(val); break
|
|
|
|
is streqi(statName, @S_HAND_TO_HAND); player->b_handToHand = clampByte(val); break
|
|
|
|
is streqi(statName, @S_DODGING); player->b_dodging = clampByte(val); break
|
2021-09-03 15:46:24 +00:00
|
|
|
is streqi(statName, @S_GOLD); global=>w_gold = max(0, val); needShowParty = TRUE; break
|
|
|
|
is streqi(statName, @S_TIME); setHour(val); break;
|
2018-03-06 16:58:47 +00:00
|
|
|
is streqi(statName, @S_XP); addXP(player, val - player=>w_curXP); needShowParty = TRUE; break
|
2017-09-12 15:21:23 +00:00
|
|
|
is streqi(statName, @S_SP); player->b_skillPoints = clampByte(max(0, val)); needShowParty = TRUE; break
|
|
|
|
is streqi(statName, @S_PACK_SIZE); player->b_packSize = clampByte(max(player->b_packSize, val)); break
|
2021-09-03 15:46:24 +00:00
|
|
|
is streqi(statName, @S_BANK_BAL); global=>w_bankBal = val; break
|
2016-07-07 23:15:31 +00:00
|
|
|
otherwise
|
2017-09-13 15:06:52 +00:00
|
|
|
pSkill = scanForNamedObj(player=>p_skills, statName)
|
|
|
|
if pSkill
|
|
|
|
pSkill=>w_modValue = max(0, val)
|
|
|
|
else
|
|
|
|
puts(statName); fatal("setStat")
|
|
|
|
fin
|
2016-07-07 23:15:31 +00:00
|
|
|
wend
|
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-08-29 16:06:53 +00:00
|
|
|
export def setGameFlag(flagNum, val)#0
|
|
|
|
byte byteNum, mask
|
|
|
|
byteNum = flagNum >> 3
|
|
|
|
mask = 1<<(flagNum & 7)
|
|
|
|
if val
|
|
|
|
global->ba_gameFlags[byteNum] = global->ba_gameFlags[byteNum] | mask
|
|
|
|
else
|
|
|
|
global->ba_gameFlags[byteNum] = global->ba_gameFlags[byteNum] & ~mask
|
2016-07-07 23:15:31 +00:00
|
|
|
fin
|
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-08-29 16:06:53 +00:00
|
|
|
export def getGameFlag(flagNum)#1
|
|
|
|
byte byteNum, mask
|
|
|
|
byteNum = flagNum >> 3
|
|
|
|
mask = 1<<(flagNum & 7)
|
|
|
|
return global->ba_gameFlags[byteNum] & mask
|
2016-07-07 23:15:31 +00:00
|
|
|
end
|
|
|
|
|
2019-01-10 17:45:40 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
export def setStoryMode(enable)#0
|
2020-06-15 14:21:37 +00:00
|
|
|
// Story mode only exists on 800K or hard drive builds, and not the disk-limited ones.
|
|
|
|
if !isFloppyVer and !diskLimit
|
2019-01-24 16:52:02 +00:00
|
|
|
storyMode = enable
|
|
|
|
if enable
|
|
|
|
loadEngine(MOD_STORY)
|
|
|
|
curEngine()=>story_mode(TRUE, preEnginePortraitNum)
|
|
|
|
needRender = FALSE
|
|
|
|
frameLoaded = 0 // since we just destroyed it
|
|
|
|
textDrawn = FALSE
|
|
|
|
textClearCountdown = 0
|
|
|
|
else
|
|
|
|
curEngine()=>story_mode(FALSE, 0)
|
|
|
|
preEnginePortraitNum = 0 // so we don't restore it
|
|
|
|
returnFromEngine(TRUE)
|
|
|
|
fin
|
|
|
|
fin
|
2019-01-10 17:45:40 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2019-01-24 16:52:02 +00:00
|
|
|
export def displayStory(storyNum)#0
|
|
|
|
curEngine()=>story_display(storyNum)
|
2019-01-10 17:45:40 +00:00
|
|
|
end
|
|
|
|
|
2016-11-10 16:40:50 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
export def benchPlayer()#0
|
2016-12-08 15:05:28 +00:00
|
|
|
loadEngine(MOD_PARTY)=>party_benchPlayer()
|
2017-04-10 14:27:10 +00:00
|
|
|
returnFromEngine(TRUE)
|
2016-11-10 16:40:50 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
export def unbenchPlayer()#0
|
2016-12-08 15:05:28 +00:00
|
|
|
loadEngine(MOD_PARTY)=>party_unbenchPlayer()
|
2017-04-10 14:27:10 +00:00
|
|
|
returnFromEngine(TRUE)
|
2016-11-10 16:40:50 +00:00
|
|
|
end
|
|
|
|
|
2017-03-04 16:20:01 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2017-06-10 15:56:23 +00:00
|
|
|
export def buySell(storeCode, profitRatio)#0
|
2017-04-03 15:22:32 +00:00
|
|
|
if storeCode
|
2017-04-10 14:27:10 +00:00
|
|
|
loadEngine(MOD_STORE)=>store_buyFromStore(storeCode, profitRatio)
|
2017-04-03 15:22:32 +00:00
|
|
|
else
|
2017-04-10 14:27:10 +00:00
|
|
|
loadEngine(MOD_STORE)=>store_sellToStore(profitRatio)
|
2017-04-03 15:22:32 +00:00
|
|
|
fin
|
2017-04-10 14:27:10 +00:00
|
|
|
returnFromEngine(FALSE) // no render, we're mid-script
|
2017-03-04 16:20:01 +00:00
|
|
|
end
|
|
|
|
|
2015-12-29 19:22:14 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2018-03-29 17:12:22 +00:00
|
|
|
def startGame(firstTime, ask)#0
|
2016-06-24 12:58:30 +00:00
|
|
|
word p_module
|
2016-03-24 16:45:07 +00:00
|
|
|
|
2018-03-15 15:02:21 +00:00
|
|
|
typeHash = hashBuffer(@typeTbl_TGlobal, @typeTbls - @typeTbl_TGlobal) ^ HEAP_BOTTOM
|
2017-08-14 15:01:31 +00:00
|
|
|
|
2016-06-24 12:58:30 +00:00
|
|
|
// Create a new game or load an existing one
|
|
|
|
mmgr(START_LOAD, 1) // code is in partition 1
|
2018-03-29 17:12:22 +00:00
|
|
|
if firstTime
|
2020-06-07 17:14:43 +00:00
|
|
|
// Temporarily reserve a big chunk of aux mem to keep diskops bytecode out
|
2018-04-26 16:19:33 +00:00
|
|
|
auxMmgr(SET_MEM_TARGET, $800) // well above where resource index loads at startup
|
2020-06-07 17:14:43 +00:00
|
|
|
auxMmgr(REQUEST_MEMORY, $7F00) // plenty of room for resource index + temp expander
|
|
|
|
// Reserve space for the small-object heap
|
2018-04-27 15:03:21 +00:00
|
|
|
mmgr(SET_MEM_TARGET, HEAP_BOTTOM)
|
|
|
|
mmgr(REQUEST_MEMORY, HEAP_SIZE)
|
|
|
|
mmgr(LOCK_MEMORY, HEAP_BOTTOM)
|
2018-03-29 17:12:22 +00:00
|
|
|
fin
|
2016-12-08 15:05:28 +00:00
|
|
|
p_module = mmgr(QUEUE_LOAD, MOD_DISKOPS<<8 | RES_TYPE_MODULE)
|
2016-09-08 15:39:41 +00:00
|
|
|
mmgr(FINISH_LOAD, 0)
|
2018-09-29 17:10:25 +00:00
|
|
|
q_playerDeath = FALSE
|
2018-03-29 17:12:22 +00:00
|
|
|
if firstTime; p_module()=>diskops_startup(); fin
|
2016-07-18 23:47:49 +00:00
|
|
|
if p_module()=>diskops_newOrLoadGame(ask)
|
2016-06-24 12:58:30 +00:00
|
|
|
mapIs3D = q_mapIs3D
|
|
|
|
mapNum = q_mapNum
|
|
|
|
q_mapNum = 0
|
|
|
|
initMap(q_x, q_y, q_dir)
|
2017-09-09 17:08:06 +00:00
|
|
|
saveGame()
|
2020-07-28 14:31:07 +00:00
|
|
|
setTextCountdown
|
2016-06-24 12:58:30 +00:00
|
|
|
else
|
2016-06-24 14:16:08 +00:00
|
|
|
q_mapNum = 0
|
2016-06-24 12:58:30 +00:00
|
|
|
restoreMapPos()
|
|
|
|
fin
|
2015-12-29 19:22:14 +00:00
|
|
|
end
|
|
|
|
|
2014-08-21 15:17:50 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Main code.
|
|
|
|
//
|
2017-09-19 15:49:00 +00:00
|
|
|
scriptDisplayStr(@_scriptDisplayStr) // 1-time init
|
2018-03-29 17:12:22 +00:00
|
|
|
startGame(TRUE, TRUE) // first time init; ask whether new or load
|
2014-07-06 23:02:52 +00:00
|
|
|
kbdLoop()
|
|
|
|
|
2016-07-12 15:09:55 +00:00
|
|
|
done
|