├── release └── scummnext.nex ├── .gitignore ├── scripts ├── constants.py ├── utils.py ├── inv_all.py ├── rooms.py ├── tables.py ├── room0.py └── tiles.py ├── project.lst ├── scummnext1.sh ├── convert.sh ├── room.h ├── costume.h ├── cursor.h ├── sprites.h ├── string.h ├── make.sh ├── engine.h ├── helper.h ├── camera.h ├── scummnext.sh ├── zpragma.inc ├── verbs.h ├── debug.h ├── box.h ├── Makefile ├── resource.h ├── verbs.c ├── README.md ├── string.c ├── script.h ├── actor.h ├── cursor.c ├── object.h ├── resource.c ├── mem.asm ├── camera.c ├── debug.c ├── engine.c ├── main.c ├── graphics.h ├── sprites.c ├── room.c ├── box.c ├── costume.c ├── actor.c ├── LICENSE ├── object.c ├── graphics.c └── script.c /release/scummnext.nex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dovgalyuk/scummnext/HEAD/release/scummnext.nex -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.bin 2 | *.nex 3 | *.map 4 | MM/* 5 | *.o 6 | *.lis 7 | zcc_opt.def 8 | gen/* 9 | __pycache__ -------------------------------------------------------------------------------- /scripts/constants.py: -------------------------------------------------------------------------------- 1 | numGlobalObjects = 775 2 | numRooms = 55 3 | numCostumes = 80 4 | numScripts = 200 5 | -------------------------------------------------------------------------------- /project.lst: -------------------------------------------------------------------------------- 1 | main.c 2 | engine.c 3 | script.c 4 | debug.c 5 | resource.c 6 | graphics.c 7 | actor.c 8 | room.c 9 | camera.c 10 | -------------------------------------------------------------------------------- /scummnext1.sh: -------------------------------------------------------------------------------- 1 | ../zesarux/src/zesarux --machine tbblue --quickexit --verbose 0 --enable-esxdos-handler --hardware-debug-ports --nowelcomemessage scummnext.nex 2 | -------------------------------------------------------------------------------- /convert.sh: -------------------------------------------------------------------------------- 1 | rm gen/* 2 | python3 scripts/inv_all.py MM gen 3 | python3 scripts/room0.py gen 4 | python3 scripts/tables.py gen 5 | python3 scripts/tiles.py gen 6 | python3 scripts/rooms.py gen 7 | -------------------------------------------------------------------------------- /room.h: -------------------------------------------------------------------------------- 1 | #ifndef ROOM_H 2 | #define ROOM_H 3 | 4 | #include 5 | 6 | extern uint8_t currentRoom; 7 | extern uint8_t roomWidth; 8 | 9 | void startScene(uint8_t room); 10 | 11 | #endif -------------------------------------------------------------------------------- /costume.h: -------------------------------------------------------------------------------- 1 | #ifndef COSTUME_H 2 | #define COSTUME_H 3 | 4 | #include "actor.h" 5 | 6 | void costume_loadData(void); 7 | void costume_updateAll(void); 8 | void costume_updateActor(Actor *act); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /cursor.h: -------------------------------------------------------------------------------- 1 | #ifndef CURSOR_H 2 | #define CURSOR_H 3 | 4 | #include 5 | 6 | extern uint16_t cursorX; 7 | extern uint16_t cursorY; 8 | 9 | void cursor_setState(uint8_t s); 10 | void cursor_animate(void); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /sprites.h: -------------------------------------------------------------------------------- 1 | #ifndef SPRITES_H 2 | #define SPRITES_H 3 | 4 | void decodeSpriteTiles(uint8_t set); 5 | void decodeTiles(uint8_t set); 6 | void graphics_loadSpritePattern(uint8_t nextSprite, uint8_t tile, uint8_t mask, uint8_t sprpal); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /string.h: -------------------------------------------------------------------------------- 1 | #ifndef STRING_H 2 | #define STRING_H 3 | 4 | #include 5 | 6 | extern int8_t talkDelay; 7 | 8 | uint8_t *message_new(void); 9 | void message_print(uint8_t *m, uint8_t c); 10 | void messages_update(void); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /make.sh: -------------------------------------------------------------------------------- 1 | export PATH=${PATH}:${HOME}/z88dk/bin 2 | export ZCCCFG=${HOME}/z88dk/lib/config 3 | 4 | #zcc +zxn -m -v -startup=1 -SO3 -clib=sdcc_iy --max-allocs-per-node200000 -subtype=nex -create-app @project.lst -o scummnext -pragma-include:zpragma.inc 5 | make -j 4 6 | -------------------------------------------------------------------------------- /scripts/utils.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | def readByte(fin): 4 | return int.from_bytes(fin.read(1), "little") 5 | 6 | def readWord(fin): 7 | return int.from_bytes(fin.read(2), "little") 8 | 9 | def openCostume(path, cost): 10 | return open("%s/%03d.cost" % (path, cost), "rb") 11 | -------------------------------------------------------------------------------- /engine.h: -------------------------------------------------------------------------------- 1 | #ifndef ENGINE_H 2 | #define ENGINE_H 3 | 4 | #include 5 | 6 | uint8_t strcopy(char *d, const char *s); 7 | 8 | uint8_t getObjOrActorName(char *s, uint16_t id); 9 | int8_t getObjectOrActorXY(uint16_t object, uint8_t *x, uint8_t *y); 10 | uint16_t getObjActToObjActDist(uint16_t a, uint16_t b); 11 | 12 | #endif -------------------------------------------------------------------------------- /helper.h: -------------------------------------------------------------------------------- 1 | #ifndef HELPER_H 2 | #define HELPER_H 3 | 4 | #define ABS(x) ((x) < 0 ? -(x) : (x)) 5 | #define MAX(a, b) ((a) < (b) ? (b) : (a)) 6 | 7 | #define PUSH_PAGE(p, v) \ 8 | uint8_t page##p = ZXN_READ_MMU##p(); \ 9 | ZXN_WRITE_MMU##p(v); \ 10 | 11 | #define POP_PAGE(p) ZXN_WRITE_MMU##p(page##p); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /camera.h: -------------------------------------------------------------------------------- 1 | #ifndef CAMERA_H 2 | #define CAMERA_H 3 | 4 | #include 5 | 6 | extern uint8_t cameraX; 7 | extern uint8_t cameraMode; 8 | 9 | void camera_move(void); 10 | 11 | void camera_setX(uint8_t x); 12 | void camera_panTo(uint8_t x); 13 | void camera_followActor(uint8_t actor); 14 | uint8_t camera_getVirtScreenX(void); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /scummnext.sh: -------------------------------------------------------------------------------- 1 | ../zesarux-origin/src/zesarux --machine tbblue --verbose 0 --ao null --enable-esxdos-handler --hardware-debug-ports --nowelcomemessage scummnext.nex \ 2 | # --debugregisters \ 3 | 4 | # --enable-breakpoints --set-breakpoint 1 "PC=90A2h" --set-breakpointaction 1 "printe PEEK(0D467h)" 5 | #--enable-breakpoints --set-breakpoint n s --set-breakpointaction n a 6 | #-- 91D4 7 | -------------------------------------------------------------------------------- /scripts/inv_all.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | dir_in = sys.argv[1] 4 | dir_out = sys.argv[2] 5 | 6 | for i in range(0,55): 7 | name_in = "%s/%02d.LFL" % (dir_in, i) 8 | name_out = "%s/%02d.out" % (dir_out, i) 9 | fin = open(name_in, "rb") 10 | fout = open(name_out, "wb") 11 | byte = fin.read(1) 12 | while byte != b"": 13 | fout.write((255 - byte[0]).to_bytes(1, "big")) 14 | byte = fin.read(1) 15 | fin.close() 16 | fout.close() 17 | -------------------------------------------------------------------------------- /scripts/rooms.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import utils 3 | import constants 4 | 5 | print("Decoding rooms") 6 | 7 | path = sys.argv[1] 8 | 9 | fout = open("%s/rooms.asm" % path, "w") 10 | 11 | for i in range(1, constants.numRooms): 12 | f = open("%s/%02d.out" % (path, i), "rb") 13 | f.seek(10) 14 | # graphics data 15 | f.seek(utils.readWord(f)) 16 | fout.write("; Room %d\n" % i) 17 | fout.write("; tileset=%d\n" % utils.readByte(f)) 18 | # palette 19 | # background 20 | f.close() 21 | 22 | fout.close() 23 | -------------------------------------------------------------------------------- /zpragma.inc: -------------------------------------------------------------------------------- 1 | // code start address 2 | 3 | #pragma output CRT_ORG_CODE = 0x6a00 4 | 5 | // pages for buffers 6 | #pragma output CRT_ORG_PAGE_0 = 0xC000 7 | #pragma output CRT_ORG_PAGE_1 = 0xE000 8 | #pragma output CRT_ORG_PAGE_2 = 0x4000 9 | #pragma output CRT_ORG_PAGE_3 = 0x4000 10 | #pragma output CRT_ORG_PAGE_32 = 0x4000 11 | #pragma output CRT_ORG_PAGE_48 = 0x4000 12 | #pragma output CRT_ORG_PAGE_49 = 0x4000 13 | 14 | // stack location 15 | 16 | #pragma output REGISTER_SP = 0x0 17 | 18 | // indicate we are not returning to basic, reduces crt size 19 | 20 | #pragma output CRT_ON_EXIT = 0x10001 21 | 22 | // control size of printf 23 | 24 | //#pragma printf = "%u" 25 | 26 | // heap sizes 27 | 28 | #pragma output CLIB_STDIO_HEAP_SIZE = 0 29 | -------------------------------------------------------------------------------- /verbs.h: -------------------------------------------------------------------------------- 1 | #ifndef VERBS_H 2 | #define VERBS_H 3 | 4 | #include 5 | 6 | #define _numVerbs 32//100 7 | 8 | #define VERB_NAME_SIZE 32 9 | 10 | typedef struct VerbSlot { 11 | // Common::Rect curRect; 12 | // Common::Rect oldRect; 13 | uint8_t verbid; 14 | uint8_t x, y; 15 | uint8_t curmode; 16 | // uint8 color, hicolor, dimcolor, bkcolor, type; 17 | // uint8 charset_nr, curmode; 18 | // uint16 saveid; 19 | // uint8 key; 20 | // bool center; 21 | // uint8 prep; 22 | // uint16 imgindex; 23 | char name[VERB_NAME_SIZE]; 24 | } VerbSlot; 25 | 26 | extern VerbSlot verbs[]; 27 | 28 | void verb_kill(uint8_t slot); 29 | uint8_t verb_getSlot(uint8_t verb, uint8_t mode); 30 | uint8_t verb_findAtPos(uint8_t x, uint8_t y); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /scripts/tables.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import utils 3 | 4 | # static const int v1MMNEScostTables[2][6] = { 5 | # /* desc lens offs data gfx pal */ 6 | # { 25, 27, 29, 31, 33, 35}, 7 | # { 26, 28, 30, 32, 34, 36} 8 | # }; 9 | 10 | path = sys.argv[1] 11 | 12 | print("Generating tables") 13 | 14 | # translation table - costume 77 15 | 16 | fin = utils.openCostume(path, 77) 17 | 18 | tables = open("gen/tables.asm", "w") 19 | tables.write("; Translation table\n") 20 | tables.write("SECTION PAGE_3\n") 21 | tables.write("PUBLIC _translationTable\n") 22 | tables.write("_translationTable: defb") 23 | for i in range(0, utils.readWord(fin)): 24 | b = utils.readByte(fin) 25 | if i: 26 | tables.write(",") 27 | tables.write(" %d" % b) 28 | tables.write("\n\n") 29 | 30 | fin.close() 31 | 32 | 33 | tables.close() 34 | -------------------------------------------------------------------------------- /debug.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_H 2 | #define DEBUG_H 3 | 4 | //#define DEBUG 5 | //#define HABR 6 | 7 | #ifdef DEBUG 8 | #define DEBUG_PUTC(c) debug_putc(c) 9 | #define DEBUG_PUTS(s) debug_puts(s) 10 | #define DEBUG_PRINTF(s, ...) debug_printf(s, __VA_ARGS__) 11 | #define DEBUG_HALT while(1) 12 | #define DEBUG_ASSERT(c, s) do { if (!(c)) { DEBUG_PRINTF("ASSERT %s\n", s); DEBUG_HALT; } } while (0) 13 | #define DEBUG_DELAY(n) debug_delay(n) 14 | #else 15 | #define DEBUG_PUTC(c) while(0) 16 | #define DEBUG_PUTS(s) while(0) 17 | #define DEBUG_PRINTF(s, ...) while(0) 18 | #define DEBUG_HALT while(1) 19 | #define DEBUG_ASSERT(c, s) while(0) 20 | #define DEBUG_DELAY(n) while(0) 21 | #endif 22 | 23 | void debug_putc(char c); 24 | void debug_puts(const char *s); 25 | void debug_printf(const char *s, ...); 26 | void debug_delay(unsigned int n); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /box.h: -------------------------------------------------------------------------------- 1 | #ifndef BOX_H 2 | #define BOX_H 3 | 4 | #include 5 | 6 | #define MAX_BOXES 16 7 | 8 | typedef enum { 9 | kBoxXFlip = 0x08, 10 | kBoxYFlip = 0x10, 11 | kBoxIgnoreScale = 0x20, 12 | kBoxPlayerOnly = 0x20, 13 | kBoxLocked = 0x40, 14 | kBoxInvisible = 0x80 15 | } BoxFlags; 16 | 17 | typedef struct Box { 18 | uint8_t uy; 19 | uint8_t ly; 20 | uint8_t ulx; 21 | uint8_t urx; 22 | uint8_t llx; 23 | uint8_t lrx; 24 | uint8_t mask; 25 | uint8_t flags; 26 | } Box; 27 | 28 | extern uint8_t numBoxes; 29 | extern Box boxes[]; 30 | extern uint8_t boxesMatrix[]; 31 | 32 | uint8_t boxes_adjustXY(uint8_t *px, uint8_t *py); 33 | int8_t boxes_getNext(int8_t from, int8_t to); 34 | uint8_t box_getClosestPtOnBox(uint8_t b, uint8_t x, uint8_t y, uint8_t *outX, uint8_t *outY); 35 | uint8_t box_checkXYInBounds(uint8_t b, uint8_t x, uint8_t y); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFILES = $(wildcard *.c) 2 | ASMFILES = $(wildcard *.asm) $(wildcard gen/*.asm) 3 | OFILES = $(CFILES:.c=.o) $(ASMFILES:.asm=.o) 4 | 5 | PROGRAM = scummnext.nex 6 | 7 | all: $(PROGRAM) 8 | 9 | scummnext.nex: $(OFILES) 10 | # zcc +zxn -m -o $(basename $@) $(OFILES) -clib=sdcc_iy -v -create-app -subtype=nex -pragma-include:zpragma.inc 11 | zcc +scumm -m -o $(basename $@) $(OFILES) -clib=sdcc_iy -v -create-app -subtype=nex -pragma-include:zpragma.inc 12 | 13 | %.o: %.c 14 | # zcc +zxn -o $@ -c $^ -v -SO3 -clib=sdcc_iy --max-allocs-per-node200000 -subtype=nex -Izxn --list 15 | zcc +scumm -o $@ -c $^ -v -SO3 --max-allocs-per-node200000 -subtype=scumm -Izxn -l 16 | 17 | %.o: %.asm 18 | # zcc +zxn -o $@ -c $^ -v -SO3 -clib=sdcc_iy --max-allocs-per-node200000 -subtype=nex -Izxn --list 19 | zcc +scumm -o $@ -c $^ -v -SO3 --max-allocs-per-node200000 -subtype=scumm -Izxn -l 20 | 21 | clean: 22 | $(RM) *.o *.lis scummnext.nex 23 | -------------------------------------------------------------------------------- /resource.h: -------------------------------------------------------------------------------- 1 | #ifndef RESOURCE_H 2 | #define RESOURCE_H 3 | 4 | #include 5 | 6 | typedef uint8_t HROOM; 7 | 8 | typedef struct Resource 9 | { 10 | uint8_t room; 11 | uint16_t roomoffs; 12 | } Resource; 13 | 14 | #define _numCostumes 80 15 | #define _numScripts 200 16 | //#define _numSounds 100 17 | 18 | #define READ_LE_UINT16(p) (*(uint16_t*)(p)) 19 | 20 | extern Resource costumes[]; 21 | extern Resource scripts[]; 22 | 23 | uint8_t readByte(HROOM f); 24 | uint16_t readWord(HROOM f); 25 | void readBuffer(HROOM r, uint8_t *buf, uint16_t sz); 26 | 27 | HROOM openRoom(uint8_t i); 28 | void closeRoom(HROOM r); 29 | /* Opens a room and seeks to the beginning of the resource */ 30 | HROOM seekResource(Resource *res); 31 | uint16_t readResource(HROOM r, uint8_t *buf, uint16_t sz); 32 | void seekToOffset(HROOM r, uint16_t offs); 33 | void seekFwd(HROOM r, uint16_t offs); 34 | uint8_t readString(HROOM r, char *s); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /verbs.c: -------------------------------------------------------------------------------- 1 | #include "verbs.h" 2 | #include "graphics.h" 3 | #include "debug.h" 4 | 5 | VerbSlot verbs[_numVerbs]; 6 | 7 | void verb_kill(uint8_t slot) 8 | { 9 | VerbSlot *vs = &verbs[slot]; 10 | // clear old verb on screen 11 | if (vs->verbid) 12 | { 13 | char *s = vs->name; 14 | while (*s) 15 | *s++ = ' '; 16 | graphics_drawVerb(vs); 17 | } 18 | 19 | vs->verbid = 0; 20 | vs->curmode = 0; 21 | } 22 | 23 | // TODO: mode is useless? 24 | uint8_t verb_getSlot(uint8_t verb, uint8_t mode) 25 | { 26 | uint8_t i; 27 | for (i = 1 ; i < _numVerbs ; i++) { 28 | if (verbs[i].verbid == verb/* && _verbs[i].saveid == mode*/) { 29 | return i; 30 | } 31 | } 32 | return 0; 33 | } 34 | 35 | uint8_t verb_findAtPos(uint8_t x, uint8_t y) 36 | { 37 | uint8_t i = _numVerbs - 1; 38 | while (i) 39 | { 40 | VerbSlot *vs = &verbs[i]; 41 | if (vs->curmode == 1 && vs->verbid && y == vs->y 42 | && x >= vs->x) 43 | { 44 | int8_t d = x - vs->x; 45 | char *c = vs->name; 46 | while (d && *c) 47 | { 48 | --d; 49 | ++c; 50 | } 51 | if (!d && *c) 52 | break; 53 | } 54 | 55 | --i; 56 | } 57 | return i; 58 | } 59 | -------------------------------------------------------------------------------- /scripts/room0.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from utils import readWord 3 | import constants 4 | 5 | def extractResource(res, room, offset, suffix): 6 | fr = open("%s/%02d.out" % (path, room), "rb") 7 | fr.seek(offset) 8 | fc = open("%s/%03d.%s" % (path, res, suffix), "wb") 9 | sz = readWord(fr) 10 | fr.seek(offset) 11 | buf = fr.read(sz) 12 | fc.write(buf) 13 | fr.close() 14 | fc.close() 15 | 16 | def extractCostume(cost, room, offset): 17 | extractResource(cost, room, offset, "cost") 18 | 19 | def extractScript(cost, room, offset): 20 | extractResource(cost, room, offset, "script") 21 | 22 | print("Parsing room 0") 23 | 24 | path = sys.argv[1] 25 | name_in = "%s/00.out" % path 26 | 27 | fin = open(name_in, "rb") 28 | magic = readWord(fin) 29 | 30 | # read global objects 31 | 32 | fin.seek(constants.numGlobalObjects, 1) 33 | 34 | # skip room offsets 35 | 36 | fin.seek(constants.numRooms * 3, 1) 37 | 38 | # extract costumes 39 | 40 | rooms = fin.read(constants.numCostumes) 41 | for cost in range(0, constants.numCostumes): 42 | extractCostume(cost, rooms[cost], readWord(fin)) 43 | 44 | # extract scripts 45 | 46 | rooms = fin.read(constants.numScripts) 47 | for script in range(0, constants.numScripts): 48 | extractScript(script, rooms[script], readWord(fin)) 49 | 50 | # do nothing with sounds 51 | 52 | fin.close() 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Mini-SCUMMVM for ZXNext. 2 | 3 | Supports only Maniac Mansion from NES. 4 | 5 | Directory structure for running: 6 | * scummnext.nex 7 | * MM 8 | * unpacked *.LFL from NES image 9 | 10 | 11 | Memory map: 12 | 13 | * 0x0000 - ROM, Also used for temporary mapping of other pages 14 | * 0x4000 - Page for mapping data 15 | * 0x6000 - Tilemap 16 | * 0x6a00 - code 17 | 18 | * Character set translation table (costume 77) 19 | * Preposition list (costume 78) 20 | * NES base tiles (costume 37) 21 | * Resource heap 22 | * Costume rooms/offsets 23 | * Script rooms/offsets 24 | 25 | Pages map: 26 | 27 | * 2 - tile data 28 | * 3 - object data, some graphics data 29 | * 32-47 - scripts 30 | * 48, 49 - costumes 31 and 32 with sprite data 31 | 32 | Resource info: 33 | 34 | Rooms: 55: 0-54 35 | 36 | costumes 25-36 are special. see v1MMNEScostTables[] in costume.cpp 37 | costumes 37-76 are room graphics resources 38 | costume 77 is a character set translation table 39 | costume 78 is a preposition list 40 | costume 79 is unused but allocated, so the total is a nice even number :) 41 | 42 | static data: 43 | 44 | extern uint8_t translationTable[256]; 45 | 46 | // costume set 47 | extern uint8_t costdesc[51]; 48 | extern uint8_t costlens[279]; 49 | extern uint8_t costoffs[556]; 50 | 51 | 52 | Z88DK: 53 | 54 | https://github.com/Dovgalyuk/z88dk/tree/scumm 55 | 56 | z88dk/build.sh 57 | z88dk/libsrc/_DEVELOPMENT/make.sh 58 | -------------------------------------------------------------------------------- /string.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | #include "graphics.h" 3 | #include "script.h" 4 | #include "debug.h" 5 | 6 | #define _defaultTalkDelay 3 7 | 8 | static uint8_t messageBuf[128]; 9 | static uint8_t *curPos; 10 | static uint8_t color; 11 | int8_t talkDelay; 12 | 13 | 14 | uint8_t *message_new(void) 15 | { 16 | curPos = messageBuf; 17 | return messageBuf; 18 | } 19 | 20 | void message_print(uint8_t *m, uint8_t c) 21 | { 22 | uint8_t haveMsg = 1; 23 | //talkDelay += _msgCount * _defaultTalkDelay; 24 | curPos = m; 25 | color = c; 26 | talkDelay = 0; 27 | scummVars[VAR_CHARCOUNT] = 0; 28 | while (*curPos) 29 | { 30 | talkDelay += 3; 31 | ++scummVars[VAR_CHARCOUNT]; 32 | if (*curPos == 3) 33 | { 34 | // next part of the message 35 | *curPos = 0; 36 | haveMsg = 0xff; 37 | break; 38 | } 39 | ++curPos; 40 | } 41 | //talkDelay = 60; 42 | // TODO: switch between messages 43 | scummVars[VAR_HAVE_MSG] = haveMsg; 44 | 45 | graphics_print(m, color); 46 | } 47 | 48 | void messages_update(void) 49 | { 50 | if (!scummVars[VAR_HAVE_MSG]) 51 | return; 52 | 53 | if (talkDelay) 54 | return; 55 | 56 | // last message processed 57 | if (scummVars[VAR_HAVE_MSG] == 1) 58 | { 59 | actor_stopTalk(); 60 | return; 61 | } 62 | 63 | // process next message 64 | message_print(curPos + 1, color); 65 | } 66 | -------------------------------------------------------------------------------- /script.h: -------------------------------------------------------------------------------- 1 | #ifndef SCRIPT_H 2 | #define SCRIPT_H 3 | 4 | #include 5 | 6 | #include "room.h" 7 | 8 | #define VAR_EGO 0 9 | #define VAR_CAMERA_POS_X 2 10 | #define VAR_HAVE_MSG 3 11 | #define VAR_ROOM 4 12 | #define VAR_OVERRIDE 5 13 | #define VAR_MACHINE_SPEED 6 14 | #define VAR_CHARCOUNT 7 15 | #define VAR_ACTIVE_VERB 8 16 | #define VAR_ACTIVE_OBJECT1 9 17 | #define VAR_ACTIVE_OBJECT2 10 18 | #define VAR_NUM_ACTOR 11 19 | #define VAR_CURRENT_LIGHTS 12 20 | #define VAR_CURRENTDRIVE 13 21 | #define VAR_MUSIC_TIMER 17 22 | #define VAR_VERB_ALLOWED 18 23 | #define VAR_ACTOR_RANGE_MIN 19 24 | #define VAR_ACTOR_RANGE_MAX 20 25 | #define VAR_CURSORSTATE 21 26 | #define VAR_CAMERA_MIN_X 23 27 | #define VAR_CAMERA_MAX_X 24 28 | #define VAR_TIMER_NEXT 25 29 | #define VAR_SENTENCE_VERB 26 30 | #define VAR_SENTENCE_OBJECT1 27 31 | #define VAR_SENTENCE_OBJECT2 28 32 | #define VAR_SENTENCE_PREPOSITION 29 33 | #define VAR_VIRT_MOUSE_X 30 34 | #define VAR_VIRT_MOUSE_Y 31 35 | #define VAR_CLICK_AREA 32 36 | #define VAR_CLICK_VERB 33 37 | #define VAR_CLICK_OBJECT 35 38 | #define VAR_ROOM_RESOURCE 36 39 | #define VAR_LAST_SOUND 37 40 | #define VAR_BACKUP_VERB 38 41 | #define VAR_KEYPRESS 39 42 | #define VAR_CUTSCENEEXIT_KEY 40 43 | #define VAR_TALK_ACTOR 41 44 | 45 | extern int16_t scummVars[]; 46 | 47 | void initScriptPages(void); 48 | void runScript(uint16_t s); 49 | void runRoomScript(uint16_t id, uint8_t room, uint16_t offs); 50 | void runObjectScript(uint16_t objectA, uint8_t verb); 51 | void runInventoryScript(uint16_t i); 52 | void processScript(void); 53 | void updateScummVars(void); 54 | void checkExecVerbs(void); 55 | void checkAndRunSentenceScript(void); 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /scripts/tiles.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import utils 3 | 4 | def decodeTiles(c, s, prefix): 5 | total = 0 6 | f = utils.openCostume(path, c) 7 | cnt = utils.readWord(f) - 3 8 | utils.readByte(f) # tile count 9 | count = utils.readByte(f) 10 | data = utils.readByte(f) 11 | # decode NES buffer 12 | buf = [] 13 | while cnt: 14 | for j in range(0, count & 0x7f): 15 | buf.append(data) 16 | if (count & 0x80) != 0 and cnt > 0: 17 | data = utils.readByte(f) 18 | cnt -= 1 19 | if count & 0x80: 20 | count = data 21 | else: 22 | if cnt: 23 | count = utils.readByte(f) 24 | cnt -= 1 25 | if cnt: 26 | data = utils.readByte(f) 27 | cnt -= 1 28 | f.close() 29 | # convert to Next tiles 30 | fout = open("%s/%s%02d.asm" % (path, prefix, s), "w") 31 | for t in range(0, len(buf) // 16): 32 | fout.write("; Tile %d\n;" % t) 33 | for i in range(0, 8): 34 | c0 = buf[t * 16 + i] 35 | c1 = buf[t * 16 + i + 8] 36 | for j in range(0, 4): 37 | col1 = ((c0 >> (7 - j * 2)) & 1) + (((c1 >> (7 - j * 2)) & 1) << 1) 38 | col2 = ((c0 >> (7 - (j * 2 + 1))) & 1) + (((c1 >> (7 - (j * 2 + 1))) & 1) << 1) 39 | b = (col1 << 4) | col2 40 | fout.write("%02xh " % b) 41 | total += 1 42 | fout.write("\n;") 43 | 44 | fout.close() 45 | return total 46 | 47 | print("Decoding tiles") 48 | 49 | path = sys.argv[1] 50 | 51 | # costumes 37-76 are room tiles 52 | 53 | total = 0 54 | for s in range(37, 77): 55 | total += decodeTiles(s, s - 37, "roomtiles") 56 | print("%d bytes for room tiles" % total) 57 | 58 | # costumes 33-34 are sprite tiles 59 | 60 | total = 0 61 | total += decodeTiles(33, 0, "spritetiles") 62 | total += decodeTiles(34, 1, "spritetiles") 63 | print("%d bytes for sprite tiles" % total) 64 | -------------------------------------------------------------------------------- /actor.h: -------------------------------------------------------------------------------- 1 | #ifndef ACTOR_H 2 | #define ACTOR_H 3 | 4 | #include 5 | 6 | //#define MAX_FRAMES 8 7 | 8 | typedef struct Actor 9 | { 10 | uint8_t room; 11 | uint8_t x, y; 12 | 13 | // name 14 | char name[16]; 15 | // not used for NES 16 | //uint8_t talkColor; 17 | 18 | // movement parameters 19 | uint8_t destX, destY; 20 | uint8_t nextX, nextY; 21 | uint8_t curX, curY; 22 | int32_t deltaXFactor, deltaYFactor; 23 | uint8_t moving; 24 | uint8_t walkbox; 25 | uint8_t destbox; 26 | //int8_t destdir; 27 | uint8_t curbox; 28 | // { 270, 90, 180, 0 } 29 | uint8_t facing; 30 | uint8_t targetFacing; 31 | 32 | // costume parameters 33 | uint8_t costume; 34 | // costume frame 35 | uint8_t frame; 36 | // animation of the frame 37 | uint8_t frames; 38 | //uint8_t anchors[MAX_FRAMES]; 39 | uint8_t curpos; 40 | // anchor sprite of the current view 41 | uint8_t anchor; 42 | uint8_t old_anchor; 43 | int8_t ax, ay; 44 | } Actor; 45 | 46 | #define ACTOR_COUNT 25 47 | extern Actor actors[]; 48 | extern uint8_t defaultTalkColor; 49 | 50 | void actors_walk(void); 51 | void actors_animate(void); 52 | void actors_show(void); 53 | void actors_draw(uint8_t offs, uint8_t gap); 54 | 55 | uint8_t actor_getX(uint8_t actor); 56 | uint8_t actor_isMoving(uint8_t actor); 57 | uint8_t actor_isInCurrentRoom(uint8_t actor); 58 | uint8_t actor_getFromPos(uint8_t x, uint8_t y); 59 | 60 | void actor_setRoom(uint8_t actor, uint8_t room); 61 | void actor_setCostume(uint8_t actor, uint8_t costume); 62 | // void actor_setTalkColor(uint8_t actor, uint8_t color); 63 | void actor_put(uint8_t actor, uint8_t x, uint8_t y); 64 | void actor_stopTalk(void); 65 | void actor_talk(uint8_t actor, const uint8_t *s); 66 | void actor_startWalk(uint8_t actor, uint8_t x, uint8_t y); 67 | void actor_animate(uint8_t actor, uint8_t anim); 68 | void actor_walkToObject(uint8_t actor, uint16_t obj_id); 69 | void actor_walkToActor(uint8_t actor, uint8_t toActor, uint8_t dist); 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /cursor.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "cursor.h" 3 | #include "graphics.h" 4 | #include "script.h" 5 | 6 | static uint8_t cursorState; 7 | uint16_t cursorX; 8 | uint16_t cursorY; 9 | 10 | __sfr __banked __at 0xfbfe IO_FBFE; 11 | __sfr __banked __at 0xfdfe IO_FDFE; 12 | __sfr __banked __at 0xdffe IO_DFFE; 13 | 14 | void cursor_setState(uint8_t s) 15 | { 16 | cursorState = s; 17 | } 18 | 19 | void cursor_animate(void) 20 | { 21 | // color = default_cursor_colors[idx]; 22 | // _cursor.width = 8; 23 | // _cursor.height = 8; 24 | // _cursor.hotspotX = 0; 25 | // _cursor.hotspotY = 0; 26 | 27 | // byte *dst = _grabbedCursor; 28 | // byte *src = &_NESPatTable[0][0xfa * 16]; 29 | // byte *palette = _NESPalette[1]; 30 | 31 | // for (i = 0; i < 8; i++) 32 | // { 33 | // byte c0 = src[i]; 34 | // byte c1 = src[i + 8]; 35 | // for (j = 0; j < 8; j++) 36 | // *dst++ = palette[((c0 >> (7 - j)) & 1) | (((c1 >> (7 - j)) & 1) << 1) | ((idx == 3) ? 4 : 0)]; 37 | // } 38 | 39 | // CursorMan.replaceCursor(_grabbedCursor, _cursor.width, _cursor.height, 40 | // _cursor.hotspotX, _cursor.hotspotY, 41 | // (_game.platform == Common::kPlatformNES ? _grabbedCursor[63] : transColor), 42 | // (_game.heversion == 70 ? true : false)); 43 | 44 | // scan keyboard 45 | if (!(IO_DFFE & 1)) 46 | ++cursorX; 47 | if (!(IO_DFFE & 2)) 48 | --cursorX; 49 | if (!(IO_FBFE & 1)) 50 | --cursorY; 51 | if (!(IO_FDFE & 1)) 52 | ++cursorY; 53 | 54 | // update scumm vars 55 | // scummVars[VAR_VIRT_MOUSE_X] = cursorX >> V12_X_SHIFT; 56 | // scummVars[VAR_VIRT_MOUSE_Y] = cursorY >> V12_Y_SHIFT; 57 | 58 | uint16_t x = cursorX + (LINE_GAP * 8); 59 | uint16_t y = cursorY;// + (SCREEN_TOP * 8); 60 | IO_SPRITE_SLOT = SPRITE_CURSOR; 61 | IO_SPRITE_ATTRIBUTE = x; // x 62 | IO_SPRITE_ATTRIBUTE = y; // y 63 | IO_SPRITE_ATTRIBUTE = x >> 8; // palette and x8 64 | // sprite and the pattern are equal 65 | IO_SPRITE_ATTRIBUTE = 0xc0 | SPRITE_CURSOR; // visible, 5th byte, pattern 66 | IO_SPRITE_ATTRIBUTE = 0x80; // 4-bit sprite and y8 67 | } 68 | -------------------------------------------------------------------------------- /object.h: -------------------------------------------------------------------------------- 1 | #ifndef OBJECT_H 2 | #define OBJECT_H 3 | 4 | #include 5 | #include "resource.h" 6 | 7 | #define _numGlobalObjects 775 8 | #define _numLocalObjects 200 9 | #define _numInventory 80 10 | 11 | enum ObjectStateV2 { 12 | kObjectStatePickupable = 1, 13 | kObjectStateUntouchable = 2, 14 | kObjectStateLocked = 4, 15 | 16 | // FIXME: Not quite sure how to name state 8. It seems to mark some kind 17 | // of "activation state" for the given object. E.g. is a door open? 18 | // Is a drawer extended? In addition it is used to toggle the look 19 | // of objects that the user can "pick up" (i.e. it is set in 20 | // o2_pickupObject together with kObjectStateUntouchable). So in a sense, 21 | // it can also mean "invisible" in some situations. 22 | kObjectState_08 = 8 23 | }; 24 | 25 | enum WhereIsObject { 26 | WIO_NOT_FOUND = -1, 27 | WIO_INVENTORY = 0, 28 | WIO_ROOM = 1, 29 | WIO_GLOBAL = 2, 30 | WIO_LOCAL = 3, 31 | WIO_FLOBJECT = 4 32 | }; 33 | 34 | #define OF_OWNER_ROOM 0x0f 35 | 36 | typedef struct Inventory 37 | { 38 | uint16_t obj_nr; 39 | uint8_t preposition; 40 | uint8_t room; 41 | uint8_t nameOffs; 42 | uint16_t OBCDoffset; 43 | } Inventory; 44 | 45 | //Object *object_get(uint16_t id); 46 | uint16_t object_find(uint16_t x, uint16_t y); 47 | int8_t object_whereIs(uint16_t id); 48 | uint16_t object_getVerbEntrypoint(uint16_t obj, uint16_t entry); 49 | uint8_t object_getName(char *s, uint16_t id); 50 | uint8_t object_getOwner(uint16_t id); 51 | uint16_t object_getOBCDoffset(uint16_t id); 52 | uint8_t object_getPreposition(uint16_t id); 53 | // uint16_t object_getId(Object *obj); 54 | void object_setOwner(uint16_t id, uint8_t owner); 55 | 56 | uint8_t object_getState(uint16_t id); 57 | void object_setState(uint16_t id, uint8_t s); 58 | 59 | void object_getXY(uint16_t id, uint8_t *x, uint8_t *y); 60 | 61 | void setupRoomObjects(HROOM r); 62 | void objects_clear(void); 63 | void objects_redraw(void); 64 | void object_draw(uint16_t id); 65 | 66 | void readGlobalObjects(HROOM r); 67 | 68 | 69 | void inventory_redraw(void); 70 | void inventory_addObject(uint16_t id); 71 | uint16_t inventory_checkXY(int8_t x, int8_t y); 72 | void inventory_checkButtons(int8_t x, int8_t y); 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /resource.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "resource.h" 4 | #include "debug.h" 5 | 6 | Resource costumes[_numCostumes]; 7 | Resource scripts[_numScripts]; 8 | 9 | #define ENC_BYTE 0xff 10 | 11 | uint8_t readByte(HROOM f) 12 | { 13 | uint8_t b; 14 | esx_f_read(f, &b, 1); 15 | return b ^ ENC_BYTE; 16 | } 17 | 18 | uint16_t readWord(HROOM f) 19 | { 20 | uint8_t b[2]; 21 | esx_f_read(f, b, 2); 22 | return (b[0] ^ ENC_BYTE) + (b[1] ^ ENC_BYTE) * 0x100; 23 | } 24 | 25 | HROOM openRoom(uint8_t i) 26 | { 27 | char fname[] = "MM/00.LFL"; 28 | fname[3] += i / 10; 29 | fname[4] += i % 10; 30 | return esx_f_open(fname, ESX_MODE_OPEN_EXIST | ESX_MODE_READ); 31 | } 32 | 33 | void closeRoom(HROOM r) 34 | { 35 | esx_f_close(r); 36 | } 37 | 38 | HROOM seekResource(Resource *res) 39 | { 40 | //DEBUG_PRINTF("seek res %d %d\n", res->room, res->roomoffs); 41 | HROOM f = openRoom(res->room); 42 | esx_f_seek(f, res->roomoffs, ESX_SEEK_SET); 43 | 44 | return f; 45 | } 46 | 47 | void readBuffer(HROOM r, uint8_t *buf, uint16_t sz) 48 | { 49 | esx_f_read(r, buf, sz); 50 | while (sz--) 51 | { 52 | *buf++ ^= ENC_BYTE; 53 | } 54 | } 55 | 56 | 57 | uint16_t readResource(HROOM r, uint8_t *buf, uint16_t sz) 58 | { 59 | uint16_t size = readWord(r); 60 | //DEBUG_PRINTF("read res %d into buf %d\n", size, sz); 61 | uint16_t i; 62 | if (size > sz) 63 | { 64 | DEBUG_PRINTF("Can't load resource with size %u into buffer with size %u\n", size, sz); 65 | DEBUG_HALT; 66 | } 67 | esx_f_seek(r, 2, ESX_SEEK_BWD); 68 | 69 | readBuffer(r, buf, size); 70 | 71 | return size; 72 | } 73 | 74 | void seekToOffset(HROOM r, uint16_t offs) 75 | { 76 | esx_f_seek(r, offs, ESX_SEEK_SET); 77 | } 78 | 79 | void seekFwd(HROOM r, uint16_t offs) 80 | { 81 | esx_f_seek(r, offs, ESX_SEEK_FWD); 82 | } 83 | 84 | uint8_t readString(HROOM r, char *s) 85 | { 86 | char c; 87 | char *start = s; 88 | do 89 | { 90 | c = readByte(r); 91 | *s++ = c & 0x7f; 92 | if (c & 0x80) 93 | { 94 | *s++ = ' '; 95 | } 96 | } 97 | while (c); 98 | return s - start - 1; 99 | } 100 | -------------------------------------------------------------------------------- /mem.asm: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 3 | ; tiles page 4 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 5 | SECTION PAGE_2 6 | 7 | PUBLIC _tileBuf 8 | _tileBuf: defs 4096,0 9 | 10 | PUBLIC _spriteTiles 11 | _spriteTiles: defs 4096,0 12 | 13 | ; No more space 14 | 15 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 16 | ; object page 17 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 18 | SECTION PAGE_3 19 | 20 | ; Fix size when updating the Object struct 21 | PUBLIC _objects 22 | _objects: defs 200*17, 0 23 | 24 | PUBLIC _objectOwnerTable 25 | _objectOwnerTable: defs 775, 0 26 | 27 | PUBLIC _objectStateTable 28 | _objectStateTable: defs 775, 0 29 | 30 | PUBLIC _invObjects 31 | _invObjects: defs 80*7, 0 32 | 33 | PUBLIC _inventoryOffset 34 | _inventoryOffset: defs 1, 0 35 | 36 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 37 | ; graphics page 38 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 39 | SECTION PAGE_3 40 | 41 | PUBLIC _nametable 42 | _nametable: defs 1024, 0 43 | PUBLIC _attributes 44 | _attributes: defs 64, 0 45 | 46 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 47 | ; page structure for scipts 48 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 49 | SECTION PAGE_32 ;; to page 47 50 | 51 | PUBLIC _scriptBytes 52 | _scriptBytes: defs 4096, 0 53 | PUBLIC _script_id 54 | _script_id: defs 2, 0 55 | PUBLIC _script_status 56 | _script_status: defs 1, 0 57 | PUBLIC _script_room 58 | _script_room: defs 1, 0 59 | PUBLIC _script_roomoffs 60 | _script_roomoffs: defs 2, 0 61 | PUBLIC _script_offset 62 | _script_offset: defs 2, 0 63 | PUBLIC _script_delay 64 | _script_delay: defs 4, 0 65 | PUBLIC _script_resultVarNumber 66 | _script_resultVarNumber: defs 2, 0 67 | 68 | ; No more space 69 | 70 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 71 | ; two costume pages 72 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 73 | SECTION PAGE_48 74 | 75 | PUBLIC _costume31 76 | _costume31: defs 8192, 0 77 | ; _costume31: defs 11234, 0 78 | 79 | SECTION PAGE_49 80 | 81 | PUBLIC _costume31_tail 82 | _costume31_tail: defs (11234-8192), 0 83 | 84 | PUBLIC _costume32 85 | _costume32: defs 140, 0 86 | 87 | PUBLIC _actCostumes 88 | _actCostumes: defs 2048, 0 89 | 90 | PUBLIC _costumesPtr 91 | _costumesPtr: defs 80 * 2, 0 92 | 93 | PUBLIC _actCostumesUsed 94 | _actCostumesUsed: defw 0 95 | 96 | PUBLIC _anchors 97 | _anchors: defs 6, 0 98 | 99 | ; PUBLIC _costdesc 100 | ; _costdesc: defs 51, 0 101 | 102 | ; PUBLIC _costlens 103 | ; _costlens: defs 279, 0 104 | 105 | ; PUBLIC _costoffs 106 | ; _costoffs: defs 556, 0 107 | 108 | -------------------------------------------------------------------------------- /camera.c: -------------------------------------------------------------------------------- 1 | #include "camera.h" 2 | #include "graphics.h" 3 | #include "room.h" 4 | #include "actor.h" 5 | #include "debug.h" 6 | #include "script.h" 7 | 8 | enum { 9 | kNormalCameraMode = 1, 10 | kFollowActorCameraMode = 2, 11 | kPanningCameraMode = 3 12 | }; 13 | 14 | uint8_t cameraX; 15 | uint8_t cameraMode; 16 | static uint8_t cameraDestX; 17 | static uint8_t cameraMovingToActor; 18 | static uint8_t cameraFollows; 19 | 20 | void camera_setX(uint8_t x) 21 | { 22 | if (x < roomWidth / 2) 23 | x = roomWidth / 2; 24 | uint8_t max = roomWidth - SCREEN_WIDTH / 2; 25 | if (x > max) 26 | x = max; 27 | 28 | // if (camera._mode != kFollowActorCameraMode || ABS(pos_x - camera._cur.x) > (_screenWidth / 2)) { 29 | // camera._cur.x = pos_x; 30 | // } 31 | // camera._dest.x = pos_x; 32 | cameraX = x; 33 | cameraDestX = x; 34 | cameraMode = kNormalCameraMode; 35 | cameraMovingToActor = 0; 36 | scummVars[VAR_CAMERA_POS_X] = x; 37 | // TODO: min, max, destination 38 | } 39 | 40 | void camera_panTo(uint8_t x) 41 | { 42 | cameraDestX = x; 43 | cameraMode = kPanningCameraMode; 44 | cameraMovingToActor = 0; 45 | } 46 | 47 | void camera_followActor(uint8_t actor) 48 | { 49 | // DEBUG_PRINTF("Camera is following actor %u\n", actor); 50 | cameraMode = kFollowActorCameraMode; 51 | cameraFollows = actor; 52 | 53 | if (actors[actor].room != currentRoom) 54 | { 55 | startScene(actors[actor].room); 56 | cameraX = actors[actor].x; 57 | camera_setX(cameraX); 58 | } 59 | 60 | // t = a->getPos().x / 8 - _screenStartStrip; 61 | 62 | // if (t < camera._leftTrigger || t > camera._rightTrigger || setCamera == true) 63 | // setCameraAt(a->getPos().x, 0); 64 | 65 | // for (i = 1; i < _numActors; i++) { 66 | // if (_actors[i]->isInCurrentRoom()) 67 | // _actors[i]->_needRedraw = true; 68 | // } 69 | runInventoryScript(0); 70 | } 71 | 72 | void camera_move(void) 73 | { 74 | if (cameraMode == kFollowActorCameraMode) 75 | { 76 | cameraDestX = actor_getX(cameraFollows); 77 | } 78 | 79 | if (cameraX < cameraDestX/* && cameraX < roomWidth - SCREEN_WIDTH / 2*/) 80 | ++cameraX; 81 | else if (cameraX > cameraDestX/* && cameraX > SCREEN_WIDTH / 2*/) 82 | --cameraX; 83 | 84 | scummVars[VAR_CAMERA_POS_X] = cameraX; 85 | } 86 | 87 | uint8_t camera_getVirtScreenX(void) 88 | { 89 | if (roomWidth < SCREEN_WIDTH) 90 | return 0; 91 | 92 | uint8_t cam = cameraX; 93 | if (cam < SCREEN_WIDTH / 2) 94 | cam = SCREEN_WIDTH / 2; 95 | else if (cam > roomWidth - SCREEN_WIDTH / 2) 96 | cam = roomWidth - SCREEN_WIDTH / 2; 97 | 98 | return cam - SCREEN_WIDTH / 2; 99 | } 100 | -------------------------------------------------------------------------------- /debug.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "debug.h" 4 | 5 | #ifdef DEBUG 6 | 7 | __sfr __banked __at 0xCF3B DEBUG_PORT; 8 | __sfr __banked __at 0xDF3B DEBUG_DATA; 9 | 10 | void debug_putc(char c) 11 | { 12 | DEBUG_PORT = 1; 13 | DEBUG_DATA = c; 14 | } 15 | 16 | void debug_puts(const char *s) 17 | { 18 | while (*s) 19 | debug_putc(*s++); 20 | } 21 | 22 | static void debug_print_num(uint32_t num, uint8_t base) 23 | { 24 | char buf[12]; 25 | int8_t i = 0; 26 | while (num) 27 | { 28 | char c = num % base + '0'; 29 | if (c > '9') 30 | c += 'a' - '0' - 10; 31 | buf[i++] = c; 32 | num /= base; 33 | } 34 | if (i == 0) 35 | { 36 | debug_putc('0'); 37 | } 38 | else 39 | { 40 | while (--i >= 0) 41 | debug_putc(buf[i]); 42 | } 43 | } 44 | 45 | void debug_printf(const char *s, ...) 46 | { 47 | va_list ap; 48 | va_start(ap, s); 49 | 50 | while (*s) 51 | { 52 | char c = *s++; 53 | if (c == '%') 54 | { 55 | switch (*s++) 56 | { 57 | case '%': 58 | debug_putc('%'); 59 | break; 60 | case 'u': 61 | { 62 | uint16_t arg = va_arg(ap, uint16_t); 63 | debug_print_num(arg, 10); 64 | } 65 | break; 66 | case 'x': 67 | { 68 | uint16_t arg = va_arg(ap, uint16_t); 69 | debug_print_num(arg, 16); 70 | } 71 | break; 72 | case 'd': 73 | { 74 | int16_t arg = va_arg(ap, int16_t); 75 | if (arg < 0) 76 | { 77 | debug_putc('-'); 78 | arg = -arg; 79 | } 80 | debug_print_num(arg, 10); 81 | } 82 | break; 83 | case 'l': 84 | { 85 | int32_t arg = va_arg(ap, int32_t); 86 | if (arg < 0) 87 | { 88 | debug_putc('-'); 89 | arg = -arg; 90 | } 91 | debug_print_num(arg, 10); 92 | } 93 | break; 94 | case 'c': 95 | debug_putc(va_arg(ap, int8_t)); 96 | break; 97 | case 's': 98 | debug_puts(va_arg(ap, char*)); 99 | break; 100 | } 101 | } 102 | else if (c != '\r') 103 | { 104 | debug_putc(c); 105 | } 106 | } 107 | va_end(ap); 108 | } 109 | 110 | uint8_t debug_data; 111 | void debug_delay(unsigned int n) 112 | { 113 | while (n--) 114 | ++debug_data; 115 | } 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /engine.c: -------------------------------------------------------------------------------- 1 | #include "engine.h" 2 | #include "object.h" 3 | #include "resource.h" 4 | #include "actor.h" 5 | #include "room.h" 6 | #include "helper.h" 7 | #include "box.h" 8 | #include "debug.h" 9 | 10 | uint8_t strcopy(char *d, const char *s) 11 | { 12 | char *d0 = d; 13 | while (*d++ = *s++) 14 | ; 15 | // returns length in characters 16 | return d - d0 - 1; 17 | } 18 | 19 | static uint8_t getDist(uint8_t x, uint8_t y, uint8_t x2, uint8_t y2) 20 | { 21 | uint8_t a = ABS(y - y2); 22 | uint8_t b = ABS(x - x2); 23 | return MAX(a, b); 24 | } 25 | 26 | uint8_t getObjOrActorName(char *s, uint16_t id) 27 | { 28 | // if (objIsActor(obj)) 29 | if (id < ACTOR_COUNT) 30 | { 31 | //DEBUG_PRINTF("Name for actor %d\n", id); 32 | return strcopy(s, actors[id].name); 33 | } 34 | 35 | // for (i = 0; i < _numNewNames; i++) { 36 | // if (_newNames[i] == obj) { 37 | // debug(5, "Found new name for object %d at _newNames[%d]", obj, i); 38 | // return getResourceAddress(rtObjectName, i); 39 | // } 40 | // } 41 | 42 | return object_getName(s, id); 43 | } 44 | 45 | int8_t getObjectOrActorXY(uint16_t object, uint8_t *x, uint8_t *y) 46 | { 47 | if (object < ACTOR_COUNT) 48 | { 49 | Actor *act = &actors[object]; 50 | if (act->room == currentRoom) 51 | { 52 | *x = act->x; 53 | *y = act->y; 54 | return 0; 55 | } 56 | else 57 | { 58 | return -1; 59 | } 60 | } 61 | 62 | switch (object_whereIs(object)) 63 | { 64 | case WIO_NOT_FOUND: 65 | return -1; 66 | case WIO_INVENTORY: 67 | { 68 | uint8_t owner = object_getOwner(object); 69 | if (owner && owner < ACTOR_COUNT) 70 | { 71 | Actor *act = &actors[owner]; 72 | if (act->room == currentRoom) 73 | { 74 | *x = act->x; 75 | *y = act->y; 76 | return 0; 77 | } 78 | } 79 | return -1; 80 | } 81 | default: 82 | break; 83 | } 84 | 85 | object_getXY(object, x, y); 86 | return 0; 87 | } 88 | 89 | uint16_t getObjActToObjActDist(uint16_t a, uint16_t b) 90 | { 91 | uint8_t x, y, x2, y2; 92 | 93 | if (a < ACTOR_COUNT && b < ACTOR_COUNT) 94 | { 95 | uint8_t r1 = actors[a].room; 96 | uint8_t r2 = actors[b].room; 97 | if (r1 == r2 && r1 && r1 != currentRoom) 98 | return 0; 99 | } 100 | 101 | if (getObjectOrActorXY(a, &x, &y) == -1) 102 | { 103 | //DEBUG_PRINTF("No XY for %d\n", a); 104 | return 0xFF; 105 | } 106 | 107 | if (getObjectOrActorXY(b, &x2, &y2) == -1) 108 | { 109 | //DEBUG_PRINTF("No XY for %d\n", a); 110 | return 0xFF; 111 | } 112 | 113 | if (a < ACTOR_COUNT && b >= ACTOR_COUNT) 114 | { 115 | boxes_adjustXY(&x2, &y2); 116 | } 117 | 118 | // Now compute the distance between the two points 119 | uint16_t res = getDist(x, y, x2, y2); 120 | // DEBUG_PRINTF("Dist(%d,%d)-(%d,%d) = %d\n", x, y, x2, y2, res); 121 | return res; 122 | } 123 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "debug.h" 4 | #include "resource.h" 5 | #include "script.h" 6 | #include "graphics.h" 7 | #include "camera.h" 8 | #include "actor.h" 9 | #include "cursor.h" 10 | #include "object.h" 11 | #include "sprites.h" 12 | #include "string.h" 13 | #include "costume.h" 14 | 15 | const int _numRooms = 55; 16 | 17 | // costumes 25-36 are special. see v1MMNEScostTables[] in costume.cpp 18 | // costumes 37-76 are room graphics resources 19 | // costume 77 is a character set translation table 20 | // costume 78 is a preposition list 21 | // costume 79 is unused but allocated, so the total is a nice even number :) 22 | 23 | //int _numBitVariables = 4096; // 2048 24 | //int _numArray = 50; 25 | //int _numNewNames = 50; 26 | //int _numCharsets = 9; // 9 27 | // all scripts are global in MM 28 | //int _numGlobalScripts = 200; 29 | //int _numFlObject = 50; 30 | 31 | //int _shadowPaletteSize = 256; 32 | 33 | #ifdef HABR 34 | int habr; 35 | #endif 36 | 37 | int main() 38 | { 39 | ZXN_NEXTREGA(REG_MACHINE_TYPE, RMT_TIMING_P3E | RMT_P3E); 40 | ZXN_NEXTREG(REG_TURBO_MODE, 3/*RTM_14MHZ*/); // 28MHz 41 | 42 | // need default ZXNext pages instead of ULA screen 43 | ZXN_WRITE_MMU6(0); 44 | ZXN_WRITE_MMU7(1); 45 | 46 | // page for scripts 47 | ZXN_WRITE_MMU2(32); 48 | 49 | int i; 50 | HROOM f = openRoom(0); 51 | 52 | uint16_t magic = readWord(f); 53 | //DEBUG_PRINTF("Magic %x\n", magic); /* version magic number */ 54 | readGlobalObjects(f); 55 | 56 | // room offsets are always 0, skip them 57 | // 3 - roomno + roomoffs size 58 | esx_f_seek(f, _numRooms * 3, ESX_SEEK_FWD); 59 | 60 | for (i = 0; i < _numCostumes; i++) { 61 | costumes[i].room = readByte(f); 62 | } 63 | for (i = 0; i < _numCostumes; i++) { 64 | costumes[i].roomoffs = readWord(f); 65 | // HROOM c = seekResource(&costumes[i]); 66 | // uint16_t sz = readWord(c); 67 | // closeRoom(c); 68 | // DEBUG_PRINTF("costume %u room %u offs %u size %u\n", i, costumes[i].room, costumes[i].roomoffs, sz); 69 | } 70 | 71 | for (i = 0; i < _numScripts; i++) { 72 | scripts[i].room = readByte(f); 73 | } 74 | for (i = 0; i < _numScripts; i++) { 75 | scripts[i].roomoffs = readWord(f); 76 | //DEBUG_PRINTF("script %u room %u offs %x\n", i, scripts[i].room, scripts[i].roomoffs); 77 | } 78 | 79 | closeRoom(f); 80 | 81 | costume_loadData(); 82 | 83 | initGraphics(); 84 | 85 | // NES base tiles 86 | decodeTiles(0); 87 | 88 | initScriptPages(); 89 | // boot script 90 | runScript(1); 91 | 92 | //startScene(33); 93 | // graphics_updateScreen(); 94 | //DEBUG_HALT; 95 | 96 | while (1) 97 | { 98 | actors_animate(); 99 | actors_walk(); 100 | camera_move(); 101 | messages_update(); 102 | updateScummVars(); 103 | processScript(); 104 | checkExecVerbs(); 105 | checkAndRunSentenceScript(); 106 | cursor_animate(); 107 | 108 | graphics_updateScreen(); 109 | #ifdef HABR 110 | int n = 10000; 111 | while (n--) 112 | ++habr; 113 | #endif 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /graphics.h: -------------------------------------------------------------------------------- 1 | #ifndef GRAPHICS_H 2 | #define GRAPHICS_H 3 | 4 | #include "resource.h" 5 | #include "object.h" 6 | #include "actor.h" 7 | #include "verbs.h" 8 | 9 | #define SCREEN_TOP 2 10 | #define SCREEN_WIDTH 32 11 | #define SCREEN_HEIGHT 16 12 | #define LINE_WIDTH 40 13 | #define LINE_GAP ((LINE_WIDTH - SCREEN_WIDTH) / 2) 14 | #define TEXT_GAP (LINE_GAP + 2) 15 | #define SENTENCE_HEIGHT 2 16 | #define VERB_HEIGHT 4 17 | #define INV_GAP (LINE_GAP + 2) 18 | #define INV_WIDTH (SCREEN_WIDTH - 4) 19 | #define INV_TOP (SCREEN_TOP + SCREEN_HEIGHT + SENTENCE_HEIGHT + VERB_HEIGHT) 20 | #define INV_COLS 2 21 | #define INV_ROWS 2 22 | #define INV_ARR_W 4 23 | #define INV_SLOT_WIDTH ((INV_WIDTH - INV_ARR_W) / INV_COLS) 24 | #define INV_UP_X (INV_GAP + INV_SLOT_WIDTH + 1) 25 | #define INV_DOWN_X (INV_UP_X + 1) 26 | #define INV_ARR_Y (INV_TOP) 27 | 28 | #define TILEMAP_BASE 0x6000 29 | #define TILE_BASE 0x4000 30 | #define TILE_BYTES 2 31 | #define LINE_BYTES (LINE_WIDTH * TILE_BYTES) 32 | 33 | #define V12_X_MULTIPLIER 8 34 | #define V12_Y_MULTIPLIER 2 35 | #define V12_X_SHIFT 3 36 | #define V12_Y_SHIFT 1 37 | 38 | #define SPRITE_CURSOR 0 39 | 40 | /** Virtual screen identifiers */ 41 | enum VirtScreenNumber { 42 | kMainVirtScreen = 0, // The 'stage' 43 | kTextVirtScreen = 1, // In V0-V3 games: the area where text is printed 44 | kVerbVirtScreen = 2, // The verb area 45 | kUnkVirtScreen = 3 // ?? Not sure what this one is good for... 46 | }; 47 | 48 | enum { 49 | /** 50 | * Lighting flag that indicates whether the normal palette, or the 'dark' 51 | * palette shall be used to draw actors. 52 | * Apparantly only used in very old games (so far only NESCostumeRenderer 53 | * checks it). 54 | */ 55 | LIGHTMODE_actor_use_base_palette = 1 << 0, 56 | 57 | /** 58 | * Lighting flag that indicates whether the room is currently lit. Normally 59 | * always on. Used for rooms in which the light can be switched "off". 60 | */ 61 | LIGHTMODE_room_lights_on = 1 << 1, 62 | 63 | /** 64 | * Lighting flag that indicates whether a flashlight like device is active. 65 | * Used in Loom (flashlight follows the actor) and Indy 3 (flashlight 66 | * follows the mouse). Only has any effect if the room lights are off. 67 | */ 68 | LIGHTMODE_flashlight_on = 1 << 2, 69 | 70 | /** 71 | * Lighting flag that indicates whether actors are to be drawn with their 72 | * own custom palette, or using a fixed 'dark' palette. This is the 73 | * modern successor of LIGHTMODE_actor_use_base_palette. 74 | * Note: It is tempting to 'merge' these two flags, but since flags can 75 | * check their values, this is probably not a good idea. 76 | */ 77 | LIGHTMODE_actor_use_colors = 1 << 3 78 | // 79 | }; 80 | 81 | extern uint8_t costdesc[]; 82 | extern uint8_t costlens[]; 83 | extern uint8_t costoffs[]; 84 | 85 | extern uint8_t costdata_id; 86 | 87 | void initGraphics(void); 88 | 89 | void decodeNESGfx(HROOM r); 90 | void decodeRoomBackground(HROOM r); 91 | 92 | void graphics_clearScreen(void); 93 | void graphics_updateScreen(void); 94 | void graphics_clearInventory(void); 95 | 96 | void graphics_loadCostumeSet(uint8_t n); 97 | void graphics_print(const char *s, uint8_t c); 98 | void graphics_printSentence(const char *s); 99 | void graphics_drawObject(uint16_t bimoffs, uint8_t ox, uint8_t oy, uint8_t w, uint8_t h); 100 | void graphics_drawVerb(VerbSlot *v); 101 | void graphics_drawInventory(uint8_t slot, const char *s); 102 | void graphics_printAtXY(const uint8_t *s, uint8_t x, uint8_t y, uint8_t left, uint8_t color, uint8_t len); 103 | 104 | uint8_t graphics_findVirtScreen(uint8_t y); 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /sprites.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "sprites.h" 3 | #include "graphics.h" 4 | #include "room.h" 5 | #include "resource.h" 6 | #include "debug.h" 7 | #include "helper.h" 8 | 9 | static uint8_t baseTilesCount; 10 | //uint8_t spriteTiles[4096]; 11 | extern uint8_t spriteTiles[4096]; 12 | //#define tileBuf ((uint8_t*)0x3000) 13 | extern uint8_t tileBuf[4096]; 14 | 15 | #define BUF_PAGE 2 16 | #define TILEMAP_PAGE 10 17 | 18 | static uint16_t decodeNESTileData(uint8_t *buf, HROOM f, uint16_t len) 19 | { 20 | uint8_t *dst = buf; 21 | // decode NES tile data 22 | uint8_t count = readByte(f); 23 | uint8_t data = readByte(f); 24 | len -= 2; 25 | while (len) { 26 | for (uint8_t j = 0; j < (count & 0x7F); j++) 27 | { 28 | *dst++ = data; 29 | if (count & 0x80) 30 | { 31 | if (len) 32 | { 33 | data = readByte(f); 34 | --len; 35 | } 36 | } 37 | } 38 | if (count & 0x80) 39 | { 40 | count = data; 41 | } 42 | else 43 | { 44 | if (len) 45 | { 46 | count = readByte(f); 47 | --len; 48 | } 49 | } 50 | if (len) 51 | { 52 | data = readByte(f); 53 | --len; 54 | } 55 | } 56 | 57 | return dst - buf; 58 | } 59 | 60 | static void setTiles(uint16_t count) 61 | { 62 | PUSH_PAGE(0, TILEMAP_PAGE); 63 | uint8_t *tiles = (uint8_t*)0 + baseTilesCount * 32; 64 | //uint8_t *tiles = (uint8_t*)TILE_BASE + baseTilesCount * 32; 65 | if (!baseTilesCount) 66 | baseTilesCount = count; 67 | for (uint16_t c = 0 ; c < count ; ++c) 68 | { 69 | //if (c < 2) 70 | //DEBUG_PRINTF("Tile %u:\n", c); 71 | for (uint8_t i = 0 ; i < 8 ; ++i) 72 | { 73 | uint8_t c0 = tileBuf[c * 16 + i]; 74 | uint8_t c1 = tileBuf[c * 16 + i + 8]; 75 | for (uint8_t j = 0 ; j < 8 ; ++j) 76 | { 77 | uint8_t col1 = ((c0 >> (7 - j)) & 1) + (((c1 >> (7 - j)) & 1) << 1); 78 | ++j; 79 | uint8_t col2 = ((c0 >> (7 - j)) & 1) + (((c1 >> (7 - j)) & 1) << 1); 80 | uint8_t b = (col1 << 4) | col2; 81 | *tiles = b; 82 | ++tiles; 83 | // if (c == 1) 84 | // //DEBUG_PRINTF("%x%x", col1, col2); 85 | // DEBUG_PRINTF("%x %x ", tiles, b); 86 | } 87 | // DEBUG_PUTS("\n"); 88 | } 89 | // if (c == 1) 90 | // DEBUG_PUTS("\n"); 91 | //*screen++ = c; 92 | } 93 | POP_PAGE(0); 94 | } 95 | 96 | // Buffer should be in page >= 2 97 | static uint16_t decodeNESTiles(uint8_t *buf, uint8_t set) 98 | { 99 | HROOM f = seekResource(&costumes[set]); 100 | uint16_t len = readWord(f); 101 | uint16_t count = readByte(f); 102 | uint16_t bytes = decodeNESTileData(buf, f, len - 1); 103 | closeRoom(f); 104 | if (count == 0) 105 | count = bytes / 16; 106 | // DEBUG_PRINTF("NES tiles %u %u/%u\n", count, len, bytes); 107 | return count; 108 | } 109 | 110 | void decodeSpriteTiles(uint8_t set) 111 | { 112 | PUSH_PAGE(2, BUF_PAGE); 113 | decodeNESTiles(spriteTiles, set); 114 | POP_PAGE(2); 115 | } 116 | 117 | void graphics_loadSpritePattern(uint8_t nextSprite, uint8_t tile, uint8_t mask, uint8_t sprpal) 118 | { 119 | PUSH_PAGE(2, BUF_PAGE); 120 | 121 | uint8_t spr = (nextSprite & 0x3f) | ((nextSprite & 0x40) << 1); 122 | 123 | IO_SPRITE_SLOT = spr; 124 | uint8_t i; 125 | //DEBUG_PRINTF("tile: %x\n", tile); 126 | uint8_t *t = &spriteTiles[tile * 16]; 127 | for (i = 0 ; i < 8 ; ++i) 128 | { 129 | uint8_t c0 = t[i]; 130 | uint8_t c1 = t[i + 8]; 131 | // tile data is ok 132 | //DEBUG_PRINTF("tile line: %x %x\n", c0, c1); 133 | uint8_t c = 0; 134 | for (uint8_t j = 0 ; j < 8 ; ++j) 135 | { 136 | uint8_t cc = ((c0 & mask) ? 1 : 0) | ((c1 & mask) ? 2 : 0); 137 | // zero is transparent 138 | if (cc) 139 | cc |= sprpal; 140 | #ifdef HABR 141 | if (i == 0 || j == 0) 142 | { 143 | cc = 0x2; 144 | } 145 | #endif 146 | c = (c << 4) | cc; 147 | if (j & 1) 148 | { 149 | IO_SPRITE_PATTERN = c; 150 | } 151 | 152 | if (mask == 0x01) { 153 | c0 >>= 1; 154 | c1 >>= 1; 155 | } else { 156 | c0 <<= 1; 157 | c1 <<= 1; 158 | } 159 | } 160 | #ifdef HABR 161 | if (i == 0) 162 | { 163 | IO_SPRITE_PATTERN = 0x22; 164 | IO_SPRITE_PATTERN = 0x22; 165 | IO_SPRITE_PATTERN = 0x22; 166 | IO_SPRITE_PATTERN = 0x22; 167 | } 168 | else 169 | { 170 | IO_SPRITE_PATTERN = 0x0; 171 | IO_SPRITE_PATTERN = 0x0; 172 | IO_SPRITE_PATTERN = 0x0; 173 | IO_SPRITE_PATTERN = 0x02; 174 | } 175 | #else 176 | IO_SPRITE_PATTERN = 0x0; 177 | IO_SPRITE_PATTERN = 0x0; 178 | IO_SPRITE_PATTERN = 0x0; 179 | IO_SPRITE_PATTERN = 0x0; 180 | #endif 181 | } 182 | for (i = 0 ; i < 64 ; ++i) 183 | { 184 | #ifdef HABR 185 | if (i >= 64 - 8) 186 | IO_SPRITE_PATTERN = 0x22; 187 | else if (i % 8 == 0) 188 | IO_SPRITE_PATTERN = 0x20; 189 | else if (i % 8 == 7) 190 | IO_SPRITE_PATTERN = 0x02; 191 | else 192 | #endif 193 | IO_SPRITE_PATTERN = 0x0; 194 | } 195 | 196 | POP_PAGE(2); 197 | } 198 | 199 | void decodeTiles(uint8_t set) 200 | { 201 | // DEBUG_PRINTF("Decoding tiles set %u\n", set); 202 | PUSH_PAGE(2, BUF_PAGE); 203 | setTiles(decodeNESTiles(tileBuf, 37 + set)); 204 | POP_PAGE(2); 205 | } 206 | -------------------------------------------------------------------------------- /room.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "debug.h" 3 | #include "resource.h" 4 | #include "graphics.h" 5 | #include "camera.h" 6 | #include "object.h" 7 | #include "script.h" 8 | #include "box.h" 9 | 10 | uint8_t currentRoom; 11 | uint8_t roomWidth; 12 | uint8_t roomHeight; 13 | static uint16_t entryScriptOffs; 14 | static uint16_t exitScriptOffs; 15 | 16 | static void setupRoomSubBlocks(void) 17 | { 18 | HROOM r = openRoom(currentRoom); 19 | uint16_t size = readWord(r); 20 | readWord(r); 21 | roomWidth = readWord(r); // *8? 22 | roomHeight = readWord(r); // *8 ? 23 | esx_f_seek(r, 20, ESX_SEEK_SET); 24 | uint8_t numObj = readByte(r); 25 | uint8_t matrixOffs = readByte(r); 26 | uint8_t numSnd = readByte(r); 27 | uint8_t numScript = readByte(r); 28 | exitScriptOffs = readWord(r); 29 | entryScriptOffs = readWord(r); 30 | uint16_t exitLen = entryScriptOffs - exitScriptOffs;// + 4; 31 | uint16_t entryLen = size - entryScriptOffs;// + 4; 32 | // offset 28 - objects start here 33 | 34 | // then sound and scripts 35 | // ptr = roomptr + 28 + num_objects * 4; 36 | // while (num_sounds--) 37 | // loadResource(rtSound, *ptr++); 38 | // while (num_scripts--) 39 | // loadResource(rtScript, *ptr++); 40 | 41 | esx_f_seek(r, matrixOffs, ESX_SEEK_SET); 42 | // boxes 43 | numBoxes = readByte(r); 44 | DEBUG_ASSERT(numBoxes <= MAX_BOXES, "setupRoomSubBlocks"); 45 | readBuffer(r, (uint8_t*)boxes, sizeof(Box) * numBoxes); 46 | // boxes matrix 47 | readBuffer(r, boxesMatrix, numBoxes * (numBoxes + 1)); 48 | 49 | DEBUG_PRINTF("Open room %u width %u objects %u boxes %u\n", currentRoom, roomWidth, numObj, numBoxes); 50 | 51 | // int col, row; 52 | // debug_putc('\n'); 53 | // for (row = 0 ; row < roomHeight * 2 ; ++row) 54 | // { 55 | // for (col = 0 ; col < roomWidth ; ++col) 56 | // { 57 | // char c = '.'; 58 | // int i; 59 | // for (i = 0 ; i < numBoxes ; ++i) 60 | // { 61 | // if (col >= boxes[i].ulx && col <= boxes[i].urx 62 | // && row >= boxes[i].uy / 2 && row <= boxes[i].ly / 2) { 63 | // if (i < 10) 64 | // c = '0' + i; 65 | // else 66 | // c = 'A' + i; 67 | // break; 68 | // } 69 | // } 70 | // debug_putc(c); 71 | // } 72 | // debug_putc('\n'); 73 | // } 74 | 75 | // int i; 76 | // for (i = 0 ; i < numBoxes ; ++i) 77 | // { 78 | // DEBUG_PRINTF("Box %d uy=%d ly=%d ulx=%d urx=%d llx=%d lrx=%d mask=%x flags=%x\n", 79 | // i, boxes[i].uy, boxes[i].ly, boxes[i].ulx, boxes[i].urx, 80 | // boxes[i].llx, boxes[i].lrx, boxes[i].mask, boxes[i].flags 81 | // ); 82 | // } 83 | // int j; 84 | // uint8_t *ptr = boxesMatrix; 85 | // for (i = 0 ; i < numBoxes + 1 ; ++i) 86 | // { 87 | // for (j = 0 ; j < numBoxes ; ++j) 88 | // DEBUG_PRINTF("%d ", *ptr++); 89 | // DEBUG_PUTS("\n"); 90 | // } 91 | 92 | decodeNESGfx(r); 93 | setupRoomObjects(r); 94 | 95 | closeRoom(r); 96 | 97 | objects_redraw(); 98 | } 99 | 100 | void startScene(uint8_t room/*, Actor *a, int objectNr*/) 101 | { 102 | //int i, where; 103 | 104 | // debugC(DEBUG_GENERAL, "Loading room %d", room); 105 | 106 | actor_stopTalk(); 107 | graphics_clearScreen(); 108 | 109 | // fadeOut(_switchRoomEffect2); 110 | // _newEffect = _switchRoomEffect; 111 | 112 | // ScriptSlot *ss = &vm.slot[_currentScript]; 113 | 114 | // if (_currentScript != 0xFF) { 115 | // if (ss->where == WIO_ROOM || ss->where == WIO_FLOBJECT) { 116 | // if (ss->cutsceneOverride && _game.version >= 5) 117 | // error("Object %d stopped with active cutscene/override in exit", ss->number); 118 | 119 | // nukeArrays(_currentScript); 120 | // _currentScript = 0xFF; 121 | // } else if (ss->where == WIO_LOCAL) { 122 | // if (ss->cutsceneOverride && _game.version >= 5) 123 | // error("Script %d stopped with active cutscene/override in exit", ss->number); 124 | 125 | // nukeArrays(_currentScript); 126 | // _currentScript = 0xFF; 127 | // } 128 | // } 129 | 130 | // killScriptsAndResources(); 131 | 132 | // clearDrawQueues(); 133 | 134 | //actors_hide(); 135 | 136 | // for (i = 0; i < 256; i++) { 137 | // _roomPalette[i] = i; 138 | // if (_shadowPalette) 139 | // _shadowPalette[i] = i; 140 | // } 141 | // setDirtyColors(0, 255); 142 | 143 | // _fullRedraw = true; 144 | 145 | // _res->increaseResourceCounters(); 146 | 147 | scummVars[VAR_ROOM] = room; 148 | 149 | if (exitScriptOffs) 150 | { 151 | runRoomScript(-1, currentRoom, exitScriptOffs); 152 | } 153 | 154 | // clear before changing the room id 155 | objects_clear(); 156 | 157 | currentRoom = room; 158 | 159 | // _roomResource = room; 160 | 161 | scummVars[VAR_ROOM_RESOURCE] = room; 162 | 163 | // if (room != 0) 164 | // ensureResourceLoaded(rtRoom, room); 165 | 166 | if (currentRoom == 0) 167 | { 168 | // _numObjectsInRoom = 0; 169 | exitScriptOffs = 0; 170 | entryScriptOffs = 0; 171 | return; 172 | } 173 | 174 | setupRoomSubBlocks(); 175 | // resetRoomSubBlocks(); 176 | 177 | // initBGBuffers(_roomHeight); 178 | 179 | //resetRoomObjects(); 180 | 181 | scummVars[VAR_CAMERA_MIN_X] = SCREEN_WIDTH / 2; 182 | scummVars[VAR_CAMERA_MAX_X] = roomWidth - SCREEN_WIDTH / 2; 183 | 184 | camera_setX(roomWidth / 2); 185 | // camera._mode = kNormalCameraMode; 186 | // camera._cur.y = camera._dest.y = _screenHeight / 2; 187 | 188 | // if (_roomResource == 0) 189 | // return; 190 | 191 | // memset(gfxUsageBits, 0, sizeof(gfxUsageBits)); 192 | 193 | actors_show(); 194 | 195 | // _egoPositioned = false; 196 | 197 | if (entryScriptOffs) 198 | { 199 | runRoomScript(-1, room, entryScriptOffs); 200 | } 201 | 202 | // this is for common rooms, should be run in parallel 203 | runScript(5); 204 | 205 | // _doEffect = true; 206 | } 207 | -------------------------------------------------------------------------------- /box.c: -------------------------------------------------------------------------------- 1 | #include "box.h" 2 | #include "helper.h" 3 | #include "debug.h" 4 | 5 | uint8_t numBoxes; 6 | Box boxes[MAX_BOXES]; 7 | uint8_t boxesMatrix[MAX_BOXES * (MAX_BOXES + 1)]; 8 | 9 | static uint8_t box_check_xy_in_bounds(Box *box, 10 | uint8_t x, uint8_t y, uint8_t *destX, uint8_t *destY) 11 | { 12 | uint8_t xmin, xmax; 13 | // We are supposed to determine the point (destX,destY) contained in 14 | // the given box which is closest to the point (x,y), and then return 15 | // some kind of "distance" between the two points. 16 | 17 | // First, we determine destY and a range (xmin to xmax) in which destX 18 | // is contained. 19 | if (y < box->uy) { 20 | // Point is above the box 21 | *destY = box->uy; 22 | xmin = box->ulx; 23 | xmax = box->urx; 24 | } else if (y >= box->ly) { 25 | // Point is below the box 26 | *destY = box->ly; 27 | xmin = box->llx; 28 | xmax = box->lrx; 29 | } else if (x >= box->ulx && x >= box->llx && x < box->urx && x < box->lrx) { 30 | // Point is strictly inside the box 31 | *destX = x; 32 | *destY = y; 33 | xmin = xmax = x; 34 | } else { 35 | // Point is to the left or right of the box, 36 | // so the y coordinate remains unchanged 37 | *destY = y; 38 | uint8_t ul = box->ulx; 39 | uint8_t ll = box->llx; 40 | uint8_t ur = box->urx; 41 | uint8_t lr = box->lrx; 42 | uint8_t top = box->uy; 43 | uint8_t bottom = box->ly; 44 | uint8_t cury; 45 | 46 | // Perform a binary search to determine the x coordinate. 47 | // Note: It would be possible to compute this value in a 48 | // single step simply by calculating the slope of the left 49 | // resp. right side and using that to find the correct 50 | // result. However, the original engine did use the search 51 | // approach, so we do that, too. 52 | do { 53 | xmin = (ul + ll) / 2; 54 | xmax = (ur + lr) / 2; 55 | cury = (top + bottom) / 2; 56 | 57 | if (cury < y) { 58 | top = cury; 59 | ul = xmin; 60 | ur = xmax; 61 | } else if (cury > y) { 62 | bottom = cury; 63 | ll = xmin; 64 | lr = xmax; 65 | } 66 | } while (cury != y); 67 | } 68 | 69 | // Now that we have limited the value of destX to a fixed 70 | // interval, it's a trivial matter to finally determine it. 71 | if (x < xmin) { 72 | *destX = xmin; 73 | } else if (x > xmax) { 74 | *destX = xmax; 75 | } else { 76 | *destX = x; 77 | } 78 | 79 | // Compute the distance of the points. We measure the 80 | // distance with a granularity of 8x8 blocks only (hence 81 | // yDist must be divided by 4, as we are using 8x2 pixels 82 | // blocks for actor coordinates). 83 | uint8_t xDist = ABS(x - *destX); 84 | uint8_t yDist = ABS(y - *destY) / 4; 85 | uint8_t dist; 86 | 87 | if (xDist < yDist) 88 | dist = (xDist >> 1) + yDist; 89 | else 90 | dist = (yDist >> 1) + xDist; 91 | 92 | return dist; 93 | } 94 | 95 | /** 96 | * Return the square of the distance between two points. 97 | */ 98 | uint16_t sqrDist(int8_t x1, int8_t y1, int8_t x2, int8_t y2) 99 | { 100 | uint16_t diffx = ABS(x1 - x2); 101 | // if (diffx >= 0x1000) 102 | // return 0xFFFFFF; 103 | 104 | uint16_t diffy = ABS(y1 - y2); 105 | // if (diffy >= 0x1000) 106 | // return 0xFFFFFF; 107 | 108 | return diffx * diffx + diffy * diffy; 109 | } 110 | 111 | 112 | /** 113 | * Find the point on a line segment which is closest to a given point. 114 | */ 115 | static void closestPtOnLine(uint8_t sx, uint8_t sy, uint8_t ex, uint8_t ey, 116 | uint8_t x, uint8_t y, uint8_t *ox, uint8_t *oy) 117 | { 118 | const int lxdiff = ex - sx; 119 | const int lydiff = ey - sy; 120 | 121 | if (ex == sx) { // Vertical line? 122 | *ox = sx; 123 | *oy = y; 124 | } else if (ey == sy) { // Horizontal line? 125 | *ox = x; 126 | *oy = sy; 127 | } else { 128 | const int dist = lxdiff * lxdiff + lydiff * lydiff; 129 | int a, b, c; 130 | if (ABS(lxdiff) > ABS(lydiff)) { 131 | a = sx * lydiff / lxdiff; 132 | b = x * lxdiff / lydiff; 133 | 134 | c = (a + b - sy + y) * lydiff * lxdiff / dist; 135 | 136 | *ox = c; 137 | *oy = c * lydiff / lxdiff - a + sy; 138 | } else { 139 | a = sy * lxdiff / lydiff; 140 | b = y * lydiff / lxdiff; 141 | 142 | c = (a + b - sx + x) * lydiff * lxdiff / dist; 143 | 144 | *ox = c * lxdiff / lydiff - a + sx; 145 | *oy = c; 146 | } 147 | } 148 | 149 | if (ABS(lydiff) < ABS(lxdiff)) 150 | { 151 | if (lxdiff > 0) { 152 | if (*ox < sx) 153 | { 154 | *ox = sx; 155 | *oy = sy; 156 | } 157 | else if (*ox > ex) 158 | { 159 | *ox = ex; 160 | *oy = ey; 161 | } 162 | } else { 163 | if (*ox > sx) 164 | { 165 | *ox = sx; 166 | *oy = sy; 167 | } 168 | else if (*ox < ex) 169 | { 170 | *ox = ex; 171 | *oy = ey; 172 | } 173 | } 174 | } else { 175 | if (lydiff > 0) { 176 | if (*oy < sy) 177 | { 178 | *ox = sx; 179 | *oy = sy; 180 | } 181 | else if (*oy > ey) 182 | { 183 | *ox = ex; 184 | *oy = ey; 185 | } 186 | } else { 187 | if (*oy > sy) 188 | { 189 | *ox = sx; 190 | *oy = sy; 191 | } 192 | else if (*oy < ey) 193 | { 194 | *ox = ex; 195 | *oy = ey; 196 | } 197 | } 198 | } 199 | } 200 | 201 | uint8_t box_getClosestPtOnBox(uint8_t b, uint8_t x, uint8_t y, uint8_t *outX, uint8_t *outY) 202 | { 203 | Box *box = &boxes[b]; 204 | uint8_t tx, ty; 205 | uint16_t dist; 206 | uint16_t bestdist = 0xFFFF; 207 | 208 | closestPtOnLine(box->ulx, box->uy, box->urx, box->uy, x, y, &tx, &ty); 209 | dist = sqrDist(x, y, tx, ty); 210 | if (dist < bestdist) { 211 | bestdist = dist; 212 | *outX = tx; 213 | *outY = ty; 214 | } 215 | 216 | closestPtOnLine(box->urx, box->uy, box->lrx, box->ly, x, y, &tx, &ty); 217 | dist = sqrDist(x, y, tx, ty); 218 | if (dist < bestdist) { 219 | bestdist = dist; 220 | *outX = tx; 221 | *outY = ty; 222 | } 223 | 224 | closestPtOnLine(box->lrx, box->ly, box->llx, box->ly, x, y, &tx, &ty); 225 | dist = sqrDist(x, y, tx, ty); 226 | if (dist < bestdist) { 227 | bestdist = dist; 228 | *outX = tx; 229 | *outY = ty; 230 | } 231 | 232 | closestPtOnLine(box->llx, box->ly, box->ulx, box->uy, x, y, &tx, &ty); 233 | dist = sqrDist(x, y, tx, ty); 234 | if (dist < bestdist) { 235 | bestdist = dist; 236 | *outX = tx; 237 | *outY = ty; 238 | } 239 | 240 | return bestdist; 241 | } 242 | 243 | static uint8_t compareSlope(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3) { 244 | return (y2 - y1) * (x3 - x1) <= (y3 - y1) * (x2 - x1); 245 | } 246 | 247 | uint8_t box_checkXYInBounds(uint8_t b, uint8_t x, uint8_t y) 248 | { 249 | DEBUG_ASSERT(b < MAX_BOXES, "box_checkXYInBounds max"); 250 | Box *box = &boxes[b]; 251 | 252 | // Quick check: If the x (resp. y) coordinate of the point is 253 | // strictly smaller (bigger) than the x (y) coordinates of all 254 | // corners of the quadrangle, then it certainly is *not* contained 255 | // inside the quadrangle. 256 | if (x < box->ulx && x < box->urx && x < box->lrx && x < box->llx) 257 | return 0; 258 | 259 | if (x > box->ulx && x > box->urx && x > box->lrx && x > box->llx) 260 | return 0; 261 | 262 | if (y < box->uy && y < box->ly) 263 | return 0; 264 | 265 | if (y > box->uy && y > box->ly) 266 | return 0; 267 | 268 | // TODO: support simple line segment 269 | if ((box->ulx == box->urx && box->lrx == box->llx) || 270 | (box->ulx == box->llx && box->uy == box->ly && box->urx == box->lrx)) 271 | { 272 | DEBUG_ASSERT(0, "box_checkXYInBounds"); 273 | } 274 | 275 | 276 | // Finally, fall back to the classic algorithm to compute containment 277 | // in a convex polygon: For each (oriented) side of the polygon 278 | // (quadrangle in this case), compute whether p is "left" or "right" 279 | // from it. 280 | 281 | if (!compareSlope(box->ulx, box->uy, box->urx, box->uy, x, y)) 282 | return 0; 283 | 284 | if (!compareSlope(box->urx, box->uy, box->lrx, box->ly, x, y)) 285 | return 0; 286 | 287 | if (!compareSlope(box->lrx, box->ly, box->llx, box->ly, x, y)) 288 | return 0; 289 | 290 | if (!compareSlope(box->llx, box->ly, box->ulx, box->uy, x, y)) 291 | return 0; 292 | 293 | return 1; 294 | } 295 | 296 | 297 | uint8_t boxes_adjustXY(uint8_t *px, uint8_t *py) 298 | { 299 | int8_t i = numBoxes - 1; 300 | uint8_t bestDist = 0xFF; 301 | uint8_t box = 0xFF; 302 | uint8_t x = *px, y = *py; 303 | for ( ; i >= 0 ; --i) 304 | { 305 | // if ((flags & kBoxInvisible) && !((flags & kBoxPlayerOnly) && !isPlayer())) 306 | // continue; 307 | uint8_t foundX, foundY; 308 | uint8_t dist = box_check_xy_in_bounds(&boxes[i], x, y, &foundX, &foundY); 309 | //DEBUG_PRINTF("Dist %d(%d,%d) for box %d x=%d\n", dist, x, y, i, foundX); 310 | if (dist == 0) { 311 | *px = foundX; 312 | *py = foundY; 313 | box = i; 314 | break; 315 | } 316 | if (dist < bestDist) { 317 | bestDist = dist; 318 | *px = foundX; 319 | *py = foundY; 320 | box = i; 321 | } 322 | } 323 | 324 | return box; 325 | } 326 | 327 | /** 328 | * Compute if there is a way that connects box 'from' with box 'to'. 329 | * Returns the number of a box adjacent to 'from' that is the next on the 330 | * way to 'to' (this can be 'to' itself or a third box). 331 | * If there is no connection -1 is return. 332 | */ 333 | int8_t boxes_getNext(int8_t from, int8_t to) 334 | { 335 | uint8_t *boxm = boxesMatrix; 336 | 337 | if (from == to) 338 | return to; 339 | 340 | if (to == -1) 341 | return -1; 342 | 343 | if (from == -1) 344 | return to; 345 | 346 | // The v2 box matrix is a real matrix with numOfBoxes rows and columns. 347 | // The first numOfBoxes bytes contain indices to the start of the corresponding 348 | // row (although that seems unnecessary to me - the value is easily computable. 349 | boxm += numBoxes + boxm[from]; 350 | return boxm[to]; 351 | } 352 | -------------------------------------------------------------------------------- /costume.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "costume.h" 4 | #include "resource.h" 5 | #include "graphics.h" 6 | #include "room.h" 7 | #include "sprites.h" 8 | #include "helper.h" 9 | #include "script.h" 10 | #include "debug.h" 11 | 12 | #define COST_PAGE0 48 13 | #define COST_PAGE1 49 14 | 15 | #define COST_SPRITES 16 16 | #define FIRST_SPRITE 1 17 | 18 | extern uint8_t costume31[8192]; 19 | extern uint8_t costume31_tail[11234]; 20 | extern uint8_t costume32[140]; 21 | extern uint8_t actCostumes[2048]; 22 | extern uint8_t *costumesPtr[_numCostumes]; 23 | extern uint16_t actCostumesUsed; 24 | extern uint8_t anchors[6]; 25 | 26 | static const uint8_t v1MMNESLookup[25] = { 27 | 0x00, 0x03, 0x01, 0x06, 0x08, 28 | 0x02, 0x00, 0x07, 0x0C, 0x04, 29 | 0x09, 0x0A, 0x12, 0x0B, 0x14, 30 | 0x0D, 0x11, 0x0F, 0x0E, 0x10, 31 | 0x17, 0x00, 0x01, 0x05, 0x16 32 | }; 33 | 34 | static const uint8_t *getCostume(uint8_t cost) 35 | { 36 | if (!costumesPtr[cost]) 37 | { 38 | uint8_t *ptr = actCostumes + actCostumesUsed; 39 | HROOM src = seekResource(costumes + cost); 40 | uint16_t sz = readResource(src, ptr, sizeof(actCostumes) - actCostumesUsed); 41 | closeRoom(src); 42 | actCostumesUsed += sz; 43 | DEBUG_ASSERT(actCostumesUsed <= sizeof(actCostumes), "getCostume"); 44 | costumesPtr[cost] = ptr; 45 | } 46 | return costumesPtr[cost]; 47 | } 48 | 49 | static uint8_t getPalOffs(void) 50 | { 51 | if (!(scummVars[VAR_CURRENT_LIGHTS] & LIGHTMODE_actor_use_base_palette)) 52 | return 1; 53 | return 0; 54 | } 55 | 56 | // should be called with MMU2=COST_PAGE1 57 | static void decodeNESCostume(Actor *act, uint8_t nextSprite) 58 | { 59 | //DEBUG_PRINTF("Decode for %d\n", nextSprite); 60 | 61 | // costume ID -> v1MMNESLookup[] -> desc -> lens & offs -> data -> Gfx & pal 62 | // _baseptr = _vm->getResourceAddress(rtCostume, id); 63 | // _dataOffsets = _baseptr + 2; 64 | //DEBUG_PRINTF("Decoding costume %d\n", costumes[act->costume]); 65 | const uint8_t *cost = getCostume(act->costume); 66 | 67 | // int anim; 68 | 69 | //uint16_t size = readWord(src); 70 | cost += 2; 71 | //DEBUG_PRINTF("Costume %d for actor %s\n", act->costume, act->name); 72 | // read data offsets from src 73 | 74 | // limb = 0 for v2 scumm 75 | // anim = 4 * cost.frame[limb] + newDirToOldDir(a->getFacing()); 76 | // frameNum = cost.curpos[limb]; 77 | // frame = src[src[2 * anim] + frameNum]; 78 | 79 | // _numAnim = 0x17 => numframes = 6 80 | uint8_t anim = act->frame * 4 + act->facing; 81 | //esx_f_seek(src, 2 * anim, ESX_SEEK_FWD); 82 | uint8_t begin = cost[2 * anim];//readByte(src); 83 | uint8_t end = cost[2 * anim + 1];//readByte(src); 84 | //DEBUG_PRINTF("Decode animation %u/%u of %u frames from %u\n", anim, act->costume, end, begin); 85 | //DEBUG_ASSERT(end <= MAX_FRAMES, "decodeNESCostume"); 86 | //esx_f_seek(src, begin - 2 * anim - 2, ESX_SEEK_FWD); 87 | uint8_t attr2 = getPalOffs() << 4; 88 | 89 | act->frames = end; 90 | uint8_t flipped = act->facing == 1; 91 | uint8_t frame = cost[begin + act->curpos];//*cost++;//readByte(src); 92 | 93 | uint16_t offset = READ_LE_UINT16(costdesc + v1MMNESLookup[act->costume] * 2 + 2); 94 | uint8_t numSprites = costlens[offset + frame + 2] + 1; 95 | //HROOM sprdata = seekResource(&costumes[costdata_id]); 96 | //DEBUG_PRINTF("Frame %u numspr %u\n", f, numSprites); 97 | // offset is the beginning 98 | // in scummvm data is decoded in backwards direction, from the end 99 | uint16_t sprOffs = READ_LE_UINT16(costoffs + 2 * (offset + frame) + 2) + 2; 100 | //DEBUG_PRINTF("decode frame=%u numspr=%u offset=%u\n", frame, numSprites, offset); 101 | //esx_f_seek(sprdata, sprOffs, ESX_SEEK_FWD); 102 | 103 | uint8_t *sprdata = costume32; 104 | if (costdata_id == 31) 105 | { 106 | sprdata = costume31; 107 | ZXN_WRITE_MMU2(COST_PAGE0); 108 | } 109 | uint8_t *sprite = sprdata + sprOffs; 110 | 111 | // bool flipped = (newDirToOldDir(a->getFacing()) == 1); 112 | // int left = 239, right = 0, top = 239, bottom = 0; 113 | // byte *maskBuf = _vm->getMaskBuffer(0, 0, 1); 114 | 115 | act->anchor = nextSprite; 116 | 117 | // setup anchor and relative sprite attributes and patterns 118 | DEBUG_ASSERT(numSprites <= COST_SPRITES, "COST_SPRITES"); 119 | 120 | #define inc_sprite do { \ 121 | ++sprite; \ 122 | if ((uint16_t)sprite >= 0x6000) { \ 123 | sprite -= 0x2000; \ 124 | ZXN_WRITE_MMU2(COST_PAGE1); \ 125 | } \ 126 | } while (0) 127 | 128 | uint8_t spr; 129 | for (spr = 0 ; spr < numSprites ; spr++) 130 | { 131 | uint8_t d0 = *sprite; 132 | inc_sprite; 133 | uint8_t d1 = *sprite; 134 | inc_sprite; 135 | uint8_t d2 = *sprite; 136 | inc_sprite; 137 | uint8_t mask; 138 | uint8_t sprpal = 0; 139 | int8_t y, x; 140 | 141 | mask = (d0 & 0x80) ? 0x01 : 0x80; 142 | 143 | y = d0 << 1; 144 | y >>= 1; 145 | 146 | uint8_t tile = d1; 147 | 148 | sprpal = (d2 & 0x03) << 2; 149 | x = d2; 150 | x >>= 2; 151 | 152 | if (flipped) { 153 | mask ^= 0x81;//(mask == 0x80) ? 0x01 : 0x80; 154 | x = -x; 155 | } 156 | 157 | // setup tile 158 | graphics_loadSpritePattern(nextSprite, tile, mask, sprpal); 159 | 160 | // send attributes 161 | IO_SPRITE_SLOT = nextSprite; 162 | if (!spr) 163 | { 164 | act->ax = x; 165 | act->ay = y; 166 | } 167 | 168 | x = x - act->ax; 169 | y = y - act->ay; 170 | IO_SPRITE_ATTRIBUTE = x; 171 | IO_SPRITE_ATTRIBUTE = y; 172 | IO_SPRITE_ATTRIBUTE = attr2; 173 | 174 | if (!spr) 175 | { 176 | // invisible yet 177 | IO_SPRITE_ATTRIBUTE = 0x40 | (nextSprite & 0x3f); 178 | // anchor 4-bit sprite 179 | IO_SPRITE_ATTRIBUTE = 0x80 | (nextSprite & 0x40); 180 | } 181 | else 182 | { 183 | // visible and enable 5 byte attribyte 184 | IO_SPRITE_ATTRIBUTE = 0xc0 | (nextSprite & 0x3f); 185 | // relative sprite 186 | IO_SPRITE_ATTRIBUTE = 0x40 | ((nextSprite & 0x40) >> 1); 187 | } 188 | 189 | //DEBUG_PRINTF("Sprite %d tile=%d/%d x=%d y=%d\n", nextSprite, tile, flipped, x, y); 190 | 191 | ++nextSprite; 192 | } 193 | #undef inc_sprite 194 | // clean other sprites in the slot 195 | while (spr < COST_SPRITES) 196 | { 197 | IO_SPRITE_SLOT = nextSprite; 198 | IO_SPRITE_ATTRIBUTE = 0; 199 | IO_SPRITE_ATTRIBUTE = 0; 200 | IO_SPRITE_ATTRIBUTE = 0; 201 | IO_SPRITE_ATTRIBUTE = 0; 202 | ++nextSprite; 203 | ++spr; 204 | } 205 | 206 | ZXN_WRITE_MMU2(COST_PAGE1); 207 | } 208 | 209 | void costume_loadData(void) 210 | { 211 | PUSH_PAGE(2, COST_PAGE0); 212 | 213 | HROOM cost = seekResource(&costumes[31]); 214 | //readResource(cost, costume31, sizeof(costume31)); 215 | uint16_t size = readWord(cost); 216 | esx_f_seek(cost, 2, ESX_SEEK_BWD); 217 | readBuffer(cost, costume31, sizeof(costume31)); 218 | size -= sizeof(costume31); 219 | 220 | ZXN_WRITE_MMU2(COST_PAGE1); 221 | 222 | readBuffer(cost, costume31_tail, size); 223 | closeRoom(cost); 224 | 225 | cost = seekResource(&costumes[32]); 226 | readResource(cost, costume32, sizeof(costume32)); 227 | closeRoom(cost); 228 | 229 | POP_PAGE(2); 230 | } 231 | 232 | void costume_updateAll(void) 233 | { 234 | uint8_t i; 235 | 236 | // clean all sprites 237 | for (i = 0 ; i <= 127 ; ++i) 238 | { 239 | IO_SPRITE_SLOT = i; 240 | IO_SPRITE_ATTRIBUTE = 0; 241 | IO_SPRITE_ATTRIBUTE = 0; 242 | IO_SPRITE_ATTRIBUTE = 0; 243 | IO_SPRITE_ATTRIBUTE = 0; 244 | } 245 | 246 | // we always need a sprite for the cursor 247 | graphics_loadSpritePattern(SPRITE_CURSOR, 248 | // some hack for cursor id 249 | costdata_id == 32 ? 0xfe : 0xfa, 0x80, 1); 250 | 251 | PUSH_PAGE(2, COST_PAGE1); 252 | 253 | for (i = 0 ; i < sizeof(anchors) ; ++i) 254 | anchors[i] = 0; 255 | 256 | //DEBUG_PUTS("Update all costumes\n"); 257 | 258 | // decode costume sprites 259 | uint8_t nextSprite = FIRST_SPRITE; 260 | uint8_t a = 0; 261 | for (i = 0 ; i < ACTOR_COUNT ; ++i) 262 | { 263 | if (currentRoom && actors[i].room == currentRoom) 264 | { 265 | anchors[a] = 1; 266 | ++a; 267 | actors[i].old_anchor = 0; 268 | DEBUG_ASSERT(nextSprite <= 127 && a <= sizeof(anchors), "costume_updateAll"); 269 | decodeNESCostume(&actors[i], nextSprite); 270 | nextSprite += COST_SPRITES; 271 | } 272 | } 273 | 274 | POP_PAGE(2); 275 | } 276 | 277 | void costume_updateActor(Actor *act) 278 | { 279 | PUSH_PAGE(2, COST_PAGE1); 280 | 281 | uint8_t anchor = act->anchor; 282 | uint8_t a = 0; 283 | if (act->room == currentRoom) 284 | { 285 | if (act->old_anchor) 286 | { 287 | a = act->old_anchor / COST_SPRITES; 288 | } 289 | else 290 | { 291 | while (anchors[a]) 292 | ++a; 293 | } 294 | DEBUG_ASSERT(a < sizeof(anchors), "costume_updateActor"); 295 | // sets act->anchor 296 | decodeNESCostume(act, FIRST_SPRITE + a * COST_SPRITES); 297 | anchors[a] = 1; 298 | } 299 | act->old_anchor = anchor; 300 | 301 | //DEBUG_PRINTF("Update costume old=%d new=%d\n", anchor, FIRST_SPRITE + a * COST_SPRITES); 302 | 303 | POP_PAGE(2); 304 | } 305 | 306 | void actors_draw(uint8_t offs, uint8_t gap) 307 | { 308 | PUSH_PAGE(2, COST_PAGE1); 309 | 310 | uint8_t i; 311 | uint8_t attr2 = getPalOffs() << 4; 312 | // draw actors 313 | for (i = 0 ; i < ACTOR_COUNT ; ++i) 314 | { 315 | Actor *act = &actors[i]; 316 | if (act->room == currentRoom) 317 | { 318 | int16_t xx = act->x * V12_X_MULTIPLIER + act->ax - offs * 8; 319 | if (act->old_anchor) 320 | { 321 | //DEBUG_PRINTF("Hide %d %d\n", i, act->old_anchor); 322 | // switch old sprite off 323 | IO_SPRITE_SLOT = act->old_anchor; 324 | IO_SPRITE_ATTRIBUTE = 0; 325 | IO_SPRITE_ATTRIBUTE = 0; 326 | IO_SPRITE_ATTRIBUTE = 0; 327 | // invisible 328 | IO_SPRITE_ATTRIBUTE = 0x40; 329 | IO_SPRITE_ATTRIBUTE = 0x80; 330 | anchors[act->old_anchor / COST_SPRITES] = 0; 331 | act->old_anchor = 0; 332 | } 333 | uint8_t anchor = act->anchor; 334 | //DEBUG_PRINTF("Show %d %d\n", i, anchor); 335 | IO_SPRITE_SLOT = anchor; 336 | if (xx < 0 || xx > SCREEN_WIDTH * 8) 337 | { 338 | IO_SPRITE_ATTRIBUTE = 0; 339 | IO_SPRITE_ATTRIBUTE = 0; 340 | IO_SPRITE_ATTRIBUTE = 0; 341 | // invisible 342 | IO_SPRITE_ATTRIBUTE = 0x40 | (anchor & 0x3f); 343 | // anchor 4-bit sprite 344 | IO_SPRITE_ATTRIBUTE = 0x80 | (anchor & 0x40); 345 | continue; 346 | } 347 | xx += gap * 8; 348 | uint8_t y = act->y * V12_Y_MULTIPLIER + act->ay + SCREEN_TOP * 8; 349 | IO_SPRITE_ATTRIBUTE = xx; 350 | IO_SPRITE_ATTRIBUTE = y; 351 | IO_SPRITE_ATTRIBUTE = attr2 | (((uint16_t)xx >> 8) & 1); 352 | IO_SPRITE_ATTRIBUTE = 0xc0 | (anchor & 0x3f); 353 | IO_SPRITE_ATTRIBUTE = 0x80 | (anchor & 0x40); 354 | //DEBUG_PRINTF("Actor %u x=%u y=%u\n", i, xx, y); 355 | //graphics_drawActor(actors + i); 356 | //a->animateCostume(); 357 | } 358 | } 359 | 360 | POP_PAGE(2); 361 | } 362 | -------------------------------------------------------------------------------- /actor.c: -------------------------------------------------------------------------------- 1 | #include "actor.h" 2 | #include "graphics.h" 3 | #include "debug.h" 4 | #include "room.h" 5 | #include "costume.h" 6 | #include "box.h" 7 | #include "helper.h" 8 | #include "object.h" 9 | #include "script.h" 10 | #include "string.h" 11 | 12 | #define _walkFrame 0 13 | #define _standFrame 1 14 | #define _initFrame 2 15 | #define _talkStopFrame 4 16 | #define _talkStartFrame 5 17 | 18 | Actor actors[ACTOR_COUNT]; 19 | 20 | enum MoveFlags { 21 | MF_NEW_LEG = 1, 22 | MF_IN_LEG = 2, 23 | MF_TURN = 4, 24 | MF_LAST_LEG = 8, 25 | MF_FROZEN = 0x80 26 | }; 27 | 28 | uint8_t defaultTalkColor; 29 | static uint8_t talkingActor; 30 | 31 | /////////////////////////////////////////////////////////////////////////////// 32 | // Actor internals 33 | /////////////////////////////////////////////////////////////////////////////// 34 | 35 | static void actor_setBox(Actor *a, uint8_t box) 36 | { 37 | a->walkbox = box; 38 | // setupActorScale(); 39 | // DEBUG_PRINTF("Set box %u\n", box); 40 | } 41 | 42 | static void startAnimActor(Actor *a, uint8_t f) 43 | { 44 | //DEBUG_PRINTF("Actor %u start anim %u\n", a - actors, f); 45 | // then void Actor::startAnimActor(int f = anim/4) 46 | // _animProgress = 0; 47 | // _needRedraw = true; 48 | // _cost.animCounter = 0; 49 | a->frame = f; 50 | a->curpos = 0; 51 | costume_updateActor(a); 52 | } 53 | 54 | static void actor_calcMovementFactor(Actor *a, uint8_t nextX, uint8_t nextY) 55 | { 56 | const uint32_t speedy = 1; 57 | const uint32_t speedx = 1; 58 | int8_t diffX, diffY; 59 | 60 | if (a->x == nextX && a->y == nextY) 61 | return; 62 | 63 | diffX = nextX - a->x; 64 | diffY = nextY - a->y; 65 | a->deltaYFactor = speedy << 16; 66 | 67 | if (diffY < 0) 68 | a->deltaYFactor = -a->deltaYFactor; 69 | 70 | a->deltaXFactor = a->deltaYFactor * diffX; 71 | if (diffY != 0) { 72 | a->deltaXFactor /= diffY; 73 | } else { 74 | a->deltaYFactor = 0; 75 | } 76 | 77 | if (ABS(a->deltaXFactor) > (speedx << 16)) { 78 | a->deltaXFactor = speedx << 16; 79 | if (diffX < 0) 80 | a->deltaXFactor = -a->deltaXFactor; 81 | 82 | a->deltaYFactor = a->deltaXFactor * diffY; 83 | if (diffX != 0) { 84 | a->deltaYFactor /= diffX; 85 | } else { 86 | a->deltaXFactor = 0; 87 | } 88 | } 89 | 90 | a->curX = a->x; 91 | a->curY = a->y; 92 | a->nextX = nextX; 93 | a->nextY = nextY; 94 | 95 | uint8_t dir = 0; 96 | int32_t xx = V12_X_MULTIPLIER * a->deltaXFactor; 97 | xx = ABS(xx); 98 | int32_t yy = V12_Y_MULTIPLIER * a->deltaYFactor; 99 | yy = ABS(yy); 100 | if (yy * 2 < xx) 101 | { 102 | if (a->deltaXFactor > 0) 103 | dir = 1; // 90 104 | else 105 | dir = 0; // 270 106 | } 107 | else 108 | { 109 | if (a->deltaYFactor > 0) 110 | dir = 2; // 180 111 | else 112 | dir = 3; // 0 113 | } 114 | 115 | a->targetFacing = dir; 116 | // _targetFacing = getAngleFromPos(V12_X_MULTIPLIER*deltaXFactor, V12_Y_MULTIPLIER*deltaYFactor, false); 117 | } 118 | 119 | static void actor_walkStep(Actor *a) 120 | { 121 | if (a->facing != a->targetFacing) 122 | { 123 | // nextFacing = updateActorDirection(true); 124 | // TODO nextFacing 125 | a->moving |= MF_TURN; 126 | } 127 | if (!(a->moving & MF_IN_LEG)/* || _facing != nextFacing*/) 128 | { 129 | // if (_walkFrame != _frame || _facing != nextFacing) { 130 | // startWalkAnim(1, nextFacing); 131 | // } 132 | startAnimActor(a, _walkFrame); 133 | a->moving |= MF_IN_LEG; 134 | } 135 | 136 | if (a->walkbox != a->curbox && box_checkXYInBounds(a->curbox, a->x, a->y)) 137 | { 138 | actor_setBox(a, a->curbox); 139 | } 140 | 141 | //DEBUG_PRINTF("factorX=%l factorY=%l\n", deltaXFactor, deltaYFactor); 142 | 143 | uint8_t distX = ABS(a->nextX - a->curX); 144 | uint8_t distY = ABS(a->nextY - a->curY); 145 | 146 | if (ABS(a->x - a->curX) >= distX && ABS(a->y - a->curY) >= distY) { 147 | a->moving &= ~MF_IN_LEG; 148 | return; 149 | } 150 | 151 | // DEBUG_PRINTF("Moving diff %d %d\n", diffX, diffY); 152 | 153 | if (a->deltaXFactor != 0) 154 | { 155 | if (a->deltaXFactor > 0) 156 | a->x += 1; 157 | else 158 | a->x -= 1; 159 | } 160 | if (a->deltaYFactor != 0) 161 | { 162 | if (a->deltaYFactor > 0) 163 | a->y += 1; 164 | else 165 | a->y -= 1; 166 | } 167 | 168 | if (ABS(a->x - a->curX) > distX) { 169 | a->x = a->nextX; 170 | } 171 | 172 | if (ABS(a->y - a->curY) > distY) { 173 | a->y = a->nextY; 174 | } 175 | 176 | if (a->x == a->nextX && a->y == a->nextY) 177 | a->moving &= ~MF_IN_LEG; 178 | } 179 | 180 | static void actor_walk(Actor *a) 181 | { 182 | uint8_t foundX, foundY; 183 | int8_t next_box; 184 | // Common::Point foundPath, tmp; 185 | // int new_dir, next_box; 186 | 187 | // if (_moving & MF_TURN) { 188 | // new_dir = updateActorDirection(false); 189 | // if (_facing != new_dir) { 190 | // setDirection(new_dir); 191 | // } else { 192 | // _moving = 0; 193 | // } 194 | // return; 195 | // } 196 | if (a->moving & MF_TURN) 197 | { 198 | // TODO: animation? 199 | if (a->facing != a->targetFacing) 200 | { 201 | a->facing = a->targetFacing; 202 | a->moving &= ~MF_TURN; 203 | } 204 | else 205 | { 206 | a->moving = 0; 207 | } 208 | costume_updateActor(a); 209 | return; 210 | } 211 | 212 | if (a->moving & MF_IN_LEG) { 213 | actor_walkStep(a); 214 | } else if (a->moving & MF_LAST_LEG) { 215 | a->moving = 0; 216 | startAnimActor(a, _standFrame); 217 | // if (a->targetFacing != a->destdir && a->destdir != -1) 218 | // { 219 | // a->targetFacing = a->destdir; 220 | // a->moving = MF_TURN; 221 | // } 222 | // if (a->targetFacing != _walkdata.destdir) 223 | // turnToDirection(_walkdata.destdir); 224 | } else { 225 | actor_setBox(a, a->curbox); 226 | if (a->walkbox == a->destbox) { 227 | foundX = a->destX; 228 | foundY = a->destY; 229 | a->moving |= MF_LAST_LEG; 230 | } else { 231 | next_box = boxes_getNext(a->walkbox, a->destbox); 232 | if (next_box < 0) { 233 | a->moving |= MF_LAST_LEG; 234 | costume_updateActor(a); 235 | return; 236 | } 237 | 238 | // Can't walk through locked boxes 239 | // int flags = _vm->getBoxFlags(next_box); 240 | // if ((flags & kBoxLocked) && !((flags & kBoxPlayerOnly) && !isPlayer())) { 241 | // a->moving |= MF_LAST_LEG; 242 | // } 243 | 244 | a->curbox = next_box; 245 | 246 | uint8_t tx, ty; 247 | box_getClosestPtOnBox(a->curbox, a->x, a->y, &tx, &ty); 248 | box_getClosestPtOnBox(a->walkbox, tx, ty, &foundX, &foundY); 249 | } 250 | actor_calcMovementFactor(a, foundX, foundY); 251 | actor_walkStep(a); 252 | } 253 | } 254 | 255 | static void actor_setDirection(uint8_t actor, uint8_t dir) 256 | { 257 | DEBUG_PRINTF("Actor %d Set dir %d\n", actor, dir); 258 | actors[actor].moving &= ~MF_TURN; 259 | actors[actor].facing = dir; 260 | } 261 | 262 | static void actor_turnToDirection(uint8_t actor, uint8_t dir) 263 | { 264 | DEBUG_PRINTF("Actor %d Turn dir %d\n", actor, dir); 265 | actors[actor].moving = MF_TURN; 266 | actors[actor].targetFacing = dir; 267 | } 268 | 269 | /////////////////////////////////////////////////////////////////////////////// 270 | // Actor interface 271 | /////////////////////////////////////////////////////////////////////////////// 272 | 273 | uint8_t actor_getX(uint8_t actor) 274 | { 275 | return actors[actor].x; 276 | } 277 | 278 | uint8_t actor_isMoving(uint8_t actor) 279 | { 280 | return actors[actor].moving; 281 | } 282 | 283 | void actor_setCostume(uint8_t actor, uint8_t costume) 284 | { 285 | //DEBUG_PRINTF("Set actor %u costume %u\n", actor, costume); 286 | actors[actor].costume = costume; 287 | 288 | // doesn't work on startup, fix it later 289 | //costume_updateAll(); 290 | } 291 | 292 | // void actor_setTalkColor(uint8_t actor, uint8_t color) 293 | // { 294 | // actors[actor].talkColor = color; 295 | // } 296 | 297 | void actor_stopTalk(void) 298 | { 299 | scummVars[VAR_HAVE_MSG] = 0; 300 | talkDelay = 0; 301 | 302 | if (talkingActor < ACTOR_COUNT) 303 | { 304 | actor_animate(talkingActor, _talkStopFrame * 4); 305 | talkingActor = 255; 306 | } 307 | } 308 | 309 | void actor_talk(uint8_t actor, const uint8_t *s) 310 | { 311 | // TODO: variables 312 | 313 | scummVars[VAR_HAVE_MSG] = 0xff; 314 | talkingActor = actor; 315 | 316 | // TODO: switch the color when talking actor changes 317 | message_print(s, defaultTalkColor/*actors[actor].talkColor*/); 318 | 319 | if (actor < ACTOR_COUNT) 320 | actor_animate(actor, _talkStartFrame * 4); 321 | } 322 | 323 | void actor_setRoom(uint8_t actor, uint8_t room) 324 | { 325 | // DEBUG_PRINTF("Put actor %u in room %u\n", actor, room); 326 | actors[actor].room = room; 327 | 328 | costume_updateActor(&actors[actor]); 329 | } 330 | 331 | void actor_put(uint8_t actor, uint8_t x, uint8_t y) 332 | { 333 | // DEBUG_PRINTF("Put actor %u to %u, %u\n", actor, x, y); 334 | Actor *a = &actors[actor]; 335 | a->moving = 0; 336 | a->facing = 0; 337 | a->x = x; 338 | a->y = y; 339 | // if (_visible && _vm->_currentRoom != newRoom && _vm->getTalkingActor() == _number) { 340 | actor_stopTalk(); 341 | if (actor_isInCurrentRoom(actor)) 342 | { 343 | uint8_t box = boxes_adjustXY(&x, &y); 344 | a->x = x; 345 | a->y = y; 346 | a->destbox = box; 347 | actor_setBox(a, box); 348 | } 349 | } 350 | 351 | void actor_startWalk(uint8_t actor, uint8_t x, uint8_t y) 352 | { 353 | // DEBUG_PRINTF("Walk actor %u to %u, %u\n", actor, x, y); 354 | 355 | // TODO: jump to this room 356 | 357 | // if (!isInCurrentRoom() && _vm->_game.version <= 6) { 358 | // _pos.x = abr.x; 359 | // _pos.y = abr.y; 360 | // if (!_ignoreTurns && dir != -1) 361 | // _facing = dir; 362 | // return; 363 | // } 364 | 365 | uint8_t xx = x; 366 | uint8_t yy = y; 367 | uint8_t box = boxes_adjustXY(&xx, &yy); 368 | // DEBUG_PRINTF("Dest box %u x=%u y=%u\n", box, xx, yy); 369 | 370 | // if (_pos.x == abr.x && _pos.y == abr.y && (dir == -1 || _facing == dir)) 371 | // return; 372 | if (actors[actor].x == xx && actors[actor].y == yy) { 373 | return; 374 | } 375 | 376 | actors[actor].destX = xx; 377 | actors[actor].destY = yy; 378 | actors[actor].moving = MF_NEW_LEG; 379 | actors[actor].destbox = box; 380 | actors[actor].curbox = actors[actor].walkbox; 381 | //actors[actor].destdir = dir; 382 | // _walkdata.point3.x = 32000; 383 | //_moving = (_moving & ~(MF_LAST_LEG | MF_IN_LEG)) | MF_NEW_LEG; 384 | } 385 | 386 | void actors_walk(void) 387 | { 388 | uint8_t i; 389 | for (i = 0 ; i < ACTOR_COUNT ; ++i) 390 | { 391 | if (actor_isInCurrentRoom(i) && actors[i].moving) 392 | { 393 | actor_walk(actors + i); 394 | } 395 | } 396 | } 397 | 398 | void actor_animate(uint8_t actor, uint8_t anim) 399 | { 400 | // TODO: different commands and directions from void Actor::animateActor(int anim) 401 | Actor *a = &actors[actor]; 402 | uint8_t cmd = anim / 4; 403 | uint8_t dir = anim % 4; 404 | 405 | // Convert into old cmd code 406 | cmd = 0x3F - cmd + 2; 407 | 408 | DEBUG_PRINTF("Animate actor %d cmd=%d dir=%d\n", actor, cmd, dir); 409 | 410 | switch (cmd) { 411 | case 2: // stop walking 412 | //DEBUG_PRINTF("TODO: Actor %u stop walking\n", actor); 413 | a->moving = 0; 414 | startAnimActor(a, _standFrame); 415 | // stopActorMoving(); 416 | break; 417 | case 3: // change direction immediatly 418 | actor_setDirection(actor, dir); 419 | costume_updateActor(a); 420 | break; 421 | case 4: // turn to new direction 422 | actor_turnToDirection(actor, dir); 423 | break; 424 | case 64: 425 | default: 426 | startAnimActor(a, anim / 4); 427 | break; 428 | } 429 | } 430 | 431 | void actors_animate(void) 432 | { 433 | uint8_t i; 434 | for (i = 0 ; i < ACTOR_COUNT ; ++i) 435 | { 436 | // TODO: any other flags? 437 | if (actor_isInCurrentRoom(i)) 438 | { 439 | uint8_t p = actors[i].curpos++; 440 | if (actors[i].curpos >= actors[i].frames) 441 | { 442 | actors[i].curpos = 0; 443 | } 444 | if (p != actors[i].curpos) 445 | { 446 | costume_updateActor(&actors[i]); 447 | } 448 | } 449 | } 450 | } 451 | 452 | void actor_walkToObject(uint8_t actor, uint16_t obj_id) 453 | { 454 | uint8_t x, y; 455 | object_getXY(obj_id, &x, &y); 456 | // getObjectXYPos(obj, x, y, dir); 457 | 458 | // TODO: calculate direction 459 | 460 | boxes_adjustXY(&x, &y); 461 | actor_startWalk(actor, x, y); 462 | // Actor *a = derefActor(actor, "walkActorToObject"); 463 | // AdjustBoxResult r = a->adjustXYToBeInBox(x, y); 464 | // x = r.x; 465 | // y = r.y; 466 | 467 | // a->startWalkActor(x, y, dir); 468 | } 469 | 470 | static void actor_show(Actor *a) 471 | { 472 | // if (_visible) return; 473 | 474 | // adjustActorPos(); 475 | // _cost.reset(); 476 | 477 | //startAnimActor(a, _standFrame); 478 | //startAnimActor(a, _initFrame); 479 | startAnimActor(a, _talkStopFrame); 480 | 481 | // stopActorMoving(); 482 | a->moving = 0; 483 | // _visible = true; 484 | // _needRedraw = true; 485 | } 486 | 487 | void actors_show(void) 488 | { 489 | uint8_t i; 490 | for (i = 0 ; i < ACTOR_COUNT ; ++i) 491 | { 492 | if (actor_isInCurrentRoom(i)) 493 | { 494 | actor_show(&actors[i]); 495 | } 496 | } 497 | costume_updateAll(); 498 | } 499 | 500 | uint8_t actor_isInCurrentRoom(uint8_t actor) 501 | { 502 | return currentRoom && actors[actor].room == currentRoom; 503 | } 504 | 505 | void actor_walkToActor(uint8_t actor, uint8_t toActor, uint8_t dist) 506 | { 507 | uint8_t x = actors[toActor].x; 508 | uint8_t y = actors[toActor].y; 509 | if (x < actors[actor].x) 510 | x += dist; 511 | else 512 | x -= dist; 513 | 514 | boxes_adjustXY(&x, &y); 515 | actor_startWalk(actor, x, y); 516 | } 517 | 518 | uint8_t actor_getFromPos(uint8_t x, uint8_t y) 519 | { 520 | uint8_t i; 521 | for (i = 1 ; i < ACTOR_COUNT ; ++i) 522 | { 523 | Actor *a = &actors[i]; 524 | if (a->room == currentRoom && i != scummVars[VAR_EGO]) 525 | { 526 | // TODO: actor width and height 527 | if (x >= a->x && y >= a->y 528 | && x < a->x + 2 && y < a->y + 4) 529 | { 530 | return i; 531 | } 532 | } 533 | } 534 | return 0; 535 | } 536 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /object.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "object.h" 6 | #include "debug.h" 7 | #include "room.h" 8 | #include "graphics.h" 9 | #include "script.h" 10 | #include "helper.h" 11 | 12 | typedef struct Object 13 | { 14 | uint16_t obj_nr; 15 | uint8_t x, y; 16 | uint8_t walk_x, walk_y; 17 | uint8_t width, height; 18 | uint8_t actordir; 19 | //uint8_t state; 20 | uint8_t parent; 21 | uint8_t parentstate; 22 | uint8_t preposition; 23 | //uint8_t room; 24 | //uint8_t whereis; 25 | uint8_t nameOffs; 26 | uint16_t OBIMoffset; 27 | uint16_t OBCDoffset; 28 | } Object; 29 | 30 | 31 | #define MAX_INV (INV_COLS * INV_ROWS) 32 | 33 | extern Object objects[_numLocalObjects]; 34 | extern uint8_t objectOwnerTable[_numGlobalObjects]; 35 | extern uint8_t objectStateTable[_numGlobalObjects]; 36 | extern uint8_t inventoryOffset; 37 | extern Inventory invObjects[_numInventory]; 38 | 39 | #define OBJECTS_PAGE 3 40 | #define ENTER PUSH_PAGE(2, OBJECTS_PAGE) 41 | #define EXIT POP_PAGE(2) 42 | 43 | // static Object *findLocalObjectSlot(void) 44 | // { 45 | // Object *obj = objects; 46 | // Object *end = objects + _numLocalObjects; 47 | // for ( ; obj != end ; ++obj) 48 | // if (!obj->obj_nr) 49 | // { 50 | // memset(obj, 0, sizeof(Object)); 51 | // return obj; 52 | // } 53 | // DEBUG_PUTS("No more object slots available\n"); 54 | // DEBUG_HALT; 55 | // } 56 | 57 | static Object *object_get(uint16_t id) 58 | { 59 | if (!id) 60 | return NULL; 61 | 62 | //ENTER; 63 | Object *obj = objects; 64 | Object *end = objects + _numLocalObjects; 65 | for ( ; obj != end ; ++obj) 66 | { 67 | if (obj->obj_nr == id) 68 | { 69 | //EXIT; 70 | return obj; 71 | } 72 | } 73 | 74 | //EXIT; 75 | return NULL; 76 | } 77 | 78 | void objects_clear(void) 79 | { 80 | ENTER; 81 | // clean the objects 82 | Object *obj = objects; 83 | Object *end = objects + _numLocalObjects; 84 | for ( ; obj != end ; ++obj) 85 | //if (obj->room == currentRoom) 86 | obj->obj_nr = 0; 87 | EXIT; 88 | } 89 | 90 | void setupRoomObjects(HROOM r) 91 | { 92 | ENTER; 93 | esx_f_seek(r, 20, ESX_SEEK_SET); 94 | uint8_t numObj = readByte(r); 95 | 96 | uint16_t delta = numObj * 2; 97 | //#define MAX_ROOM_OBJ 32 98 | //uint8_t offs[MAX_ROOM_OBJ]; 99 | 100 | uint8_t i; 101 | for (i = 1 ; i <= numObj ; i++) 102 | { 103 | Object *od = &objects[i];//findLocalObjectSlot(); 104 | //offs[i] = od - objects; 105 | seekToOffset(r, 28 + (i - 1) * 2); 106 | od->OBIMoffset = readWord(r); 107 | seekFwd(r, delta - 2); 108 | od->OBCDoffset = readWord(r); 109 | seekToOffset(r, od->OBCDoffset); 110 | 111 | //resetRoomObject(od, room); 112 | uint16_t size = readWord(r); // 2 - object info size 113 | readWord(r); // 4 - nothing (0) 114 | od->obj_nr = readWord(r); // 6 115 | readByte(r); // 8 - nothing (0) 116 | od->x = readByte(r); // 9 117 | uint8_t y = readByte(r); // 10 118 | od->y = y & 0x7F; 119 | 120 | od->parentstate = (y & 0x80) ? 8 : 0; 121 | 122 | od->width = readByte(r); // 11 123 | 124 | od->parent = readByte(r); // 12 125 | 126 | od->walk_x = readByte(r); // 13 127 | uint8_t b14 = readByte(r); // 14 128 | // walk_y resolution is 2 pixels 129 | od->walk_y = (b14 & 0x1f) * 4; 130 | od->preposition = b14 >> 5; 131 | uint8_t b15 = readByte(r); // 15 132 | od->actordir = b15 & 7; 133 | od->height = (b15 & 0xf8) / 8; 134 | // TODO: fix name offset 135 | od->nameOffs = readByte(r); // 16 - name offset 136 | 137 | // od->whereis = WIO_ROOM; 138 | // od->room = currentRoom; 139 | 140 | DEBUG_PRINTF("Load object %u x=%u y=%u walkx=%u walky=%u w=%u h=%u p=%u ps=%u actdir=%u prep=%u nameOffs=%u\n", 141 | od->obj_nr, od->x, od->y, od->walk_x, od->walk_y, od->width, od->height, od->parent, 142 | od->parentstate, od->actordir, od->preposition, od->nameOffs); 143 | DEBUG_PRINTF("-- OBIMOffset 0x%x OBCDOffset 0x%x\n", od->OBIMoffset, od->OBCDoffset); 144 | // list of events follows 145 | // uint8_t ev = readByte(r); 146 | // while (ev) 147 | // { 148 | // uint8_t offs = readByte(r); 149 | // // DEBUG_PRINTF("-- script for event 0x%x offs 0x%x\n", ev, offs); 150 | // ev = readByte(r); 151 | // } 152 | 153 | seekToOffset(r, od->OBCDoffset + od->nameOffs); 154 | DEBUG_PUTS("-- name: "); 155 | uint8_t ev = readByte(r); 156 | while (ev) 157 | { 158 | DEBUG_PUTC(ev); 159 | ev = readByte(r); 160 | } 161 | DEBUG_PUTC('\n'); 162 | } 163 | 164 | // for (i = 0 ; i < numObj ; i++) 165 | // { 166 | // uint8_t k = offs[i]; 167 | // if (objects[k].parent) 168 | // { 169 | // objects[k].parent = offs[objects[k].parent - 1]; 170 | // } 171 | // } 172 | 173 | EXIT; 174 | } 175 | 176 | uint16_t object_find(uint16_t x, uint16_t y) 177 | { 178 | ENTER; 179 | // convert y to tiles, because objects have tile coordinates 180 | y = y / (8 / V12_Y_MULTIPLIER); 181 | Object *obj = objects; 182 | Object *end = objects + _numLocalObjects; 183 | for ( ; obj != end ; ++obj) { 184 | if (obj->obj_nr == 0/* || obj->whereis != WIO_ROOM*/) 185 | continue; 186 | 187 | if (objectStateTable[obj->obj_nr] & kObjectStateUntouchable) 188 | continue; 189 | 190 | // if (obj->x <= x && obj->x + obj->width > x 191 | // && obj->y <= y && obj->y + obj->height > y) 192 | // { 193 | // DEBUG_PRINTF("Can find object %d parent %d\n", obj->obj_nr, obj->parent); 194 | // } 195 | // return obj; 196 | 197 | Object *b = obj; 198 | uint8_t a; 199 | do 200 | { 201 | a = b->parentstate; 202 | // if (obj->x <= x && obj->x + obj->width > x 203 | // && obj->y <= y && obj->y + obj->height > y) 204 | // DEBUG_PRINTF("Testing obj %d parent %d\n", b->obj_nr, b->parent); 205 | if (b->parent == 0) 206 | { 207 | if (obj->x <= x && obj->x + obj->width > x 208 | && obj->y <= y && obj->y + obj->height > y) 209 | { 210 | uint16_t res = obj->obj_nr; 211 | EXIT; 212 | return res; 213 | } 214 | break; 215 | } 216 | b = &objects[b->parent]; 217 | } 218 | while ((objectStateTable[b->obj_nr] & kObjectState_08) == a); 219 | } 220 | 221 | EXIT; 222 | return 0; 223 | } 224 | 225 | void readGlobalObjects(HROOM r) 226 | { 227 | ENTER; 228 | int i; 229 | for (i = 0 ; i != _numGlobalObjects ; i++) 230 | { 231 | uint8_t tmp = readByte(r); 232 | //DEBUG_PRINTF("global %u owner %u state %u\n", i, tmp & 15, tmp >> 4); 233 | objectOwnerTable[i] = tmp & 15;//OF_OWNER_MASK; 234 | objectStateTable[i] = tmp >> 4;//OF_STATE_SHL; 235 | } 236 | EXIT; 237 | } 238 | 239 | uint8_t object_getOwner(uint16_t id) 240 | { 241 | ENTER; 242 | uint8_t res = objectOwnerTable[id]; 243 | EXIT; 244 | return res; 245 | } 246 | 247 | void object_setOwner(uint16_t id, uint8_t owner) 248 | { 249 | ENTER; 250 | // DEBUG_PRINTF("Set owner %u for %u\n", owner, id); 251 | objectOwnerTable[id] = owner; 252 | EXIT; 253 | } 254 | 255 | int8_t object_whereIs(uint16_t id) 256 | { 257 | DEBUG_PRINTF("Where is %d\n", id); 258 | // Note: in MM v0 bg objects are greater _numGlobalObjects 259 | if (!id || id >= _numGlobalObjects) 260 | return WIO_NOT_FOUND; 261 | 262 | // check inventory 263 | ENTER; 264 | uint8_t i; 265 | for (i = 0 ; i < _numInventory ; i++) 266 | { 267 | if (invObjects[i].obj_nr == id) 268 | { 269 | EXIT; 270 | return WIO_INVENTORY; 271 | } 272 | } 273 | 274 | Object *obj = object_get(id); 275 | EXIT; 276 | if (obj) 277 | { 278 | // if (objectOwnerTable[id] != OF_OWNER_ROOM) 279 | // { 280 | // // for (i = 0; i < _numInventory; i++) 281 | // // if (inventory[i] == id) 282 | // // return WIO_INVENTORY; 283 | // return WIO_NOT_FOUND; 284 | // } 285 | 286 | // if (_objs[i].fl_object_index) 287 | // return WIO_FLOBJECT; 288 | return WIO_ROOM; 289 | } 290 | 291 | return WIO_NOT_FOUND; 292 | } 293 | 294 | uint16_t object_getVerbEntrypoint(uint16_t obj, uint16_t entry) 295 | { 296 | if (object_whereIs(obj) == WIO_NOT_FOUND) 297 | return 0; 298 | 299 | ENTER; 300 | uint16_t ptr = object_get(obj)->OBCDoffset; 301 | EXIT; 302 | ptr += 15; 303 | 304 | uint8_t res = 0; 305 | HROOM r = openRoom(currentRoom); 306 | seekToOffset(r, ptr); 307 | do 308 | { 309 | uint8_t e = readByte(r); 310 | if (e == 0) 311 | { 312 | res = 0; 313 | break; 314 | } 315 | res = readByte(r); 316 | if (e == entry || e == 0xff) 317 | break; 318 | } 319 | while (1); 320 | closeRoom(r); 321 | 322 | //DEBUG_PRINTF("Verb entry for %d is %d\n", entry, res); 323 | return res; 324 | } 325 | 326 | uint8_t object_getState(uint16_t id) 327 | { 328 | // I knew LucasArts sold cracked copies of the original Maniac Mansion, 329 | // at least as part of Day of the Tentacle. Apparently they also sold 330 | // cracked versions of the enhanced version. At least in Germany. 331 | // 332 | // This will keep the security door open at all times. I can only 333 | // assume that 182 and 193 each correspond to one particular side of 334 | // it. Fortunately this does not prevent frustrated players from 335 | // blowing up the mansion, should they feel the urge to. 336 | 337 | // if (_game.id == GID_MANIAC && _game.version != 0 && (obj == 182 || obj == 193)) 338 | // _objectStateTable[obj] |= kObjectState_08; 339 | 340 | ENTER; 341 | uint8_t res = objectStateTable[id]; 342 | EXIT; 343 | return res; 344 | } 345 | 346 | void object_setState(uint16_t id, uint8_t s) 347 | { 348 | ENTER; 349 | //DEBUG_PRINTF("Set state %d %x\n", id, s); 350 | objectStateTable[id] = s; 351 | EXIT; 352 | } 353 | 354 | void object_getXY(uint16_t id, uint8_t *x, uint8_t *y) 355 | { 356 | ENTER; 357 | Object *obj = object_get(id); 358 | *x = obj->walk_x; 359 | *y = obj->walk_y; 360 | 361 | // x = x >> V12_X_SHIFT; 362 | // y = y >> V12_Y_SHIFT; 363 | EXIT; 364 | } 365 | 366 | uint16_t object_getOBCDoffset(uint16_t id) 367 | { 368 | ENTER; 369 | Object *obj = object_get(id); 370 | uint16_t res = obj->OBCDoffset; 371 | EXIT; 372 | return res; 373 | } 374 | 375 | uint16_t object_getId(Object *obj) 376 | { 377 | ENTER; 378 | uint16_t res = obj->obj_nr; 379 | EXIT; 380 | return res; 381 | } 382 | 383 | void object_draw(uint16_t id) 384 | { 385 | ENTER; 386 | Object *obj = object_get(id); 387 | //DEBUG_PRINTF("Draw object %d %x\n", id, obj); 388 | graphics_drawObject(obj->OBIMoffset, obj->x, obj->y, obj->width, obj->height); 389 | EXIT; 390 | } 391 | 392 | void objects_redraw(void) 393 | { 394 | //DEBUG_PUTS("Redraw all\n"); 395 | // clear screen 396 | HROOM r = openRoom(currentRoom); 397 | decodeRoomBackground(r); 398 | closeRoom(r); 399 | 400 | // Object *obj = objects; 401 | // Object *end = objects + _numObjects; 402 | // for ( ; obj != end ; ++obj) 403 | // { 404 | ENTER; 405 | int i; 406 | for (i = _numLocalObjects - 1 ; i >= 0 ; --i) 407 | { 408 | Object *obj = &objects[i]; 409 | if (obj->obj_nr 410 | && (objectStateTable[obj->obj_nr] & kObjectState_08) 411 | /*&& obj->whereis == WIO_ROOM*/ 412 | //&& obj->room == currentRoom 413 | // TODO: check room 414 | ) 415 | { 416 | Object *od = obj; 417 | uint8_t a; 418 | do { 419 | a = od->parentstate; 420 | if (!od->parent) 421 | { 422 | object_draw(obj->obj_nr); 423 | //DEBUG_PRINTF("--- obj %d\n", obj->obj_nr); 424 | break; 425 | } 426 | od = &objects[od->parent]; 427 | } while ((objectStateTable[od->obj_nr] & kObjectState_08) == a); 428 | } 429 | } 430 | EXIT; 431 | } 432 | 433 | static uint8_t readName(char *s, uint8_t room, uint16_t obcd, uint8_t offs) 434 | { 435 | if (!obcd) 436 | return 0; 437 | //DEBUG_PRINTF("Name for object %d\n", id); 438 | HROOM r = openRoom(room); 439 | seekToOffset(r, obcd + offs); 440 | uint8_t len = readString(r, s); 441 | closeRoom(r); 442 | return len; 443 | } 444 | 445 | uint8_t object_getName(char *s, uint16_t id) 446 | { 447 | ENTER; 448 | Object *obj = object_get(id); 449 | uint8_t res = readName(s, currentRoom, obj->OBCDoffset, obj->nameOffs); 450 | EXIT; 451 | return res; 452 | } 453 | 454 | uint8_t object_getPreposition(uint16_t id) 455 | { 456 | ENTER; 457 | Object *obj = object_get(id); 458 | uint8_t res = obj->preposition; 459 | EXIT; 460 | return res; 461 | } 462 | 463 | static uint8_t inventory_getCount(uint8_t owner) 464 | { 465 | ENTER; 466 | uint8_t i; 467 | uint16_t obj; 468 | uint8_t count = 0; 469 | for (i = 0; i < _numInventory; i++) { 470 | obj = invObjects[i].obj_nr; 471 | if (obj && objectOwnerTable[obj] == owner) 472 | count++; 473 | } 474 | EXIT; 475 | return count; 476 | } 477 | 478 | static uint8_t inventory_getSlot(void) 479 | { 480 | uint8_t i; 481 | uint8_t count = 0; 482 | for (i = 0; i < _numInventory; i++) 483 | if (!invObjects[i].obj_nr) 484 | return i; 485 | /* Should never happen */ 486 | return 0; 487 | } 488 | 489 | // uint16_t inventory_find(uint8_t owner, uint8_t idx) 490 | // { 491 | // // uint8_t count = 1, i; 492 | // // uint16_t obj; 493 | // // for (i = 0; i < _numInventory; i++) { 494 | // // obj = inventory[i]; 495 | // // if (obj && object_getOwner(obj) == owner && count++ == idx) 496 | // // return obj; 497 | // // } 498 | // return 0; 499 | // } 500 | 501 | void inventory_redraw(void) 502 | { 503 | ENTER; 504 | // int inventoryArea = 48; 505 | // int maxChars = 13; 506 | 507 | //_mouseOverBoxV2 = -1; 508 | 509 | // if (!(_userState & USERSTATE_IFACE_INVENTORY)) // Don't draw inventory unless active 510 | // return; 511 | graphics_clearInventory(); 512 | 513 | uint8_t all = inventory_getCount(scummVars[VAR_EGO]); 514 | uint8_t max_inv = all - inventoryOffset; 515 | if (max_inv > 4) 516 | max_inv = 4; 517 | uint8_t count = 0; 518 | uint8_t slot = 0; 519 | // Object *obj = objects; 520 | // Object *end = objects + _numObjects; 521 | Inventory *obj = invObjects; 522 | Inventory *end = invObjects + _numInventory; 523 | for ( ; obj != end ; ++obj) 524 | { 525 | if (obj->obj_nr == 0// || obj->whereis != WIO_INVENTORY 526 | || objectOwnerTable[obj->obj_nr] != scummVars[VAR_EGO]) 527 | continue; 528 | 529 | ++count; 530 | if (count <= inventoryOffset) 531 | continue; 532 | 533 | char name[32]; 534 | readName(name, obj->room, obj->OBCDoffset, obj->nameOffs); 535 | graphics_drawInventory(slot, name); 536 | ++slot; 537 | 538 | if (slot >= max_inv) 539 | break; 540 | 541 | //uint16_t obj = inventory_find(scummVars[VAR_EGO], i + 1 + inventoryOffset); 542 | 543 | // _string[1].ypos = _mouseOverBoxesV2[i].rect.top + vs->topline; 544 | // _string[1].xpos = _mouseOverBoxesV2[i].rect.left; 545 | // _string[1].right = _mouseOverBoxesV2[i].rect.right - 1; 546 | // _string[1].color = _mouseOverBoxesV2[i].color; 547 | 548 | // const byte *tmp = getObjOrActorName(obj); 549 | // assert(tmp); 550 | 551 | // Prevent inventory entries from overflowing by truncating the text 552 | // byte msg[20]; 553 | // msg[maxChars] = 0; 554 | // strncpy((char *)msg, (const char *)tmp, maxChars); 555 | 556 | // Draw it 557 | // drawString(1, msg); 558 | } 559 | 560 | 561 | // If necessary, draw "up" arrow 562 | if (inventoryOffset > 0) 563 | { 564 | graphics_printAtXY("\x7e", INV_UP_X, INV_ARR_Y, 0, 3, 1); 565 | } 566 | 567 | // If necessary, draw "down" arrow 568 | if (inventoryOffset + 4 < all) 569 | { 570 | graphics_printAtXY("\x7f", INV_DOWN_X, INV_ARR_Y, 0, 3, 1); 571 | } 572 | EXIT; 573 | } 574 | 575 | void inventory_addObject(uint16_t id) 576 | { 577 | ENTER; 578 | // int idx, slot; 579 | // uint32 size; 580 | // const byte *ptr; 581 | // byte *dst; 582 | // FindObjectInRoom foir; 583 | 584 | // debug(1, "Adding object %d from room %d into inventory", obj, room); 585 | 586 | // if (whereIsObject(obj) == WIO_FLOBJECT) { 587 | // idx = getObjectIndex(obj); 588 | // assert(idx >= 0); 589 | // ptr = getResourceAddress(rtFlObject, _objs[idx].fl_object_index) + 8; 590 | // assert(ptr); 591 | // size = READ_BE_UINT32(ptr + 4); 592 | // } else { 593 | // findObjectInRoom(&foir, foCodeHeader, obj, room); 594 | // if (_game.features & GF_OLD_BUNDLE) 595 | // size = READ_LE_UINT16(foir.obcd); 596 | // else if (_game.features & GF_SMALL_HEADER) 597 | // size = READ_LE_UINT32(foir.obcd); 598 | // else 599 | // size = READ_BE_UINT32(foir.obcd + 4); 600 | // ptr = foir.obcd; 601 | // } 602 | Object *obj = object_get(id); 603 | 604 | uint8_t slot = inventory_getSlot(); 605 | invObjects[slot].obj_nr = obj->obj_nr; 606 | invObjects[slot].room = currentRoom; 607 | invObjects[slot].OBCDoffset = obj->OBCDoffset; 608 | invObjects[slot].nameOffs = obj->nameOffs; 609 | //inventory[slot] = obj->obj_nr; 610 | // dst = _res->createResource(rtInventory, slot, size); 611 | // assert(dst); 612 | // memcpy(dst, ptr, size); 613 | 614 | //obj->whereis = WIO_INVENTORY; 615 | EXIT; 616 | } 617 | 618 | uint16_t inventory_checkXY(int8_t x, int8_t y) 619 | { 620 | //DEBUG_PRINTF("check %d %d\n", x, y); 621 | y -= INV_TOP; 622 | x -= INV_GAP; 623 | if (x < 0 || y < 0 || y >= INV_ROWS) 624 | { 625 | return 0; 626 | } 627 | 628 | if (x < INV_SLOT_WIDTH) 629 | { 630 | x = 0; 631 | } 632 | else if (x >= INV_SLOT_WIDTH + INV_ARR_W && x < INV_WIDTH) 633 | { 634 | x = 1; 635 | } 636 | else 637 | { 638 | return 0; 639 | } 640 | ENTER; 641 | //DEBUG_PRINTF(" --- %d %d\n", x, y); 642 | uint8_t slot = x + y * INV_COLS + inventoryOffset; 643 | uint8_t count = 0; 644 | Inventory *obj = invObjects; 645 | Inventory *end = invObjects + _numInventory; 646 | for ( ; obj != end ; ++obj) 647 | { 648 | if (obj->obj_nr == 0 649 | || objectOwnerTable[obj->obj_nr] != scummVars[VAR_EGO]) 650 | continue; 651 | 652 | if (slot == count) 653 | { 654 | uint8_t res = obj->obj_nr; 655 | EXIT; 656 | return res; 657 | } 658 | 659 | ++count; 660 | } 661 | EXIT; 662 | 663 | return 0; 664 | } 665 | 666 | void inventory_checkButtons(int8_t x, int8_t y) 667 | { 668 | ENTER; 669 | // check up and down arrows 670 | if (y == INV_ARR_Y) 671 | { 672 | DEBUG_PRINTF("Check buttons %d %d\n", x, inventoryOffset); 673 | if (x == INV_UP_X && inventoryOffset > 0) 674 | { 675 | inventoryOffset -= 2; 676 | inventory_redraw(); 677 | } 678 | else if (x == INV_DOWN_X) 679 | { 680 | uint8_t all = inventory_getCount(scummVars[VAR_EGO]); 681 | if (all - inventoryOffset > INV_COLS * INV_ROWS) 682 | { 683 | inventoryOffset += 2; 684 | inventory_redraw(); 685 | } 686 | } 687 | } 688 | EXIT; 689 | } 690 | -------------------------------------------------------------------------------- /graphics.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "graphics.h" 4 | #include "resource.h" 5 | #include "camera.h" 6 | #include "room.h" 7 | #include "debug.h" 8 | #include "verbs.h" 9 | #include "sprites.h" 10 | #include "string.h" 11 | #include "script.h" 12 | #include "helper.h" 13 | 14 | #define PAL_SPRITES 0x20 15 | #define PAL_TILES 0x30 16 | 17 | #define GRAPH_PAGE 3 18 | //#define SCREEN_PAGE 11 19 | extern uint8_t nametable[16][64]; 20 | extern uint8_t attributes[64]; 21 | extern uint8_t translationTable[]; 22 | 23 | // costume set 24 | uint8_t costdesc[51]; 25 | uint8_t costlens[279]; 26 | uint8_t costoffs[556]; 27 | // static uint8_t costdata[11234]; 28 | uint8_t costdata_id; 29 | 30 | #define RGB2NEXT(r, g, b) (uint8_t)(((r) & 0xe0) | (((g) >> 3) & 0x1c) | (((b) >> 6) & 0x3)), \ 31 | (((b) >> 5) & 1) 32 | // #define RGB2NEXT(r, g, b) r, g, b 33 | 34 | 35 | static const uint8_t tableNESPalette[] = { 36 | /* 0x1D */ 37 | RGB2NEXT(0x24, 0x24, 0x24), RGB2NEXT(0x00, 0x24, 0x92), 38 | RGB2NEXT(0x00, 0x00, 0xDB), RGB2NEXT(0x6D, 0x49, 0xDB), 39 | RGB2NEXT(0x92, 0x00, 0x6D), RGB2NEXT(0xB6, 0x00, 0x6D), 40 | RGB2NEXT(0xB6, 0x24, 0x00), RGB2NEXT(0x92, 0x49, 0x00), 41 | RGB2NEXT(0x6D, 0x49, 0x00), RGB2NEXT(0x24, 0x49, 0x00), 42 | RGB2NEXT(0x00, 0x6D, 0x24), RGB2NEXT(0x00, 0x92, 0x00), 43 | RGB2NEXT(0x00, 0x49, 0x49), RGB2NEXT(0x00, 0x00, 0x00), 44 | RGB2NEXT(0x00, 0x00, 0x00), RGB2NEXT(0x00, 0x00, 0x00), 45 | 46 | RGB2NEXT(0xB6, 0xB6, 0xB6), RGB2NEXT(0x00, 0x6D, 0xDB), 47 | RGB2NEXT(0x00, 0x49, 0xFF), RGB2NEXT(0x92, 0x00, 0xFF), 48 | RGB2NEXT(0xB6, 0x00, 0xFF), RGB2NEXT(0xFF, 0x00, 0x92), 49 | RGB2NEXT(0xFF, 0x00, 0x00), RGB2NEXT(0xDB, 0x6D, 0x00), 50 | RGB2NEXT(0x92, 0x6D, 0x00), RGB2NEXT(0x24, 0x92, 0x00), 51 | RGB2NEXT(0x00, 0x92, 0x00), RGB2NEXT(0x00, 0xB6, 0x6D), 52 | /* 0x00 */ 53 | RGB2NEXT(0x00, 0x92, 0x92), RGB2NEXT(0x6D, 0x6D, 0x6D), 54 | RGB2NEXT(0x00, 0x00, 0x00), RGB2NEXT(0x00, 0x00, 0x00), 55 | 56 | RGB2NEXT(0xFF, 0xFF, 0xFF), RGB2NEXT(0x6D, 0xB6, 0xFF), 57 | RGB2NEXT(0x92, 0x92, 0xFF), RGB2NEXT(0xDB, 0x6D, 0xFF), 58 | RGB2NEXT(0xFF, 0x00, 0xFF), RGB2NEXT(0xFF, 0x6D, 0xFF), 59 | RGB2NEXT(0xFF, 0x92, 0x00), RGB2NEXT(0xFF, 0xB6, 0x00), 60 | RGB2NEXT(0xDB, 0xDB, 0x00), RGB2NEXT(0x6D, 0xDB, 0x00), 61 | RGB2NEXT(0x00, 0xFF, 0x00), RGB2NEXT(0x49, 0xFF, 0xDB), 62 | RGB2NEXT(0x00, 0xFF, 0xFF), RGB2NEXT(0x49, 0x49, 0x49), 63 | RGB2NEXT(0x00, 0x00, 0x00), RGB2NEXT(0x00, 0x00, 0x00), 64 | 65 | RGB2NEXT(0xFF, 0xFF, 0xFF), RGB2NEXT(0xB6, 0xDB, 0xFF), 66 | RGB2NEXT(0xDB, 0xB6, 0xFF), RGB2NEXT(0xFF, 0xB6, 0xFF), 67 | RGB2NEXT(0xFF, 0x92, 0xFF), RGB2NEXT(0xFF, 0xB6, 0xB6), 68 | RGB2NEXT(0xFF, 0xDB, 0x92), RGB2NEXT(0xFF, 0xFF, 0x49), 69 | RGB2NEXT(0xFF, 0xFF, 0x6D), RGB2NEXT(0xB6, 0xFF, 0x49), 70 | RGB2NEXT(0x92, 0xFF, 0x6D), RGB2NEXT(0x49, 0xFF, 0xDB), 71 | RGB2NEXT(0x92, 0xDB, 0xFF), RGB2NEXT(0x92, 0x92, 0x92), 72 | RGB2NEXT(0x00, 0x00, 0x00), RGB2NEXT(0x00, 0x00, 0x00) 73 | }; 74 | 75 | __sfr __at 0xfe IO_FE; 76 | 77 | //#define TESTING 1 78 | 79 | void initGraphics(void) 80 | { 81 | int i; 82 | 83 | // black border 84 | IO_FE = 0; 85 | // disable layer2 86 | //ZXN_WRITE_REG(0x69, 0x00); 87 | // enable tilemap with attributes 88 | ZXN_WRITE_REG(0x6B, 0x81); 89 | ZXN_WRITE_REG(0x6C, 0); 90 | // tilemap transparency index 91 | //ZXN_WRITE_REG(0x4C, 0); 92 | // sprite transparency index 93 | ZXN_WRITE_REG(0x4B, 0); 94 | // disable ULA 95 | ZXN_WRITE_REG(0x68, 0x80); 96 | 97 | 98 | // tilemap base address is 0x6000 99 | ZXN_WRITE_REG(0x6E, TILEMAP_BASE >> 8); 100 | // tile base address is 0x4000 101 | ZXN_WRITE_REG(0x6F, TILE_BASE >> 8); 102 | 103 | // // switch on ULANext 104 | // ZXN_WRITE_REG(0x43 = 1; 105 | 106 | // enable sprites and sprites over border 107 | ZXN_WRITE_REG(0x15, 0x23); 108 | // set sprites clipping window 320x256 109 | ZXN_WRITE_REG(0x19, 0); 110 | ZXN_WRITE_REG(0x19, 159); 111 | ZXN_WRITE_REG(0x19, 0); 112 | ZXN_WRITE_REG(0x19, 255); 113 | 114 | #ifdef TESTING 115 | // palette 116 | ZXN_WRITE_REG(0x43, 0x30); 117 | for (i = 0 ; i < 16 ; ++i) 118 | { 119 | uint8_t c = i & 7; 120 | ZXN_WRITE_REG(0x40, i); 121 | ZXN_WRITE_REG(0x44, (c << 5) | (c << 2) | (c >> 1)); 122 | ZXN_WRITE_REG(0x44, 0); 123 | } 124 | 125 | uint8_t *p; 126 | 127 | // enable layer2 128 | // ZXN_WRITE_REG(0xLAYER_2_CONFIG = 2 | 1; 129 | 130 | // uint8_t *p = 0; 131 | // for (i = 0 ; i < 0x4000 ; ++i) 132 | // *p++ = i; 133 | 134 | // // DEBUG_PUTS("Setting tiles\n"); 135 | 136 | char j; 137 | PUSH_PAGE(2, 10); 138 | p = (uint8_t*)TILE_BASE; 139 | for (i = 0 ; i < 256 * 32 ; ++i) 140 | //for (j = 0 ; j < 32 ; ++j) 141 | *p++ = i; 142 | POP_PAGE(2); 143 | 144 | // // DEBUG_PUTS("Setting tilemap\n"); 145 | 146 | PUSH_PAGE(3, 11); 147 | p = (uint8_t*)TILEMAP_BASE + LINE_BYTES * 26; 148 | for (i = 0 ; i < 256 ; ++i) 149 | { 150 | *p++ = i; 151 | *p++ = 0;//i << 4; 152 | } 153 | POP_PAGE(3); 154 | #endif 155 | 156 | // reset all sprite attributes 157 | for (i = 0 ; i < 128 ; ++i) 158 | { 159 | IO_SPRITE_SLOT = i; 160 | IO_SPRITE_ATTRIBUTE = 0; 161 | IO_SPRITE_ATTRIBUTE = 0; 162 | IO_SPRITE_ATTRIBUTE = 0; 163 | IO_SPRITE_ATTRIBUTE = 0; 164 | } 165 | 166 | // // DEBUG_PUTS("End setup\n"); 167 | } 168 | 169 | 170 | static void updatePalette(HROOM r, uint8_t id) 171 | { 172 | uint8_t i, c; 173 | ZXN_WRITE_REG(0x43, id); 174 | for (i = 0 ; i < 16 ; ++i) 175 | { 176 | c = readByte(r); 177 | // TODO: this is for background tiles only 178 | // if (c == 0x0D) 179 | // c = 0x1D; 180 | if (c == 0x1D) // HACK - switch around colors 0x00 and 0x1D 181 | c = 0; // so we don't need a zillion extra checks 182 | else if (c == 0)// for determining the proper background color 183 | c = 0x1D; 184 | c = c * 2; 185 | // color(0-3) + palette offset(0-3) 186 | if (id == PAL_SPRITES) 187 | { 188 | ZXN_WRITE_REG(0x40, i); 189 | } 190 | else 191 | { 192 | ZXN_WRITE_REG(0x40, (i & 3) | ((i & 0xc) << 2)); 193 | } 194 | ZXN_WRITE_REG(0x44, tableNESPalette[c]); 195 | ZXN_WRITE_REG(0x44, tableNESPalette[c + 1]); 196 | } 197 | 198 | // dark palette for the sprites 199 | if (id == PAL_SPRITES) 200 | { 201 | static const uint8_t darkpalette[16] = { 202 | 0x00,0x00,0x2D,0x3D,0x00,0x00,0x2D,0x3D, 203 | 0x00,0x00,0x2D,0x3D,0x00,0x00,0x2D,0x3D 204 | }; 205 | const uint8_t *d = darkpalette; 206 | for (i = 16 ; i < 32 ; ++i) 207 | { 208 | c = *d; 209 | ++d; 210 | if (c == 0x1D) // HACK - switch around colors 0x00 and 0x1D 211 | c = 0; // so we don't need a zillion extra checks 212 | else if (c == 0)// for determining the proper background color 213 | c = 0x1D; 214 | c = c * 2; 215 | ZXN_WRITE_REG(0x40, i); 216 | ZXN_WRITE_REG(0x44, tableNESPalette[c]); 217 | ZXN_WRITE_REG(0x44, tableNESPalette[c + 1]); 218 | } 219 | } 220 | } 221 | 222 | void decodeRoomBackground(HROOM r) 223 | { 224 | PUSH_PAGE(2, GRAPH_PAGE); 225 | 226 | esx_f_seek(r, 0xa, ESX_SEEK_SET); 227 | uint16_t gdata = readWord(r); 228 | // 17 = tileset_id + palette 229 | esx_f_seek(r, gdata + 17, ESX_SEEK_SET); 230 | 231 | uint8_t i, j; 232 | uint16_t n; 233 | uint8_t data = readByte(r); 234 | //DEBUG_PUTS("nametable:\n"); 235 | for (i = 0 ; i < 16 ; ++i) 236 | { 237 | nametable[i][0] = nametable[i][1] = 0; 238 | n = 0; 239 | while (n < roomWidth) 240 | { 241 | uint8_t next = readByte(r); 242 | for (j = 0 ; j < (data & 0x7F) ; j++) 243 | { 244 | //if (i == 0) DEBUG_PRINTF("%x ", next); 245 | nametable[i][2 + n++] = next; 246 | if (data & 0x80) 247 | next = readByte(r); 248 | } 249 | if (!(data & 0x80)) 250 | next = readByte(r); 251 | data = next; 252 | } 253 | nametable[i][roomWidth + 2] = nametable[i][roomWidth + 3] = 0; 254 | } 255 | //DEBUG_PUTS("\n"); 256 | // decode attributes 257 | esx_f_seek(r, 0xc, ESX_SEEK_SET); 258 | uint16_t adata = readWord(r); 259 | esx_f_seek(r, adata, ESX_SEEK_SET); 260 | data = readByte(r); 261 | for (n = 0 ; n < 64 ; ) { 262 | uint8_t next = readByte(r); 263 | for (j = 0 ; j < (data & 0x7F) ; j++) 264 | { 265 | attributes[n++] = next; 266 | if (data & 0x80) 267 | next = readByte(r); 268 | } 269 | if (!(n & 7) && (roomWidth == 0x1C)) 270 | n += 8; 271 | if (!(data & 0x80)) 272 | next = readByte(r); 273 | data = next; 274 | } 275 | // decode masktable 276 | 277 | POP_PAGE(2); 278 | } 279 | 280 | void decodeNESGfx(HROOM r) 281 | { 282 | // uint16_t width; 283 | //esx_f_seek(r, 0x4, ESX_SEEK_SET); 284 | // width = readWord(r); 285 | esx_f_seek(r, 0xa, ESX_SEEK_SET); 286 | uint16_t gdata = readWord(r); 287 | esx_f_seek(r, gdata, ESX_SEEK_SET); 288 | uint8_t tileset = readByte(r); 289 | //DEBUG_PRINTF("decoding gdata %u tileset %u\n", gdata, tileset); 290 | decodeTiles(tileset); 291 | // decode palette 292 | updatePalette(r, PAL_TILES); 293 | // decode name table 294 | decodeRoomBackground(r); 295 | } 296 | 297 | static const int v1MMNEScostTables[2][6] = { 298 | /* desc lens offs data gfx pal */ 299 | { 25, 27, 29, 31, 33, 35}, 300 | { 26, 28, 30, 32, 34, 36} 301 | }; 302 | 303 | void graphics_loadCostumeSet(uint8_t n) 304 | { 305 | HROOM r; 306 | //DEBUG_PRINTF("Load costume set %u\n", n); 307 | r = seekResource(&costumes[v1MMNEScostTables[n][0]]); 308 | readResource(r, costdesc, sizeof(costdesc)); 309 | closeRoom(r); 310 | r = seekResource(&costumes[v1MMNEScostTables[n][1]]); 311 | readResource(r, costlens, sizeof(costlens)); 312 | closeRoom(r); 313 | r = seekResource(&costumes[v1MMNEScostTables[n][2]]); 314 | readResource(r, costoffs, sizeof(costoffs)); 315 | closeRoom(r); 316 | 317 | // read this directly from the file 318 | costdata_id = v1MMNEScostTables[n][3]; 319 | // r = seekResource(&costumes[v1MMNEScostTables[n][3]]); 320 | // readResource(r, costdata, sizeof(costdata)); 321 | // closeRoom(r); 322 | 323 | // decode tiles, but do not copy them to ZXNext yet, 324 | // because 256 patterns won't fit 325 | decodeSpriteTiles(v1MMNEScostTables[n][4]); 326 | 327 | // palette for sprites 328 | r = seekResource(&costumes[v1MMNEScostTables[n][5]]); 329 | // skip size 330 | readWord(r); 331 | updatePalette(r, PAL_SPRITES); 332 | closeRoom(r); 333 | } 334 | 335 | static void clearLine(uint8_t y) 336 | { 337 | uint8_t *screen = (uint8_t*)TILEMAP_BASE + LINE_BYTES * y; 338 | //uint8_t *screen = (uint8_t*)0 + LINE_BYTES * y; 339 | uint8_t *end = screen + LINE_BYTES; 340 | while (screen != end) 341 | { 342 | // hardcoded black tile 343 | *screen++ = 0; 344 | // hardcoded background color offset 345 | *screen++ = 3 << 4; 346 | } 347 | } 348 | 349 | static void clearTalkArea(void) 350 | { 351 | uint8_t i; 352 | for (i = 0 ; i < SCREEN_TOP ; ++i) 353 | clearLine(i); 354 | } 355 | 356 | void graphics_clearInventory(void) 357 | { 358 | //PUSH_PAGE(0, SCREEN_PAGE); 359 | uint8_t i; 360 | for (i = 0 ; i < INV_ROWS ; ++i) 361 | clearLine(INV_TOP + i); 362 | //POP_PAGE(0); 363 | } 364 | 365 | 366 | void graphics_printAtXY(const uint8_t *s, uint8_t x, uint8_t y, uint8_t left, uint8_t color, uint8_t len) 367 | { 368 | //PUSH_PAGE(0, SCREEN_PAGE); 369 | PUSH_PAGE(2, GRAPH_PAGE); 370 | 371 | uint8_t *screen = (uint8_t*)TILEMAP_BASE + x * TILE_BYTES + y * LINE_BYTES; 372 | //uint8_t *screen = (uint8_t*)0 + x * TILE_BYTES + y * LINE_BYTES; 373 | uint8_t curpos = 0; 374 | while (*s) 375 | { 376 | uint8_t c = *s++; 377 | if (curpos >= len) 378 | continue; 379 | if (c == '@') 380 | { 381 | continue; 382 | } 383 | else if (c == ' ') 384 | { 385 | // need special space color and symbol 386 | *screen++ = 0; 387 | *screen++ = 3 << 4; 388 | ++x; 389 | continue; 390 | } 391 | else if (c == 1) 392 | { 393 | curpos = 0; 394 | // newline 395 | if (x != 0) 396 | { 397 | screen += 2 * left * TILE_BYTES + (LINE_WIDTH - left - x) * TILE_BYTES; 398 | x = 0; 399 | } 400 | continue; 401 | } 402 | else if (c == 3) 403 | { 404 | // next message, should not be passed here 405 | } 406 | else if (c == 4) 407 | { 408 | // don't know what code is it, skip 409 | continue; 410 | } 411 | if (c < 0x20 || c >= 0x80) 412 | { 413 | DEBUG_PRINTF("\nSpecial code %u\n", c); 414 | DEBUG_HALT; 415 | } 416 | //DEBUG_PRINTF("char %u pattern %u\n", *s, translationTable[*s]); 417 | *screen++ = translationTable[c - ' ']; 418 | *screen++ = color << 4; 419 | ++x; 420 | // if (x >= LINE_WIDTH - gap) 421 | // { 422 | // x = 0; 423 | // screen += 2 * gap * TILE_BYTES; 424 | // } 425 | } 426 | 427 | POP_PAGE(2); 428 | //POP_PAGE(0); 429 | } 430 | 431 | void graphics_print(const char *s, uint8_t c) 432 | { 433 | //PUSH_PAGE(0, SCREEN_PAGE); 434 | clearTalkArea(); 435 | // TODO: correct color 436 | c = 3; 437 | graphics_printAtXY(s, TEXT_GAP, 0, TEXT_GAP, c, SCREEN_WIDTH); 438 | //POP_PAGE(0); 439 | } 440 | 441 | void graphics_printSentence(const char *s) 442 | { 443 | //PUSH_PAGE(0, SCREEN_PAGE); 444 | clearLine(SCREEN_HEIGHT + SCREEN_TOP); 445 | // color offset 3 to match transparent color 446 | graphics_printAtXY(s, TEXT_GAP, SCREEN_HEIGHT + SCREEN_TOP, TEXT_GAP, 3, SCREEN_WIDTH); 447 | //POP_PAGE(0); 448 | } 449 | 450 | void graphics_drawVerb(VerbSlot *v) 451 | { 452 | // color offset 3 to match transparent color 453 | graphics_printAtXY(v->name, LINE_GAP + v->x, v->y, LINE_GAP, 3, SCREEN_WIDTH); 454 | } 455 | 456 | void graphics_drawInventory(uint8_t slot, const char *s) 457 | { 458 | uint8_t x = 0; 459 | // only 2 cols supported 460 | if (slot % INV_COLS) 461 | x = INV_SLOT_WIDTH + INV_ARR_W; 462 | // color offset 3 to match transparent color 463 | graphics_printAtXY(s, INV_GAP + x, 464 | INV_TOP + slot / INV_COLS, INV_GAP, 3, INV_SLOT_WIDTH); 465 | } 466 | 467 | void graphics_clearScreen(void) 468 | { 469 | uint8_t i; 470 | 471 | // clear sprites first 472 | for (i = 0 ; i <= 127 ; ++i) 473 | { 474 | IO_SPRITE_SLOT = i; 475 | IO_SPRITE_ATTRIBUTE = 0; 476 | IO_SPRITE_ATTRIBUTE = 0; 477 | IO_SPRITE_ATTRIBUTE = 0; 478 | IO_SPRITE_ATTRIBUTE = 0; 479 | } 480 | 481 | //PUSH_PAGE(0, SCREEN_PAGE); 482 | // clear tiles after 483 | for (i = 0 ; i < SCREEN_TOP + SCREEN_HEIGHT ; ++i) 484 | clearLine(i); 485 | //POP_PAGE(0); 486 | } 487 | 488 | void graphics_updateScreen(void) 489 | { 490 | //PUSH_PAGE(0, SCREEN_PAGE); 491 | PUSH_PAGE(2, GRAPH_PAGE); 492 | 493 | //DEBUG_PRINTF("Draw nametable %x\n", nametable[15][2]); 494 | 495 | uint8_t i, j, x, y; 496 | uint16_t xx; 497 | // update message area 498 | if (talkDelay) 499 | { 500 | if (!--talkDelay) 501 | { 502 | clearTalkArea(); 503 | } 504 | } 505 | 506 | // update background picture 507 | uint8_t *screen = (uint8_t*)TILEMAP_BASE + SCREEN_TOP * LINE_BYTES; 508 | //uint8_t *screen = (uint8_t*)0 + SCREEN_TOP * LINE_BYTES; 509 | 510 | uint8_t gap = LINE_GAP; 511 | uint8_t offs; 512 | uint8_t width = SCREEN_WIDTH; 513 | if (roomWidth < SCREEN_WIDTH) 514 | { 515 | gap += (SCREEN_WIDTH - roomWidth) / 2; 516 | width = roomWidth; 517 | } 518 | offs = camera_getVirtScreenX(); 519 | 520 | uint8_t light = scummVars[VAR_CURRENT_LIGHTS] & LIGHTMODE_actor_use_base_palette; 521 | 522 | uint8_t bytegap = gap * 2; 523 | //DEBUG_PRINTF("cameraX=%u roomWidth=%u offs=%u gap=%u\n", cameraX, roomWidth, offs, gap); 524 | // DEBUG_PRINTF("Screen bytes %x %x\n", 525 | // nametable[0][2], 526 | // nametable[0][offs + 2]); 527 | for (i = 0 ; i < SCREEN_HEIGHT ; ++i) 528 | { 529 | screen += bytegap; 530 | for (j = 0 ; j < width ; ++j) 531 | { 532 | x = j + offs + 2; 533 | y = i; 534 | uint8_t attr = (attributes[((y << 2) & 0x30) | ((x >> 2) & 0xF)] 535 | >> (((y & 2) << 1) | (x & 2))) & 0x3; 536 | /* 2 empty cells at the beginning */ 537 | if (light) 538 | { 539 | //if (i == 0) DEBUG_PRINTF("%x ", nametable[i][offs + j + 2]); 540 | *screen = nametable[i][offs + j + 2]; 541 | ++screen; 542 | *screen = attr << 4; 543 | ++screen; 544 | } 545 | else 546 | { 547 | *screen = 0; 548 | ++screen; 549 | *screen = 0; 550 | ++screen; 551 | } 552 | } 553 | screen += bytegap; 554 | } 555 | //DEBUG_PUTS("\n"); 556 | 557 | POP_PAGE(2); 558 | //POP_PAGE(0); 559 | 560 | actors_draw(offs, gap); 561 | } 562 | 563 | void graphics_drawObject(uint16_t bimoffs, uint8_t ox, uint8_t oy, uint8_t w, uint8_t h) 564 | { 565 | if (!bimoffs) 566 | return; 567 | 568 | //DEBUG_PRINTF("--- decode object ox=%u oy=%u w=%u h=%u\n", ox, oy, w, h); 569 | 570 | HROOM r = openRoom(currentRoom); 571 | esx_f_seek(r, bimoffs, ESX_SEEK_SET); 572 | 573 | PUSH_PAGE(2, GRAPH_PAGE); 574 | uint8_t len = readByte(r); 575 | for (uint8_t y = 0 ; y < h ; ++y) 576 | { 577 | uint8_t x = 0; 578 | while (x < w) 579 | { 580 | uint8_t next = readByte(r); 581 | for (uint8_t i = 0 ; i < (len & 0x7f) ; ++i) 582 | { 583 | // DEBUG_PRINTF("%x ", next); 584 | nametable[oy + y][x + ox + 2] = next; 585 | if (len & 0x80) 586 | next = readByte(r); 587 | ++x; 588 | } 589 | if (!(len & 0x80)) 590 | next = readByte(r); 591 | len = next; 592 | } 593 | // DEBUG_PUTS("\n"); 594 | } 595 | 596 | // decode attribute update data 597 | uint8_t y = h / 2; 598 | uint8_t ay = oy; 599 | while (y) 600 | { 601 | uint8_t ax = ox + 2; 602 | uint8_t x = 0; 603 | uint8_t adata = 0; 604 | while (x < (w >> 1)) 605 | { 606 | if (!(x & 3)) 607 | adata = readByte(r); 608 | uint8_t *dest = &attributes[((ay << 2) & 0x30) | ((ax >> 2) & 0xF)]; 609 | 610 | uint8_t aand = 3; 611 | uint8_t aor = adata & 3; 612 | if (ay & 0x02) 613 | { 614 | aand <<= 4; 615 | aor <<= 4; 616 | } 617 | if (ax & 0x02) 618 | { 619 | aand <<= 2; 620 | aor <<= 2; 621 | } 622 | *dest = ((~aand) & *dest) | aor; 623 | 624 | adata >>= 2; 625 | ax += 2; 626 | x++; 627 | } 628 | ay += 2; 629 | y--; 630 | } 631 | 632 | // decode mask update data 633 | // if (!_NES.hasmask) 634 | // return; 635 | // int mx, mwidth; 636 | // int lmask, rmask; 637 | // mx = *ptr++; 638 | // mwidth = *ptr++; 639 | // lmask = *ptr++; 640 | // rmask = *ptr++; 641 | 642 | // for (y = 0; y < height; ++y) { 643 | // byte *dest = &_NES.masktableObj[y + ypos][mx]; 644 | // *dest = (*dest & lmask) | *ptr++; 645 | // dest++; 646 | // for (x = 1; x < mwidth; x++) { 647 | // if (x + 1 == mwidth) 648 | // *dest = (*dest & rmask) | *ptr++; 649 | // else 650 | // *dest = *ptr++; 651 | // dest++; 652 | // } 653 | // } 654 | 655 | closeRoom(r); 656 | 657 | POP_PAGE(2); 658 | } 659 | 660 | uint8_t graphics_findVirtScreen(uint8_t y) 661 | { 662 | if (y <= SCREEN_TOP) 663 | return kTextVirtScreen; 664 | if (y <= SCREEN_TOP + SCREEN_HEIGHT) 665 | return kMainVirtScreen; 666 | return kVerbVirtScreen; 667 | } 668 | -------------------------------------------------------------------------------- /script.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "script.h" 4 | #include "resource.h" 5 | #include "debug.h" 6 | #include "actor.h" 7 | #include "room.h" 8 | #include "camera.h" 9 | #include "object.h" 10 | #include "graphics.h" 11 | #include "costume.h" 12 | #include "cursor.h" 13 | #include "verbs.h" 14 | #include "engine.h" 15 | #include "box.h" 16 | #include "string.h" 17 | 18 | #define MAX_SCRIPTS 16 19 | 20 | enum { 21 | PARAM_1 = 0x80, 22 | PARAM_2 = 0x40, 23 | PARAM_3 = 0x20 24 | }; 25 | 26 | enum UserStates { 27 | USERSTATE_SET_FREEZE = 0x01, // freeze scripts if USERSTATE_FREEZE_ON is set, unfreeze otherwise 28 | USERSTATE_SET_CURSOR = 0x02, // shows cursor if USERSTATE_CURSOR_ON is set, hides it otherwise 29 | USERSTATE_SET_IFACE = 0x04, // change user-interface (sentence-line, inventory, verb-area) 30 | USERSTATE_FREEZE_ON = 0x08, // only interpreted if USERSTATE_SET_FREEZE is set 31 | USERSTATE_CURSOR_ON = 0x10, // only interpreted if USERSTATE_SET_CURSOR is set 32 | USERSTATE_IFACE_SENTENCE = 0x20, // only interpreted if USERSTATE_SET_IFACE is set 33 | USERSTATE_IFACE_INVENTORY = 0x40, // only interpreted if USERSTATE_SET_IFACE is set 34 | USERSTATE_IFACE_VERBS = 0x80 // only interpreted if USERSTATE_SET_IFACE is set 35 | }; 36 | 37 | #define _numVariables 800 38 | 39 | int16_t scummVars[_numVariables]; 40 | static uint8_t curScript; 41 | static uint8_t opcode; 42 | static uint8_t exitFlag; 43 | static uint8_t clickDelay; 44 | // cutscene variables 45 | static uint16_t cutSceneData[4]; 46 | static uint8_t cutSceneScript; 47 | static uint16_t cutScenePtr; 48 | 49 | // mapped in MMU1 to pages 32-... 50 | // can be switched for different scripts 51 | extern uint8_t scriptBytes[4096]; 52 | extern uint16_t script_id; 53 | extern uint8_t script_status; 54 | extern uint8_t script_room; 55 | extern uint16_t script_offset; 56 | extern uint32_t script_delay; 57 | extern uint16_t script_resultVarNumber; 58 | 59 | #define FIRST_BANK 32 60 | 61 | typedef struct SentenceTab { 62 | uint8_t verb; 63 | uint8_t preposition; 64 | uint16_t objectA; 65 | uint16_t objectB; 66 | uint8_t freezeCount; 67 | } SentenceTab; 68 | 69 | #define SENTENCE_SCRIPT 2 70 | #define MAX_SENTENCE 4 71 | static SentenceTab sentence[MAX_SENTENCE]; 72 | static uint8_t sentenceNum; 73 | 74 | ///////////////////////////////////////////////////////////////////////// 75 | // scripting internals 76 | ///////////////////////////////////////////////////////////////////////// 77 | 78 | static void switchScriptPage(uint8_t s) 79 | { 80 | //DEBUG_PRINTF("Select page %u\n", FIRST_BANK + s); 81 | ZXN_WRITE_MMU2(FIRST_BANK + s); 82 | } 83 | 84 | static void loadScriptBytes(uint16_t roomoffs) 85 | { 86 | HROOM r = openRoom(script_room); 87 | esx_f_seek(r, roomoffs, ESX_SEEK_SET); 88 | //readResource(r, scriptBytes, sizeof(scriptBytes)); 89 | // always read up to 4k, because object scripts do not include size field 90 | readBuffer(r, scriptBytes, sizeof(scriptBytes)); 91 | closeRoom(r); 92 | // for (int8_t i = 0 ; i < 16 ; ++i) 93 | // DEBUG_PRINTF("%x ", scriptBytes[i]); 94 | // DEBUG_PUTS("\n"); 95 | } 96 | 97 | void initScriptPages(void) 98 | { 99 | uint8_t f; 100 | for (f = 0 ; f < MAX_SCRIPTS ; ++f) 101 | { 102 | switchScriptPage(f); 103 | script_id = 0; 104 | } 105 | } 106 | 107 | static uint8_t findEmptyFrame(void) 108 | { 109 | uint8_t f; 110 | for (f = 0 ; f < MAX_SCRIPTS ; ++f) 111 | { 112 | switchScriptPage(f); 113 | if (script_id == 0) 114 | { 115 | switchScriptPage(curScript); 116 | return f; 117 | } 118 | } 119 | DEBUG_ASSERT(0, "Number of scripts exceeded\n"); 120 | } 121 | 122 | static uint8_t fetchScriptByte(void) 123 | { 124 | return scriptBytes[script_offset++]; 125 | } 126 | 127 | static uint16_t fetchScriptWord() 128 | { 129 | uint8_t b1 = fetchScriptByte(); 130 | uint8_t b2 = fetchScriptByte(); 131 | return b1 + (b2 << 8); 132 | } 133 | 134 | static uint16_t readVar(uint16_t v) 135 | { 136 | return scummVars[v]; 137 | } 138 | 139 | static uint16_t getVar(void) 140 | { 141 | return readVar(fetchScriptByte()); 142 | } 143 | 144 | void writeVar(uint16_t var, uint16_t val) 145 | { 146 | scummVars[var] = val; 147 | //DEBUG_PRINTF("var%u = %u\n", var, val); 148 | } 149 | 150 | static int16_t fetchScriptWordSigned(void) 151 | { 152 | return (int16_t)fetchScriptWord(); 153 | } 154 | 155 | static uint8_t getVarOrDirectByte(uint8_t mask) 156 | { 157 | if (opcode & mask) 158 | return getVar(); 159 | return fetchScriptByte(); 160 | } 161 | 162 | static int16_t getVarOrDirectWord(uint8_t mask) 163 | { 164 | if (opcode & mask) 165 | return getVar(); 166 | return fetchScriptWordSigned(); 167 | } 168 | 169 | static void setResult(uint16_t value) 170 | { 171 | writeVar(script_resultVarNumber, value); 172 | } 173 | 174 | static void stopScriptIdx(uint8_t index) 175 | { 176 | switchScriptPage(index); 177 | DEBUG_PRINTF("Stopping the script %u room %u offset %x\n", 178 | script_id, script_room, script_offset - 4); 179 | script_id = 0; 180 | if (index == curScript) 181 | exitFlag = 1; 182 | switchScriptPage(curScript); 183 | } 184 | 185 | static uint8_t getScriptIndex(uint8_t id) 186 | { 187 | if (id == 0) 188 | return MAX_SCRIPTS; 189 | uint8_t i; 190 | for (i = 0 ; i < MAX_SCRIPTS ; ++i) 191 | { 192 | switchScriptPage(i); 193 | if (script_id == id) 194 | break; 195 | } 196 | switchScriptPage(curScript); 197 | return i; 198 | } 199 | 200 | static void stopScript(uint8_t id) 201 | { 202 | uint8_t idx = getScriptIndex(id); 203 | if (idx == MAX_SCRIPTS) 204 | return; 205 | stopScriptIdx(idx); 206 | } 207 | 208 | static void op_stopObjectCode(void) 209 | { 210 | stopScriptIdx(curScript); 211 | } 212 | 213 | static void op_stopScript(void) 214 | { 215 | uint8_t s = getVarOrDirectByte(PARAM_1); 216 | 217 | DEBUG_PRINTF("Stopping from script %u room %u next offset %x\n", 218 | script_id, script_room, script_offset); 219 | 220 | // WORKAROUND bug #4112: If you enter the lab while Dr. Fred has the powered turned off 221 | // to repair the Zom-B-Matic, the script will be stopped and the power will never turn 222 | // back on. This fix forces the power on, when the player enters the lab, 223 | // if the script which turned it off is running 224 | // if (_game.id == GID_MANIAC && _roomResource == 4 && isScriptRunning(MM_SCRIPT(138))) { 225 | // TODO 226 | 227 | uint8_t i = curScript; 228 | if (s != 0) 229 | { 230 | i = getScriptIndex(s); 231 | if (i == MAX_SCRIPTS) 232 | { 233 | DEBUG_PRINTF("Stopping the script %u which is not executing\n", s); 234 | return; 235 | } 236 | } 237 | 238 | stopScriptIdx(i); 239 | } 240 | 241 | static void getResultPos(void) 242 | { 243 | script_resultVarNumber = fetchScriptByte(); 244 | } 245 | 246 | static void pushScript(uint16_t id, uint8_t room, uint16_t roomoffs, uint16_t offset) 247 | { 248 | uint8_t s = findEmptyFrame(); 249 | DEBUG_PRINTF("Starting script %u room %u offset %u in frame %u\n", id, room, roomoffs, s); 250 | switchScriptPage(s); 251 | script_id = id; 252 | script_status = 0; 253 | script_room = room; 254 | script_offset = offset; 255 | script_delay = 0; 256 | loadScriptBytes(roomoffs); 257 | switchScriptPage(curScript); 258 | } 259 | 260 | static void parseString(uint8_t actor) 261 | { 262 | uint8_t *message = message_new(); 263 | uint8_t *ptr = message; 264 | uint8_t c; 265 | 266 | while ((c = fetchScriptByte())) { 267 | 268 | uint8_t insertSpace = (c & 0x80) != 0; 269 | c &= 0x7f; 270 | 271 | // if (c < 8) { 272 | // // Special codes as seen in CHARSET_1 etc. My guess is that they 273 | // // have a similar function as the corresponding embedded stuff in modern 274 | // // games. Hence for now we convert them to the modern format. 275 | // // This might allow us to reuse the existing code. 276 | // // *ptr++ = 0xFF; 277 | // // *ptr++ = c; 278 | // if (c > 3) { 279 | // *ptr++ = fetchScriptByte(); 280 | // *ptr++ = 0; 281 | // } 282 | // } else 283 | // *ptr++ = c; 284 | //DEBUG_PUTC(c); 285 | *ptr++ = c; 286 | 287 | if (insertSpace) 288 | { 289 | //DEBUG_PUTC(' '); 290 | *ptr++ = ' '; 291 | } 292 | 293 | } 294 | *ptr = 0; 295 | 296 | // TODO 297 | // int textSlot = 0; 298 | // _string[textSlot].xpos = 0; 299 | // _string[textSlot].ypos = 0; 300 | // _string[textSlot].right = _screenWidth - 1; 301 | // _string[textSlot].center = false; 302 | // _string[textSlot].overhead = false; 303 | 304 | actor_talk(actor, message); 305 | } 306 | 307 | static void jumpRelative(int cond) 308 | { 309 | int16_t offset = fetchScriptWord(); 310 | if (!cond) 311 | script_offset += offset; 312 | // DEBUG_PRINTF(" jump to %x\n", stack[curScript].offset + offset); 313 | } 314 | 315 | static uint8_t isScriptRunning(uint8_t s) 316 | { 317 | uint8_t i = getScriptIndex(s); 318 | return i != MAX_SCRIPTS; 319 | } 320 | 321 | static uint16_t getActiveObject(void) 322 | { 323 | return getVarOrDirectWord(PARAM_1); 324 | } 325 | 326 | static void ifStateCommon(uint16_t type) 327 | { 328 | uint16_t obj = getActiveObject(); 329 | 330 | jumpRelative((object_getState(obj) & type) != 0); 331 | } 332 | 333 | static void ifNotStateCommon(uint16_t type) 334 | { 335 | uint16_t obj = getActiveObject(); 336 | 337 | jumpRelative((object_getState(obj) & type) == 0); 338 | } 339 | 340 | static void clearStateCommon(uint8_t type) 341 | { 342 | uint16_t obj = getActiveObject(); 343 | object_setState(obj, object_getState(obj) & ~type); 344 | } 345 | 346 | static void setStateCommon(uint8_t type) 347 | { 348 | uint16_t obj = getActiveObject(); 349 | object_setState(obj, object_getState(obj) | type); 350 | } 351 | 352 | static void resetSentence(void) 353 | { 354 | scummVars[VAR_SENTENCE_VERB] = scummVars[VAR_BACKUP_VERB]; 355 | scummVars[VAR_SENTENCE_OBJECT1] = 0; 356 | scummVars[VAR_SENTENCE_OBJECT2] = 0; 357 | scummVars[VAR_SENTENCE_PREPOSITION] = 0; 358 | } 359 | 360 | static void freezeScripts(void) 361 | { 362 | uint8_t i; 363 | for (i = 0 ; i < MAX_SCRIPTS ; ++i) 364 | { 365 | if (i != curScript) 366 | { 367 | switchScriptPage(i); 368 | if (script_id) 369 | { 370 | script_status = 0x80; 371 | } 372 | } 373 | } 374 | switchScriptPage(curScript); 375 | } 376 | 377 | static void unfreezeScripts(void) 378 | { 379 | uint8_t i; 380 | for (i = 0 ; i < MAX_SCRIPTS ; ++i) 381 | { 382 | switchScriptPage(i); 383 | script_status &= 0x7f; 384 | } 385 | switchScriptPage(curScript); 386 | } 387 | 388 | static void setUserState(uint8_t state) 389 | { 390 | // if (state & USERSTATE_SET_IFACE) { // Userface 391 | // _userState = (_userState & ~USERSTATE_IFACE_ALL) | (state & USERSTATE_IFACE_ALL); 392 | // } 393 | 394 | if (state & USERSTATE_SET_FREEZE) { // Freeze 395 | if (state & USERSTATE_FREEZE_ON) 396 | freezeScripts(); 397 | else 398 | unfreezeScripts(); 399 | } 400 | 401 | // Cursor Show/Hide 402 | if (state & USERSTATE_SET_CURSOR) 403 | { 404 | //_userState = (_userState & ~USERSTATE_CURSOR_ON) | (state & USERSTATE_CURSOR_ON); 405 | if (state & USERSTATE_CURSOR_ON) 406 | { 407 | //_userPut = 1; 408 | cursor_setState(1); 409 | } 410 | else 411 | { 412 | //_userPut = 0; 413 | cursor_setState(0); 414 | } 415 | } 416 | 417 | // // Hide all verbs and inventory 418 | // Common::Rect rect; 419 | // rect.top = _virtscr[kVerbVirtScreen].topline; 420 | // rect.bottom = _virtscr[kVerbVirtScreen].topline + 8 * 88; 421 | // rect.right = _virtscr[kVerbVirtScreen].w - 1; 422 | // rect.left = 16; 423 | // restoreBackground(rect); 424 | 425 | // Draw all verbs and inventory 426 | //redrawVerbs(); 427 | runInventoryScript(1); 428 | } 429 | 430 | /////////////////////////////////////////////////////////// 431 | // opcodes 432 | /////////////////////////////////////////////////////////// 433 | 434 | static void op_breakHere(void) 435 | { 436 | //DEBUG_PUTS("breakHere\n"); 437 | //stopScript(); 438 | exitFlag = 1; 439 | } 440 | 441 | static void op_putActor(void) 442 | { 443 | uint8_t act = getVarOrDirectByte(PARAM_1); 444 | uint8_t x = getVarOrDirectByte(PARAM_2); 445 | uint8_t y = getVarOrDirectByte(PARAM_3); 446 | 447 | actor_put(act, x, y); 448 | } 449 | 450 | static void op_getActorY(void) 451 | { 452 | getResultPos(); 453 | 454 | uint8_t a = getVarOrDirectByte(PARAM_1); 455 | setResult(actors[a].y); 456 | } 457 | 458 | 459 | static void op_startMusic(void) 460 | { 461 | getVarOrDirectByte(PARAM_1); 462 | } 463 | 464 | static void op_getActorRoom(void) 465 | { 466 | getResultPos(); 467 | uint8_t act = getVarOrDirectByte(PARAM_1); 468 | //DEBUG_PRINTF("Get actor %u room %u\n", act, actors[act].room); 469 | setResult(actors[act].room); 470 | } 471 | 472 | static void op_drawObject(void) 473 | { 474 | uint8_t xpos, ypos; 475 | uint16_t idx; 476 | 477 | idx = getVarOrDirectWord(PARAM_1); 478 | xpos = getVarOrDirectByte(PARAM_2); 479 | ypos = getVarOrDirectByte(PARAM_3); 480 | //DEBUG_PRINTF("Draw object %u at %u,%u\n", idx, xpos, ypos); 481 | 482 | if (xpos != 0xFF) 483 | { 484 | DEBUG_PUTS("TODO: drawing object at coordinates\n"); 485 | DEBUG_HALT; 486 | // od->walk_x += (xpos * 8) - od->x_pos; 487 | // od->x_pos = xpos * 8; 488 | // od->walk_y += (ypos * 8) - od->y_pos; 489 | // od->y_pos = ypos * 8; 490 | } 491 | object_draw(idx); 492 | 493 | // x = od->x_pos; 494 | // y = od->y_pos; 495 | // w = od->width; 496 | // h = od->height; 497 | 498 | // i = _numLocalObjects; 499 | // while (i--) { 500 | // if (_objs[i].obj_nr && _objs[i].x_pos == x && _objs[i].y_pos == y && _objs[i].width == w && _objs[i].height == h) 501 | // putState(_objs[i].obj_nr, getState(_objs[i].obj_nr) & ~kObjectState_08); 502 | // } 503 | 504 | object_setState(idx, object_getState(idx) | kObjectState_08); 505 | // putState(obj, getState(od->obj_nr) | kObjectState_08); 506 | } 507 | 508 | static void op_setState04(void) 509 | { 510 | setStateCommon(kObjectStateLocked); 511 | } 512 | 513 | static void op_clearState04(void) 514 | { 515 | clearStateCommon(kObjectStateLocked); 516 | } 517 | 518 | static void op_setState02(void) 519 | { 520 | setStateCommon(kObjectStateUntouchable); 521 | } 522 | 523 | static void op_clearState02(void) 524 | { 525 | clearStateCommon(kObjectStateUntouchable); 526 | } 527 | 528 | static void op_setState01(void) 529 | { 530 | setStateCommon(kObjectStatePickupable); 531 | } 532 | 533 | static void op_clearState01(void) 534 | { 535 | clearStateCommon(kObjectStatePickupable); 536 | } 537 | 538 | static void op_getClosestObjActor(void) 539 | { 540 | // This code can't detect any actors farther away than 255 units 541 | // (pixels in newer games, characters in older ones.) But this is 542 | // perfectly OK, as it is exactly how the original behaved. 543 | 544 | uint16_t closest_obj = 0xFF, closest_dist = 0xFF; 545 | 546 | getResultPos(); 547 | 548 | uint16_t act = getVarOrDirectWord(PARAM_1); 549 | uint16_t obj = scummVars[VAR_ACTOR_RANGE_MAX]; 550 | 551 | do { 552 | uint16_t dist = getObjActToObjActDist(act, obj); 553 | if (dist < closest_dist) 554 | { 555 | closest_dist = dist; 556 | closest_obj = obj; 557 | } 558 | } while (--obj >= scummVars[VAR_ACTOR_RANGE_MIN]); 559 | 560 | setResult(closest_obj); 561 | } 562 | 563 | static void op_setState08(void) 564 | { 565 | uint16_t j = getVarOrDirectWord(PARAM_1); 566 | //DEBUG_PRINTF("setState08 %u\n", j); 567 | 568 | object_setState(j, object_getState(j) | kObjectState_08); 569 | // markObjectRectAsDirty(obj); 570 | // clearDrawObjectQueue(); 571 | 572 | //Object *obj = object_get(j); 573 | //if (obj) 574 | { 575 | objects_redraw(); 576 | //graphics_drawObject(obj); 577 | } 578 | } 579 | 580 | static void op_clearState08(void) 581 | { 582 | uint16_t j = getActiveObject(); 583 | //DEBUG_PRINTF("clearState08 %u\n", j); 584 | 585 | object_setState(j, object_getState(j) & ~kObjectState_08); 586 | //markObjectRectAsDirty(obj); 587 | //clearDrawObjectQueue(); 588 | 589 | //Object *obj = object_get(j); 590 | //if (obj) 591 | { 592 | objects_redraw(); 593 | } 594 | } 595 | 596 | static void op_resourceRoutines(void) 597 | { 598 | //DEBUG_PUTS("resourceRoutines\n"); 599 | uint8_t resid = getVarOrDirectByte(PARAM_1); 600 | uint8_t opcode = fetchScriptByte(); 601 | // DEBUG_PRINTF(" resid %u opcode %u\n", resid, opcode); 602 | // just ensures that resource is loaded 603 | // TODO 604 | } 605 | 606 | static void op_animateActor(void) 607 | { 608 | uint8_t act = getVarOrDirectByte(PARAM_1); 609 | uint8_t anim = getVarOrDirectByte(PARAM_2); 610 | //DEBUG_PRINTF("animateActor %u %u\n", act, anim); 611 | 612 | actor_animate(act, anim); 613 | } 614 | 615 | static void op_panCameraTo(void) 616 | { 617 | //DEBUG_PUTS("panCameraTo\n"); 618 | camera_panTo(getVarOrDirectByte(PARAM_1)); 619 | } 620 | 621 | static void op_walkActorToActor(void) 622 | { 623 | uint8_t nr = getVarOrDirectByte(PARAM_1); 624 | uint8_t nr2 = getVarOrDirectByte(PARAM_2); 625 | uint8_t dist = fetchScriptByte(); 626 | 627 | if (!actor_isInCurrentRoom(nr)) 628 | return; 629 | 630 | if (!actor_isInCurrentRoom(nr2)) 631 | return; 632 | 633 | actor_walkToActor(nr, nr2, dist); 634 | } 635 | 636 | static void op_getObjectOwner(void) 637 | { 638 | getResultPos(); 639 | setResult(object_getOwner(getVarOrDirectWord(PARAM_1))); 640 | } 641 | 642 | static void op_move(void) 643 | { 644 | //DEBUG_PUTS("move\n"); 645 | getResultPos(); 646 | setResult(getVarOrDirectWord(PARAM_1)); 647 | } 648 | 649 | static void op_setBitVar(void) 650 | { 651 | uint16_t var = fetchScriptWord(); 652 | uint8_t a = getVarOrDirectByte(PARAM_1); 653 | 654 | uint16_t bit_var = var + a; 655 | uint8_t bit_offset = bit_var & 0x0f; 656 | bit_var >>= 4; 657 | 658 | if (getVarOrDirectByte(PARAM_2)) 659 | scummVars[bit_var] |= (1 << bit_offset); 660 | else 661 | scummVars[bit_var] &= ~(1 << bit_offset); 662 | } 663 | 664 | static void op_startSound(void) 665 | { 666 | //DEBUG_PUTS("startSound\n"); 667 | getVarOrDirectByte(PARAM_1); 668 | } 669 | 670 | static void op_walkActorTo(void) 671 | { 672 | uint8_t act = getVarOrDirectByte(PARAM_1); 673 | uint8_t x = getVarOrDirectByte(PARAM_2); 674 | uint8_t y = getVarOrDirectByte(PARAM_3); 675 | 676 | actor_startWalk(act, x, y); 677 | } 678 | 679 | static void op_stopMusic(void) 680 | { 681 | // nothig but stop all sounds 682 | } 683 | 684 | static void op_putActorInRoom(void) 685 | { 686 | uint8_t act = getVarOrDirectByte(PARAM_1); 687 | uint8_t room = getVarOrDirectByte(PARAM_2); 688 | actor_setRoom(act, room); 689 | } 690 | 691 | static void op_waitForMessage(void) 692 | { 693 | if (scummVars[VAR_HAVE_MSG]) 694 | { 695 | script_offset--; 696 | op_breakHere(); 697 | } 698 | } 699 | 700 | static void op_ifNotState04(void) 701 | { 702 | ifNotStateCommon(kObjectStateLocked); 703 | } 704 | 705 | static void op_actorOps(void) 706 | { 707 | uint8_t act = getVarOrDirectByte(PARAM_1); 708 | uint8_t arg = getVarOrDirectByte(PARAM_2); 709 | 710 | uint8_t opcode = fetchScriptByte(); 711 | if (act == 0 && opcode == 5) { 712 | // This case happens in the Zak/MM bootscripts, to set the default talk color (9). 713 | //_string[0].color = arg; 714 | //DEBUG_PRINTF("TODO: Set default talk color %u\n", arg); 715 | defaultTalkColor = arg; 716 | return; 717 | } 718 | 719 | Actor *a = &actors[act]; 720 | 721 | switch (opcode) { 722 | case 1: // SO_SOUND 723 | //a->_sound[0] = arg; 724 | //DEBUG_PRINTF("TODO: Set actor %u sound %u\n", act, arg); 725 | break; 726 | // case 2: // SO_PALETTE 727 | // i = fetchScriptByte(); 728 | // //a->setPalette(i, arg); 729 | // DEBUG_PRINTF(" TODO: Set actor %u palette %u/%u\n", act, i, arg); 730 | // break; 731 | case 3: // SO_ACTOR_NAME 732 | //loadPtrToResource(rtActorName, a->_number, NULL); 733 | DEBUG_PRINTF("Set actor %u talk name '", act); 734 | { 735 | char c; 736 | char *n = a->name; 737 | do 738 | { 739 | c = fetchScriptByte(); 740 | *n++ = c; 741 | if (c) 742 | DEBUG_PUTC(c); 743 | } while (c); 744 | } 745 | DEBUG_PUTS("'\n"); 746 | break; 747 | case 4: // SO_COSTUME 748 | actor_setCostume(act, arg); 749 | break; 750 | case 5: // SO_TALK_COLOR 751 | // not used with NES sprites 752 | //actor_setTalkColor(act, arg); 753 | break; 754 | default: 755 | DEBUG_PRINTF("actorOps: opcode %u not yet supported\n", opcode); 756 | DEBUG_HALT; 757 | } 758 | } 759 | 760 | static void op_actorFromPos(void) 761 | { 762 | uint8_t x, y; 763 | getResultPos(); 764 | x = getVarOrDirectByte(PARAM_1);// * V12_X_MULTIPLIER; 765 | y = getVarOrDirectByte(PARAM_2);// * V12_Y_MULTIPLIER; 766 | setResult(actor_getFromPos(x, y)); 767 | } 768 | 769 | 770 | static void op_print(void) 771 | { 772 | uint8_t act = getVarOrDirectByte(PARAM_1); 773 | //DEBUG_PRINTF("print(%u, ", act); 774 | parseString(act); 775 | //DEBUG_PUTS(")\n"); 776 | } 777 | 778 | static void op_putActorAtObject(void) 779 | { 780 | uint8_t x, y; 781 | uint8_t a = getVarOrDirectByte(PARAM_1); 782 | uint16_t obj = getVarOrDirectWord(PARAM_2); 783 | if (object_whereIs(obj) != WIO_NOT_FOUND) 784 | { 785 | object_getXY(obj, &x, &y); 786 | boxes_adjustXY(&x, &y); 787 | } 788 | else 789 | { 790 | x = 30 / 8; 791 | y = 60 / 8; 792 | } 793 | 794 | actor_put(a, x, y); 795 | } 796 | 797 | static void op_ifNotState08(void) 798 | { 799 | ifNotStateCommon(kObjectState_08); 800 | } 801 | 802 | static void op_printEgo(void) 803 | { 804 | uint8_t act = scummVars[VAR_EGO]; 805 | //DEBUG_PRINTF("print(%u, ", act); 806 | parseString(act); 807 | //DEBUG_PUTS(")\n"); 808 | } 809 | 810 | static void op_getRandomNr(void) 811 | { 812 | //DEBUG_PUTS("getRandomNr\n"); 813 | getResultPos(); 814 | setResult(/*_rnd.getRandomNumber*/(getVarOrDirectByte(PARAM_1))); 815 | } 816 | 817 | static void op_jumpRelative(void) 818 | { 819 | jumpRelative(0); 820 | } 821 | 822 | static void op_setVarRange(void) 823 | { 824 | uint16_t a, b; 825 | 826 | //DEBUG_PUTS("setVarRange\n"); 827 | 828 | getResultPos(); 829 | a = fetchScriptByte(); 830 | do { 831 | if (opcode & 0x80) 832 | b = fetchScriptWordSigned(); 833 | else 834 | b = fetchScriptByte(); 835 | 836 | setResult(b); 837 | script_resultVarNumber++; 838 | } while (--a); 839 | } 840 | 841 | static void op_verbOps(void) 842 | { 843 | uint8_t verb = fetchScriptByte(); 844 | uint8_t slot, state; 845 | 846 | switch (verb) { 847 | case 0: // SO_DELETE_VERBS 848 | slot = getVarOrDirectByte(PARAM_1) + 1; 849 | // assert(0 < slot && slot < _numVerbs); 850 | verb_kill(slot); 851 | DEBUG_PRINTF("VerbKill %d\n", slot); 852 | break; 853 | 854 | case 0xFF: // Verb On/Off 855 | verb = fetchScriptByte(); 856 | state = fetchScriptByte(); 857 | slot = verb_getSlot(verb, 0); 858 | verbs[slot].curmode = state; 859 | DEBUG_PRINTF("VerbOnOff %d\n", slot); 860 | break; 861 | 862 | default: { // New Verb 863 | uint8_t x = fetchScriptByte();// * 8; 864 | uint8_t y = fetchScriptByte();// * 8; 865 | slot = getVarOrDirectByte(PARAM_1) + 1; 866 | uint8_t prep = fetchScriptByte(); // Only used in V1? 867 | x += 1;//8; 868 | 869 | // vs->color = 1; 870 | // vs->hicolor = 1; 871 | // vs->dimcolor = 1; 872 | // vs->type = kTextVerbType; 873 | // vs->charset_nr = _string[0]._default.charset; 874 | // vs->saveid = 0; 875 | // vs->key = 0; 876 | // vs->center = 0; 877 | // vs->imgindex = 0; 878 | // vs->prep = prep; 879 | 880 | // vs->curRect.left = x; 881 | // vs->curRect.top = y; 882 | DEBUG_ASSERT(slot < _numVerbs, "op_verbOps"); 883 | verb_kill(slot); 884 | VerbSlot *vs = &verbs[slot]; 885 | vs->verbid = verb; 886 | vs->x = x; 887 | // Vertical offset -2 for ZXNext 888 | vs->y = y - 2; 889 | vs->curmode = 1; 890 | 891 | // static const char keyboard[] = { 892 | // 'q','w','e','r', 893 | // 'a','s','d','f', 894 | // 'z','x','c','v' 895 | // }; 896 | // if (1 <= slot && slot <= ARRAYSIZE(keyboard)) 897 | // vs->key = keyboard[slot - 1]; 898 | // } 899 | 900 | // get the name 901 | char *s = vs->name; 902 | while (*s++ = fetchScriptByte()) 903 | ; 904 | DEBUG_ASSERT(s - vs->name < VERB_NAME_SIZE, "op_verbOps name"); 905 | // DEBUG_PRINTF("New verb '%s' at %d %d\n", vs->name, vs->x, vs->y); 906 | } 907 | break; 908 | } 909 | 910 | // Force redraw of the modified verb slot 911 | graphics_drawVerb(&verbs[slot]); 912 | // verbMouseOver(0); 913 | } 914 | 915 | static void op_isSoundRunning(void) 916 | { 917 | uint8_t snd; 918 | getResultPos(); 919 | snd = getVarOrDirectByte(PARAM_1); 920 | //if (snd) 921 | // snd = _sound->isSoundRunning(snd); 922 | snd = 0; 923 | setResult(snd); 924 | } 925 | 926 | void op_equalZero() 927 | { 928 | uint16_t a = getVar(); 929 | jumpRelative(a == 0); 930 | } 931 | 932 | static void op_delayVariable(void) 933 | { 934 | script_delay = getVar(); 935 | //vm.slot[_currentScript].status = ssPaused; 936 | op_breakHere(); 937 | } 938 | 939 | static void op_assignVarByte(void) 940 | { 941 | getResultPos(); 942 | setResult(fetchScriptByte()); 943 | } 944 | 945 | static void op_isGreaterEqual(void) 946 | { 947 | uint16_t a = getVar(); 948 | uint16_t b = getVarOrDirectWord(PARAM_1); 949 | jumpRelative(b >= a); 950 | } 951 | 952 | static void op_isNotEqual(void) 953 | { 954 | uint16_t a = getVar(); 955 | uint16_t b = getVarOrDirectWord(PARAM_1); 956 | jumpRelative(b != a); 957 | } 958 | 959 | static void op_isNotEqualZero(void) 960 | { 961 | uint16_t a = getVar(); 962 | jumpRelative(a != 0); 963 | } 964 | 965 | static void op_delay(void) 966 | { 967 | uint8_t a = fetchScriptByte() ^ 0xff; 968 | uint8_t b = fetchScriptByte() ^ 0xff; 969 | uint8_t c = fetchScriptByte() ^ 0xff; 970 | //DEBUG_PRINTF("delay %x %x %x\n", a, b, c); 971 | script_delay = a | (b << 8) | ((uint32_t)c << 16); 972 | // vm.slot[_currentScript].status = ssPaused; 973 | op_breakHere(); 974 | } 975 | 976 | static void op_setCameraAt(void) 977 | { 978 | camera_setX(getVarOrDirectByte(PARAM_1)/* * V12_X_MULTIPLIER*/); 979 | } 980 | 981 | static void op_isLessEqual(void) 982 | { 983 | uint16_t a = getVar(); 984 | uint16_t b = getVarOrDirectWord(PARAM_1); 985 | jumpRelative(b <= a); 986 | } 987 | 988 | static void op_subtract(void) 989 | { 990 | int16_t a; 991 | getResultPos(); 992 | a = getVarOrDirectWord(PARAM_1); 993 | scummVars[script_resultVarNumber] -= a; 994 | } 995 | 996 | static void op_waitForActor(void) 997 | { 998 | uint8_t act = getVarOrDirectByte(PARAM_1); 999 | if (actor_isMoving(act)) { 1000 | script_offset -= 2; 1001 | op_breakHere(); 1002 | } 1003 | } 1004 | 1005 | static void op_stopSound(void) 1006 | { 1007 | //DEBUG_PUTS("stopSound\n"); 1008 | getVarOrDirectByte(PARAM_1); 1009 | } 1010 | 1011 | static void op_cutscene(void) 1012 | { 1013 | DEBUG_PUTS("cutscene\n"); 1014 | 1015 | // TODO 1016 | cutSceneData[0] = 0;//_userState | (_userPut ? 16 : 0); 1017 | cutSceneData[1] = scummVars[VAR_CURSORSTATE]; 1018 | cutSceneData[2] = currentRoom; 1019 | cutSceneData[3] = cameraMode; 1020 | 1021 | scummVars[VAR_CURSORSTATE] = 200; 1022 | 1023 | // Hide inventory, freeze scripts, hide cursor 1024 | setUserState(USERSTATE_SET_IFACE | 1025 | USERSTATE_SET_CURSOR | 1026 | USERSTATE_SET_FREEZE | USERSTATE_FREEZE_ON); 1027 | 1028 | // _sentenceNum = 0; 1029 | stopScript(SENTENCE_SCRIPT); 1030 | resetSentence(); 1031 | 1032 | cutScenePtr = 0; 1033 | } 1034 | 1035 | static void op_startScript(void) 1036 | { 1037 | //DEBUG_PUTS("startScript\n"); 1038 | uint8_t script = getVarOrDirectByte(PARAM_1); 1039 | 1040 | // TODO 1041 | // if (script == MM_SCRIPT(82)) { 1042 | // if (isScriptRunning(MM_SCRIPT(83)) || isScriptRunning(MM_SCRIPT(84))) 1043 | // return; 1044 | // } 1045 | // if (_game.version >= 1 && script == 155) { 1046 | // if (VAR(120) == 1) 1047 | // return; 1048 | // } 1049 | DEBUG_PRINTF("Start script with id %u\n", script); 1050 | pushScript(script, scripts[script].room, scripts[script].roomoffs, 4); 1051 | // exit from current script to run newly started 1052 | exitFlag = 1; 1053 | } 1054 | 1055 | static void op_getActorX(void) 1056 | { 1057 | // DEBUG_PUTS("getActorX\n"); 1058 | getResultPos(); 1059 | setResult(actor_getX(getVarOrDirectByte(PARAM_1))); 1060 | } 1061 | 1062 | static void op_isLess(void) 1063 | { 1064 | uint16_t a = getVar(); 1065 | uint16_t b = getVarOrDirectWord(PARAM_1); 1066 | 1067 | jumpRelative(b < a); 1068 | } 1069 | 1070 | static void op_increment(void) 1071 | { 1072 | getResultPos(); 1073 | setResult(scummVars[script_resultVarNumber] + 1); 1074 | } 1075 | 1076 | static void op_isEqual(void) 1077 | { 1078 | uint8_t var = fetchScriptByte(); 1079 | uint16_t a = readVar(var); 1080 | uint16_t b = getVarOrDirectWord(PARAM_1); 1081 | jumpRelative(b == a); 1082 | } 1083 | 1084 | static void op_chainScript(void) 1085 | { 1086 | uint8_t script = getVarOrDirectByte(PARAM_1); 1087 | //stopScript(vm.slot[_currentScript].number); 1088 | op_stopObjectCode(); 1089 | //_currentScript = 0xFF; 1090 | runScript(script); 1091 | } 1092 | 1093 | static void op_pickupObject(void) 1094 | { 1095 | int id = getVarOrDirectWord(PARAM_1); 1096 | 1097 | int8_t where = object_whereIs(id); 1098 | 1099 | if (where == WIO_NOT_FOUND) 1100 | return; 1101 | 1102 | if (where == WIO_INVENTORY) /* Don't take an */ 1103 | return; /* object twice */ 1104 | 1105 | DEBUG_PRINTF("Pickup object %u by %u\n", id, scummVars[VAR_EGO]); 1106 | 1107 | inventory_addObject(id); 1108 | //markObjectRectAsDirty(obj); 1109 | object_setOwner(id, scummVars[VAR_EGO]); 1110 | object_setState(id, object_getState(id) | kObjectState_08 | kObjectStateUntouchable); 1111 | objects_redraw(); 1112 | 1113 | runInventoryScript(1); 1114 | //if (_game.platform == Common::kPlatformNES) 1115 | // _sound->addSoundToQueue(51); // play 'pickup' sound 1116 | } 1117 | 1118 | static void op_actorFollowCamera(void) 1119 | { 1120 | camera_followActor(getVarOrDirectByte(0x80)); 1121 | } 1122 | 1123 | static void op_beginOverride(void) 1124 | { 1125 | //DEBUG_PUTS("beginOverride\n"); 1126 | cutScenePtr = script_offset; 1127 | cutSceneScript = curScript; 1128 | 1129 | // Skip the jump instruction following the override instruction 1130 | fetchScriptByte(); 1131 | fetchScriptWord(); 1132 | } 1133 | 1134 | static void op_add(void) 1135 | { 1136 | uint16_t a; 1137 | getResultPos(); 1138 | a = getVarOrDirectWord(PARAM_1); 1139 | scummVars[script_resultVarNumber] += a; 1140 | } 1141 | 1142 | static void op_cursorCommand(void) 1143 | { 1144 | uint16_t cmd = getVarOrDirectWord(PARAM_1); 1145 | uint8_t state = cmd >> 8; 1146 | 1147 | //DEBUG_PRINTF("Set cursor state %x user state %x\n", cmd & 0xff, state); 1148 | if (cmd & 0xFF) { 1149 | scummVars[VAR_CURSORSTATE] = cmd & 0xFF; 1150 | } 1151 | 1152 | setUserState(state); 1153 | } 1154 | 1155 | static void op_loadRoomWithEgo(void) 1156 | { 1157 | // TODO? 1158 | // Actor *a; 1159 | // int obj, room, x, y, x2, y2, dir; 1160 | 1161 | uint16_t obj = getVarOrDirectWord(PARAM_1); 1162 | uint8_t room = getVarOrDirectByte(PARAM_2); 1163 | uint8_t ego = scummVars[VAR_EGO]; 1164 | 1165 | DEBUG_PRINTF("Load room %u with ego %u from script %u\n", room, ego, script_id); 1166 | 1167 | // a = derefActor(VAR(VAR_EGO), "o2_loadRoomWithEgo"); 1168 | 1169 | // // The original interpreter sets the actors new room X/Y to the last rooms X/Y 1170 | // // This fixes a problem with MM: script 161 in room 12, the 'Oomph!' script 1171 | // // This scripts runs before the actor position is set to the correct room entry location 1172 | // if ((_game.id == GID_MANIAC) && (_game.platform != Common::kPlatformNES)) { 1173 | // a->putActor(a->getPos().x, a->getPos().y, room); 1174 | // } else { 1175 | // a->putActor(0, 0, room); 1176 | // } 1177 | // _egoPositioned = false; 1178 | actor_put(ego, 0, 0); 1179 | actor_setRoom(ego, room); 1180 | 1181 | int8_t x = fetchScriptByte(); 1182 | int8_t y = fetchScriptByte(); 1183 | 1184 | startScene(room); 1185 | 1186 | uint8_t x2, y2; 1187 | object_getXY(obj, &x2, &y2); 1188 | boxes_adjustXY(&x2, &y2); 1189 | actor_put(ego, x2, y2); 1190 | // a->setDirection(dir + 180); 1191 | 1192 | // camera._dest.x = camera._cur.x = a->getPos().x; 1193 | // setCameraAt(a->getPos().x, a->getPos().y); 1194 | camera_setX(actors[ego].x); 1195 | camera_followActor(ego); 1196 | 1197 | // _fullRedraw = true; 1198 | 1199 | resetSentence(); 1200 | 1201 | if (x >= 0 && y >= 0) { 1202 | actor_startWalk(ego, x, y); 1203 | } 1204 | // script is started in startScene 1205 | //runScript(5); 1206 | } 1207 | 1208 | static void op_isScriptRunning(void) 1209 | { 1210 | // DEBUG_PUTS("isScriptRunning\n"); 1211 | getResultPos(); 1212 | setResult(isScriptRunning(getVarOrDirectByte(PARAM_1))); 1213 | } 1214 | 1215 | static void op_setOwnerOf(void) 1216 | { 1217 | uint16_t obj; 1218 | uint8_t owner; 1219 | 1220 | obj = getVarOrDirectWord(PARAM_1); 1221 | owner = getVarOrDirectByte(PARAM_2); 1222 | 1223 | object_setOwner(obj, owner); 1224 | } 1225 | 1226 | static void op_ifState04(void) 1227 | { 1228 | ifStateCommon(kObjectStateLocked); 1229 | } 1230 | 1231 | static void op_lights(void) 1232 | { 1233 | uint8_t a, b, c; 1234 | 1235 | a = getVarOrDirectByte(PARAM_1); 1236 | b = fetchScriptByte(); 1237 | c = fetchScriptByte(); 1238 | 1239 | DEBUG_PRINTF("Lights %u %u %u\n", a, b, c); 1240 | 1241 | if (c == 0) 1242 | { 1243 | scummVars[VAR_CURRENT_LIGHTS] = a; 1244 | } 1245 | else if (c == 1) 1246 | { 1247 | //_flashlight.xStrips = a; 1248 | //_flashlight.yStrips = b; 1249 | } 1250 | // redraw all 1251 | costume_updateAll(); 1252 | } 1253 | 1254 | static void op_loadRoom(void) 1255 | { 1256 | uint8_t room = getVarOrDirectByte(PARAM_1); 1257 | DEBUG_PRINTF("loadRoom %u from script %u\n", room, script_id); 1258 | 1259 | startScene(room); 1260 | // _fullRedraw = true; 1261 | } 1262 | 1263 | static void op_isGreater(void) 1264 | { 1265 | // DEBUG_PUTS("isGreater"); 1266 | uint16_t a = getVar(); 1267 | uint16_t b = getVarOrDirectWord(PARAM_1); 1268 | // DEBUG_PRINTF("%u %u\n", a, b); 1269 | jumpRelative(b > a); 1270 | } 1271 | 1272 | static void op_switchCostumeSet(void) 1273 | { 1274 | graphics_loadCostumeSet(fetchScriptByte()); 1275 | } 1276 | 1277 | static void op_getBitVar(void) 1278 | { 1279 | getResultPos(); 1280 | int var = fetchScriptWord(); 1281 | uint8_t a = getVarOrDirectByte(PARAM_1); 1282 | 1283 | int bit_var = var + a; 1284 | int bit_offset = bit_var & 0x0f; 1285 | bit_var >>= 4; 1286 | 1287 | setResult((scummVars[bit_var] & (1 << bit_offset)) ? 1 : 0); 1288 | } 1289 | 1290 | static void op_endCutscene(void) 1291 | { 1292 | scummVars[VAR_OVERRIDE] = 0; 1293 | 1294 | cutSceneScript = 0; 1295 | cutScenePtr = 0; 1296 | scummVars[VAR_CURSORSTATE] = cutSceneData[1]; 1297 | // Reset user state to values before cutscene 1298 | setUserState(cutSceneData[0] | USERSTATE_SET_IFACE | USERSTATE_SET_CURSOR | USERSTATE_SET_FREEZE); 1299 | 1300 | camera_followActor(scummVars[VAR_EGO]); 1301 | } 1302 | 1303 | static void op_findObject(void) 1304 | { 1305 | getResultPos(); 1306 | // objects store x y without multipliers 1307 | uint16_t x = getVarOrDirectByte(PARAM_1);// * V12_X_MULTIPLIER; 1308 | uint16_t y = getVarOrDirectByte(PARAM_2);// * V12_Y_MULTIPLIER; 1309 | uint16_t id = object_find(x, y); 1310 | 1311 | // if (obj == 0 && (_game.platform == Common::kPlatformNES) && (_userState & USERSTATE_IFACE_INVENTORY)) { 1312 | // if (_mouseOverBoxV2 >= 0 && _mouseOverBoxV2 < 4) 1313 | // obj = findInventory(VAR(VAR_EGO), _mouseOverBoxV2 + _inventoryOffset + 1); 1314 | // } 1315 | uint16_t res = 0; 1316 | if (id) 1317 | { 1318 | //DEBUG_PRINTF("Found object %d\n", id); 1319 | res = id; 1320 | } 1321 | else 1322 | { 1323 | y = y / (8 / V12_Y_MULTIPLIER) + SCREEN_TOP; 1324 | if (y >= INV_TOP) 1325 | res = inventory_checkXY(x, y); 1326 | } 1327 | setResult(res); 1328 | } 1329 | 1330 | static void op_walkActorToObject(void) 1331 | { 1332 | uint8_t actor = getVarOrDirectByte(PARAM_1); 1333 | uint16_t obj = getVarOrDirectWord(PARAM_2); 1334 | DEBUG_PRINTF("Actor %u walk to object %u\n", actor, obj); 1335 | if (object_whereIs(obj) != WIO_NOT_FOUND) { 1336 | actor_walkToObject(actor, obj); 1337 | } 1338 | } 1339 | 1340 | // Hardcoded to avoid file reads 1341 | static const char *preps[] = { 1342 | " ", " in", " with", " on", " to" 1343 | }; 1344 | 1345 | static uint8_t getPreposition(char *s) 1346 | { 1347 | // _sentenceBuf += (const char *)(getResourceAddress(rtCostume, 78) + VAR(VAR_SENTENCE_PREPOSITION) * 8 + 2); 1348 | // HROOM r = seekResource(&costumes[78]); 1349 | // seekFwd(r, scummVars[VAR_SENTENCE_PREPOSITION] * 8 + 2); 1350 | // uint8_t len = readString(r, s); 1351 | // closeRoom(r); 1352 | // return len; 1353 | return strcopy(s, preps[scummVars[VAR_SENTENCE_PREPOSITION]]); 1354 | } 1355 | 1356 | 1357 | static void op_drawSentence(void) 1358 | { 1359 | uint8_t slot = verb_getSlot(scummVars[VAR_SENTENCE_VERB], 0); 1360 | char *buf = message_new(); 1361 | char *s = buf; 1362 | 1363 | // if (!((_userState & USERSTATE_IFACE_SENTENCE) || 1364 | // (_game.platform == Common::kPlatformNES && (_userState & USERSTATE_IFACE_ALL)))) 1365 | // return; 1366 | 1367 | if (verbs[slot].verbid) 1368 | { 1369 | s += strcopy(s, verbs[slot].name); 1370 | } 1371 | else 1372 | { 1373 | return; 1374 | } 1375 | 1376 | if (scummVars[VAR_SENTENCE_OBJECT1] > 0) { 1377 | uint8_t len = getObjOrActorName(s + 1, scummVars[VAR_SENTENCE_OBJECT1]); 1378 | if (len) 1379 | { 1380 | *s++ = ' '; 1381 | s += len; 1382 | } 1383 | } 1384 | 1385 | if (0 < scummVars[VAR_SENTENCE_PREPOSITION] && scummVars[VAR_SENTENCE_PREPOSITION] <= 4) 1386 | { 1387 | uint8_t len = getPreposition(s); 1388 | s += len; 1389 | } 1390 | 1391 | if (scummVars[VAR_SENTENCE_OBJECT2] > 0) { 1392 | uint8_t len = getObjOrActorName(s + 1, scummVars[VAR_SENTENCE_OBJECT2]); 1393 | if (len) 1394 | { 1395 | *s++ = ' '; 1396 | s += len; 1397 | } 1398 | } 1399 | 1400 | *s = 0; 1401 | 1402 | // _string[2].charset = 1; 1403 | // _string[2].ypos = _virtscr[kVerbVirtScreen].topline; 1404 | // _string[2].xpos = 0; 1405 | // _string[2].right = _virtscr[kVerbVirtScreen].w - 1; 1406 | // if (_game.platform == Common::kPlatformNES) { 1407 | // _string[2].xpos = 16; 1408 | // _string[2].color = 0; 1409 | 1410 | // byte string[80]; 1411 | // const char *ptr = _sentenceBuf.c_str(); 1412 | // int i = 0, len = 0; 1413 | 1414 | // Maximum length of printable characters 1415 | // int maxChars = (_game.platform == Common::kPlatformNES) ? 60 : 40; 1416 | // while (*ptr) { 1417 | // if (*ptr != '@') 1418 | // len++; 1419 | // if (len > maxChars) { 1420 | // break; 1421 | // } 1422 | 1423 | // string[i++] = *ptr++; 1424 | 1425 | // if (_game.platform == Common::kPlatformNES && len == 30) { 1426 | // string[i++] = 0xFF; 1427 | // string[i++] = 8; 1428 | // } 1429 | // } 1430 | // string[i] = 0; 1431 | 1432 | // sentenceline.top = _virtscr[kVerbVirtScreen].topline; 1433 | // sentenceline.bottom = _virtscr[kVerbVirtScreen].topline + 16; 1434 | // sentenceline.left = 16; 1435 | // sentenceline.right = _virtscr[kVerbVirtScreen].w - 1; 1436 | 1437 | // restoreBackground(sentenceline); 1438 | 1439 | // TODO: special codes 1440 | DEBUG_PRINTF("Draw sentence '%s'\n", buf); 1441 | graphics_printSentence(buf); 1442 | } 1443 | 1444 | static void op_doSentence(void) 1445 | { 1446 | uint8_t verb; 1447 | 1448 | verb = getVarOrDirectByte(PARAM_1); 1449 | if (verb == 0xFC) { 1450 | sentenceNum = 0; 1451 | stopScript(SENTENCE_SCRIPT); 1452 | return; 1453 | } 1454 | if (verb == 0xFB) { 1455 | resetSentence(); 1456 | return; 1457 | } 1458 | 1459 | SentenceTab *st = &sentence[sentenceNum++]; 1460 | 1461 | st->verb = verb; 1462 | st->objectA = getVarOrDirectWord(PARAM_2); 1463 | st->objectB = getVarOrDirectWord(PARAM_3); 1464 | st->preposition = (st->objectB != 0); 1465 | st->freezeCount = 0; 1466 | 1467 | uint8_t opcode = fetchScriptByte(); 1468 | DEBUG_PRINTF("doSentence %d: %d %d %d\n", opcode, verb, st->objectA, st->objectB); 1469 | switch (opcode) { 1470 | case 0: 1471 | // Do nothing (besides setting up the sentence above) 1472 | break; 1473 | case 1: 1474 | // Execute the sentence 1475 | sentenceNum--; 1476 | 1477 | if (verb == 254) { 1478 | //ScummEngine::stopObjectScript(st->objectA); 1479 | } else { 1480 | // bool isBackgroundScript; 1481 | // bool isSpecialVerb; 1482 | if (verb != 253 && verb != 250) { 1483 | scummVars[VAR_ACTIVE_VERB] = verb; 1484 | scummVars[VAR_ACTIVE_OBJECT1] = st->objectA; 1485 | scummVars[VAR_ACTIVE_OBJECT2] = st->objectB; 1486 | 1487 | // isBackgroundScript = false; 1488 | // isSpecialVerb = false; 1489 | } else { 1490 | // isBackgroundScript = (st->verb == 250); 1491 | // isSpecialVerb = true; 1492 | verb = 253; 1493 | } 1494 | 1495 | // Check if an object script for this object is already running. If 1496 | // so, reuse its script slot. Note that we abuse two script flags: 1497 | // freezeResistant and recursive. We use them to track two 1498 | // script flags used in V1/V2 games. The main reason we do it this 1499 | // ugly evil way is to avoid having to introduce yet another save 1500 | // game revision. 1501 | // int slot = -1; 1502 | // ScriptSlot *ss; 1503 | // int i; 1504 | 1505 | // ss = vm.slot; 1506 | // for (i = 0; i < NUM_SCRIPT_SLOT; i++, ss++) { 1507 | // if (st->objectA == ss->number && 1508 | // ss->freezeResistant == isBackgroundScript && 1509 | // ss->recursive == isSpecialVerb && 1510 | // (ss->where == WIO_ROOM || ss->where == WIO_INVENTORY || ss->where == WIO_FLOBJECT)) { 1511 | // slot = i; 1512 | // break; 1513 | // } 1514 | // } 1515 | 1516 | // runObjectScript(st->objectA, st->verb, isBackgroundScript, isSpecialVerb, NULL, slot); 1517 | runObjectScript(st->objectA, st->verb); 1518 | } 1519 | break; 1520 | case 2: 1521 | // Print the sentence 1522 | sentenceNum--; 1523 | 1524 | scummVars[VAR_SENTENCE_VERB] = verb; 1525 | scummVars[VAR_SENTENCE_OBJECT1] = st->objectA; 1526 | scummVars[VAR_SENTENCE_OBJECT2] = st->objectB; 1527 | 1528 | op_drawSentence(); 1529 | break; 1530 | default: 1531 | DEBUG_PUTS("doSentence error\n"); 1532 | DEBUG_HALT; 1533 | } 1534 | } 1535 | 1536 | static void op_getObjPreposition(void) 1537 | { 1538 | getResultPos(); 1539 | uint16_t obj = getVarOrDirectWord(PARAM_1); 1540 | 1541 | if (object_whereIs(obj) != WIO_NOT_FOUND) 1542 | { 1543 | // byte *ptr = getOBCDFromObject(obj) + 12; 1544 | // setResult(*ptr >> 5); 1545 | setResult(object_getPreposition(obj)); 1546 | } 1547 | else 1548 | { 1549 | setResult(0xFF); 1550 | } 1551 | } 1552 | 1553 | static void op_getDist(void) 1554 | { 1555 | getResultPos(); 1556 | 1557 | uint16_t o1 = getVarOrDirectWord(PARAM_1); 1558 | uint16_t o2 = getVarOrDirectWord(PARAM_2); 1559 | 1560 | uint16_t r = getObjActToObjActDist(o1, o2); 1561 | 1562 | setResult(r); 1563 | } 1564 | 1565 | static void op_ifState01(void) 1566 | { 1567 | ifStateCommon(kObjectStatePickupable); 1568 | } 1569 | 1570 | static void op_ifState08(void) 1571 | { 1572 | ifStateCommon(kObjectState_08); 1573 | } 1574 | 1575 | typedef void (*OpcodeFunc)(void); 1576 | 1577 | static OpcodeFunc opcodes[0x100] = { 1578 | [0x00] = op_stopObjectCode, 1579 | [0x01] = op_putActor, 1580 | [0x02] = op_startMusic, 1581 | [0x03] = op_getActorRoom, 1582 | [0x04] = op_isGreaterEqual, 1583 | [0x05] = op_drawObject, 1584 | //[0x06] = op_getActorElevation, 1585 | [0x07] = op_setState08, 1586 | [0x08] = op_isNotEqual, 1587 | //[0x09] = op_faceActor, 1588 | //[0x0a] = op_assignVarWordIndirect, 1589 | //[0x0b] = op_setObjPreposition, 1590 | [0x0c] = op_resourceRoutines, 1591 | [0x0d] = op_walkActorToActor, 1592 | [0x0e] = op_putActorAtObject, 1593 | [0x0f] = op_ifNotState08, 1594 | [0x10] = op_getObjectOwner, 1595 | [0x11] = op_animateActor, 1596 | [0x12] = op_panCameraTo, 1597 | [0x13] = op_actorOps, 1598 | [0x14] = op_print, 1599 | [0x15] = op_actorFromPos, 1600 | [0x16] = op_getRandomNr, 1601 | [0x17] = op_clearState02, 1602 | [0x18] = op_jumpRelative, 1603 | [0x19] = op_doSentence, 1604 | [0x1a] = op_move, 1605 | [0x1b] = op_setBitVar, 1606 | [0x1c] = op_startSound, 1607 | //[0x1d] = op_ifClassOfIs, 1608 | [0x1e] = op_walkActorTo, 1609 | //[0x1f] = op_ifState02, 1610 | [0x20] = op_stopMusic, 1611 | [0x21] = op_putActor, 1612 | //[0x22] = op_saveLoadGame, 1613 | [0x23] = op_getActorY, 1614 | [0x24] = op_loadRoomWithEgo, 1615 | [0x25] = op_drawObject, 1616 | [0x26] = op_setVarRange, 1617 | [0x27] = op_setState04, 1618 | [0x28] = op_equalZero, 1619 | [0x29] = op_setOwnerOf, 1620 | //[0x2a] = op_addIndirect, 1621 | [0x2b] = op_delayVariable, 1622 | [0x2c] = op_assignVarByte, 1623 | [0x2d] = op_putActorInRoom, 1624 | [0x2e] = op_delay, 1625 | [0x2f] = op_ifNotState04, 1626 | //[0x30] = op_setBoxFlags, 1627 | [0x31] = op_getBitVar, 1628 | [0x32] = op_setCameraAt, 1629 | //[0x33] = op_roomOps, 1630 | [0x34] = op_getDist, 1631 | [0x35] = op_findObject, 1632 | [0x36] = op_walkActorToObject, 1633 | [0x37] = op_setState01, 1634 | [0x38] = op_isLessEqual, 1635 | [0x39] = op_doSentence, 1636 | [0x3a] = op_subtract, 1637 | [0x3b] = op_waitForActor, 1638 | [0x3c] = op_stopSound, 1639 | //[0x3d] = op_setActorElevation, 1640 | [0x3e] = op_walkActorTo, 1641 | //[0x3f] = op_ifNotState01, 1642 | [0x40] = op_cutscene, 1643 | [0x41] = op_putActor, 1644 | [0x42] = op_startScript, 1645 | [0x43] = op_getActorX, 1646 | [0x44] = op_isLess, 1647 | [0x45] = op_drawObject, 1648 | [0x46] = op_increment, 1649 | [0x47] = op_clearState08, 1650 | [0x48] = op_isEqual, 1651 | //[0x49] = op_faceActor, 1652 | [0x4a] = op_chainScript, 1653 | //[0x4b] = op_setObjPreposition, 1654 | //[0x4c] = op_waitForSentence, 1655 | [0x4d] = op_walkActorToActor, 1656 | [0x4e] = op_putActorAtObject, 1657 | [0x4f] = op_ifState08, 1658 | [0x50] = op_pickupObject, 1659 | [0x51] = op_animateActor, 1660 | [0x52] = op_actorFollowCamera, 1661 | [0x53] = op_actorOps, 1662 | //[0x54] = op_setObjectName, 1663 | [0x55] = op_actorFromPos, 1664 | //[0x56] = op_getActorMoving, 1665 | [0x57] = op_setState02, 1666 | [0x58] = op_beginOverride, 1667 | [0x59] = op_doSentence, 1668 | [0x5a] = op_add, 1669 | [0x5b] = op_setBitVar, 1670 | //[0x5c] = op_dummy, 1671 | //[0x5d] = op_ifClassOfIs, 1672 | [0x5e] = op_walkActorTo, 1673 | //[0x5f] = op_ifNotState02, 1674 | [0x60] = op_cursorCommand, 1675 | [0x61] = op_putActor, 1676 | [0x62] = op_stopScript, 1677 | //[0x63] = op_getActorFacing, 1678 | [0x64] = op_loadRoomWithEgo, 1679 | [0x65] = op_drawObject, 1680 | [0x66] = op_getClosestObjActor, 1681 | [0x67] = op_clearState04, 1682 | [0x68] = op_isScriptRunning, 1683 | [0x69] = op_setOwnerOf, 1684 | //[0x6a] = op_subIndirect, 1685 | //[0x6b] = op_dummy, 1686 | [0x6c] = op_getObjPreposition, 1687 | [0x6d] = op_putActorInRoom, 1688 | //[0x6e] = op_dummy, 1689 | [0x6f] = op_ifState04, 1690 | [0x70] = op_lights, 1691 | //[0x71] = op_getActorCostume, 1692 | [0x72] = op_loadRoom, 1693 | //[0x73] = op_roomOps, 1694 | [0x74] = op_getDist, 1695 | [0x75] = op_findObject, 1696 | [0x76] = op_walkActorToObject, 1697 | //[0x77] = op_clearState01, 1698 | [0x78] = op_isGreater, 1699 | [0x79] = op_doSentence, 1700 | [0x7a] = op_verbOps, 1701 | //[0x7b] = op_getActorWalkBox, 1702 | [0x7c] = op_isSoundRunning, 1703 | //[0x7d] = op_setActorElevation, 1704 | [0x7e] = op_walkActorTo, 1705 | [0x7f] = op_ifState01, 1706 | [0x80] = op_breakHere, 1707 | [0x81] = op_putActor, 1708 | [0x82] = op_startMusic, 1709 | [0x83] = op_getActorRoom, 1710 | [0x84] = op_isGreaterEqual, 1711 | [0x85] = op_drawObject, 1712 | //[0x86] = op_getActorElevation, 1713 | [0x87] = op_setState08, 1714 | [0x88] = op_isNotEqual, 1715 | //[0x89] = op_faceActor, 1716 | //[0x8a] = op_assignVarWordIndirect, 1717 | //[0x8b] = op_setObjPreposition, 1718 | [0x8c] = op_resourceRoutines, 1719 | [0x8d] = op_walkActorToActor, 1720 | [0x8e] = op_putActorAtObject, 1721 | [0x8f] = op_ifNotState08, 1722 | [0x90] = op_getObjectOwner, 1723 | [0x91] = op_animateActor, 1724 | [0x92] = op_panCameraTo, 1725 | [0x93] = op_actorOps, 1726 | [0x94] = op_print, 1727 | [0x95] = op_actorFromPos, 1728 | [0x96] = op_getRandomNr, 1729 | [0x97] = op_clearState02, 1730 | //[0x98] = op_restart, 1731 | [0x99] = op_doSentence, 1732 | [0x9a] = op_move, 1733 | [0x9b] = op_setBitVar, 1734 | [0x9c] = op_startSound, 1735 | //[0x9d] = op_ifClassOfIs, 1736 | [0x9e] = op_walkActorTo, 1737 | //[0x9f] = op_ifState02, 1738 | [0xa0] = op_stopObjectCode, 1739 | [0xa1] = op_putActor, 1740 | //[0xa2] = o4_saveLoadGame, 1741 | [0xa3] = op_getActorY, 1742 | [0xa4] = op_loadRoomWithEgo, 1743 | [0xa5] = op_drawObject, 1744 | [0xa6] = op_setVarRange, 1745 | [0xa7] = op_setState04, 1746 | [0xa8] = op_isNotEqualZero, 1747 | [0xa9] = op_setOwnerOf, 1748 | //[0xaa] = op_addIndirect, 1749 | [0xab] = op_switchCostumeSet, 1750 | [0xac] = op_drawSentence, 1751 | [0xad] = op_putActorInRoom, 1752 | [0xae] = op_waitForMessage, 1753 | [0xaf] = op_ifNotState04, 1754 | //[0xb0] = op_setBoxFlags, 1755 | [0xb1] = op_getBitVar, 1756 | [0xb2] = op_setCameraAt, 1757 | //[0xb3] = op_roomOps, 1758 | [0xb4] = op_getDist, 1759 | [0xb5] = op_findObject, 1760 | [0xb6] = op_walkActorToObject, 1761 | [0xb7] = op_setState01, 1762 | [0xb8] = op_isLessEqual, 1763 | [0xb9] = op_doSentence, 1764 | [0xba] = op_subtract, 1765 | [0xbb] = op_waitForActor, 1766 | [0xbc] = op_stopSound, 1767 | //[0xbd] = op_setActorElevation, 1768 | [0xbe] = op_walkActorTo, 1769 | //[0xbf] = op_ifNotState01, 1770 | [0xc0] = op_endCutscene, 1771 | [0xc1] = op_putActor, 1772 | [0xc2] = op_startScript, 1773 | [0xc3] = op_getActorX, 1774 | [0xc4] = op_isLess, 1775 | [0xc5] = op_drawObject, 1776 | //[0xc6] = op_decrement, 1777 | [0xc7] = op_clearState08, 1778 | [0xc8] = op_isEqual, 1779 | //[0xc9] = op_faceActor, 1780 | [0xca] = op_chainScript, 1781 | //[0xcb] = op_setObjPreposition, 1782 | //[0xcc] = op_pseudoRoom, 1783 | [0xcd] = op_walkActorToActor, 1784 | [0xce] = op_putActorAtObject, 1785 | [0xcf] = op_ifState08, 1786 | [0xd0] = op_pickupObject, 1787 | [0xd1] = op_animateActor, 1788 | [0xd2] = op_actorFollowCamera, 1789 | [0xd3] = op_actorOps, 1790 | //[0xd4] = op_setObjectName, 1791 | [0xd5] = op_actorFromPos, 1792 | //[0xd6] = op_getActorMoving, 1793 | [0xd7] = op_setState02, 1794 | [0xd8] = op_printEgo, 1795 | [0xd9] = op_doSentence, 1796 | [0xda] = op_add, 1797 | [0xdb] = op_setBitVar, 1798 | //[0xdc] = op_dummy, 1799 | //[0xdd] = op_ifClassOfIs, 1800 | [0xde] = op_walkActorTo, 1801 | //[0xdf] = op_ifNotState02, 1802 | [0xe0] = op_cursorCommand, 1803 | [0xe1] = op_putActor, 1804 | [0xe2] = op_stopScript, 1805 | //[0xe3] = op_getActorFacing, 1806 | [0xe4] = op_loadRoomWithEgo, 1807 | [0xe5] = op_drawObject, 1808 | [0xe6] = op_getClosestObjActor, 1809 | [0xe7] = op_clearState04, 1810 | [0xe8] = op_isScriptRunning, 1811 | [0xe9] = op_setOwnerOf, 1812 | //[0xea] = op_subIndirect, 1813 | //[0xeb] = op_dummy, 1814 | [0xec] = op_getObjPreposition, 1815 | [0xed] = op_putActorInRoom, 1816 | //[0xee] = op_dummy, 1817 | [0xef] = op_ifState04, 1818 | [0xf0] = op_lights, 1819 | //[0xf1] = op_getActorCostume, 1820 | [0xf2] = op_loadRoom, 1821 | //[0xf3] = op_roomOps, 1822 | [0xf4] = op_getDist, 1823 | [0xf5] = op_findObject, 1824 | [0xf6] = op_walkActorToObject, 1825 | [0xf7] = op_clearState01, 1826 | [0xf8] = op_isGreater, 1827 | [0xf9] = op_doSentence, 1828 | [0xfa] = op_verbOps, 1829 | //[0xfb] = op_getActorWalkBox, 1830 | [0xfc] = op_isSoundRunning, 1831 | //[0xfd] = op_setActorElevation, 1832 | [0xfe] = op_walkActorTo, 1833 | [0xff] = op_ifState01, 1834 | }; 1835 | 1836 | void executeScript(void) 1837 | { 1838 | if (script_delay) 1839 | { 1840 | //DEBUG_PRINTF("Delay %u\n", (uint16_t)delay); 1841 | // if (delay > 100) 1842 | // delay -= 100; 1843 | // else 1844 | // delay = 0; 1845 | --script_delay; 1846 | return; 1847 | } 1848 | while (!exitFlag) 1849 | { 1850 | opcode = fetchScriptByte(); 1851 | // if (script_id == 4 || script_id == 2) 1852 | // DEBUG_PRINTF("EXEC %u 0x%x op=%x\n", script_id, script_offset - 5, opcode); 1853 | OpcodeFunc f = opcodes[opcode]; 1854 | if (!f) 1855 | { 1856 | DEBUG_PRINTF("Unknown opcode %x\n", opcode); 1857 | DEBUG_HALT; 1858 | } 1859 | f(); 1860 | } 1861 | } 1862 | 1863 | void runScript(uint16_t s) 1864 | { 1865 | DEBUG_PRINTF("New script id %u\n", s); 1866 | pushScript(s, scripts[s].room, scripts[s].roomoffs, 4); 1867 | //executeScript(); 1868 | } 1869 | 1870 | void runObjectScript(uint16_t objId, uint8_t verb) 1871 | { 1872 | uint8_t offs = object_getVerbEntrypoint(objId, verb); 1873 | if (offs == 0) 1874 | return; 1875 | uint16_t obcd = object_getOBCDoffset(objId); 1876 | DEBUG_PRINTF("New object script %u for verb %u obcd=%d verbOffs=%d\n", 1877 | objId, verb, obcd, offs); 1878 | pushScript(objId, currentRoom, obcd + offs, 0); 1879 | } 1880 | 1881 | void processScript(void) 1882 | { 1883 | for (curScript = 0 ; curScript < MAX_SCRIPTS ; ++curScript) 1884 | { 1885 | switchScriptPage(curScript); 1886 | if (script_id && !(script_status & 0x7f)) 1887 | { 1888 | //DEBUG_PRINTF("Script %d\n", stack[curScript].id); 1889 | exitFlag = 0; 1890 | executeScript(); 1891 | } 1892 | } 1893 | } 1894 | 1895 | void runRoomScript(uint16_t id, uint8_t room, uint16_t offs) 1896 | { 1897 | pushScript(id, room, offs, 0); 1898 | } 1899 | 1900 | enum { 1901 | MBS_LEFT_CLICK = 0x8000, 1902 | MBS_RIGHT_CLICK = 0x4000, 1903 | MBS_MOUSE_MASK = (MBS_LEFT_CLICK | MBS_RIGHT_CLICK), 1904 | MBS_MAX_KEY = 0x0200 1905 | }; 1906 | 1907 | /** 1908 | * The area in which some click (or key press) occurred and which is passed 1909 | * to the input script. 1910 | */ 1911 | enum ClickArea { 1912 | kVerbClickArea = 1, 1913 | kSceneClickArea = 2, 1914 | kInventoryClickArea = 3, 1915 | kKeyClickArea = 4, 1916 | kSentenceClickArea = 5 1917 | }; 1918 | 1919 | void runInputScript(uint8_t clickArea, uint16_t val) 1920 | { 1921 | DEBUG_PRINTF("Run input script %d %d\n", clickArea, val); 1922 | scummVars[VAR_CLICK_AREA] = clickArea; 1923 | switch (clickArea) 1924 | { 1925 | case kVerbClickArea: // Verb clicked 1926 | scummVars[VAR_CLICK_VERB] = val; 1927 | break; 1928 | case kInventoryClickArea: // Inventory clicked 1929 | scummVars[VAR_CLICK_OBJECT] = val; 1930 | break; 1931 | default: 1932 | break; 1933 | } 1934 | 1935 | runScript(4); 1936 | } 1937 | 1938 | static void abortCutscene(void) 1939 | { 1940 | uint16_t offs = cutScenePtr; 1941 | if (offs) { 1942 | switchScriptPage(cutSceneScript); 1943 | script_offset = offs; 1944 | switchScriptPage(curScript); 1945 | 1946 | scummVars[VAR_OVERRIDE] = 1; 1947 | cutScenePtr = 0; 1948 | } 1949 | } 1950 | 1951 | __sfr __banked __at 0x7ffe IO_7FFE; 1952 | 1953 | void updateScummVars(void) 1954 | { 1955 | // updated in camera.c 1956 | //scummVars[VAR_CAMERA_POS_X] = cameraX >> V12_X_SHIFT; 1957 | 1958 | // We use shifts below instead of dividing by V12_X_MULTIPLIER resp. 1959 | // V12_Y_MULTIPLIER to handle negative coordinates correctly. 1960 | // This fixes e.g. bugs #1328131 and #1537595. 1961 | uint8_t cam = camera_getVirtScreenX(); 1962 | scummVars[VAR_VIRT_MOUSE_X] = cam + (cursorX >> V12_X_SHIFT); 1963 | int16_t y = cursorY - SCREEN_TOP * 8; 1964 | if (y < 0) 1965 | y = 0; 1966 | scummVars[VAR_VIRT_MOUSE_Y] = y >> V12_Y_SHIFT; 1967 | 1968 | // Adjust mouse coordinates as narrow rooms in NES are centered 1969 | // if (_game.platform == Common::kPlatformNES && _NESStartStrip > 0) { 1970 | if (roomWidth < SCREEN_WIDTH) 1971 | { 1972 | scummVars[VAR_VIRT_MOUSE_X] -= 2; 1973 | if (scummVars[VAR_VIRT_MOUSE_X] < 0) 1974 | scummVars[VAR_VIRT_MOUSE_X] = 0; 1975 | } 1976 | 1977 | // // 'B' is used to skip cutscenes in the NES version of Maniac Mansion 1978 | // } else if (_game.id == GID_MANIAC &&_game.platform == Common::kPlatformNES) { 1979 | // if (lastKeyHit.keycode == Common::KEYCODE_b && lastKeyHit.hasFlags(Common::KBD_SHIFT)) 1980 | // lastKeyHit = Common::KeyState(Common::KEYCODE_ESCAPE); 1981 | 1982 | // // On Alt-F5 prepare savegame for the original save/load dialog. 1983 | // if (lastKeyHit.keycode == Common::KEYCODE_F5 && lastKeyHit.hasFlags(Common::KBD_ALT)) { 1984 | // prepareSavegame(); 1985 | // if (_game.id == GID_MANIAC &&_game.platform == Common::kPlatformNES) { 1986 | // runScript(163, 0, 0, 0); 1987 | // } 1988 | // } 1989 | 1990 | // mouse and keyboard input 1991 | uint16_t s = 0; 1992 | if (clickDelay) 1993 | { 1994 | --clickDelay; 1995 | } 1996 | else 1997 | { 1998 | clickDelay = 5; 1999 | // space button 2000 | if (!(IO_7FFE & 1)) 2001 | { 2002 | s = MBS_LEFT_CLICK; 2003 | } 2004 | // M button to skip cutscene 2005 | else if (!(IO_7FFE & 4)) 2006 | { 2007 | abortCutscene(); 2008 | } 2009 | } 2010 | scummVars[VAR_KEYPRESS] = s; 2011 | 2012 | // if (VAR_KEYPRESS != 0xFF && _mouseAndKeyboardStat) { // Key Input 2013 | // if (315 <= _mouseAndKeyboardStat && _mouseAndKeyboardStat <= 323) { 2014 | // // Convert F-Keys for V1/V2 games (they start at 1) 2015 | // VAR(VAR_KEYPRESS) = _mouseAndKeyboardStat - 314; 2016 | // } else { 2017 | // VAR(VAR_KEYPRESS) = _mouseAndKeyboardStat; 2018 | // } 2019 | // } 2020 | } 2021 | 2022 | void checkExecVerbs(void) 2023 | { 2024 | uint16_t state = scummVars[VAR_KEYPRESS]; 2025 | //if (_userPut <= 0 || _mouseAndKeyboardStat == 0) 2026 | if (!state) 2027 | return; 2028 | 2029 | if (state < MBS_MAX_KEY) { 2030 | /* Check keypresses */ 2031 | // vs = &_verbs[1]; 2032 | // for (i = 1; i < _numVerbs; i++, vs++) { 2033 | // if (vs->verbid && vs->saveid == 0 && vs->curmode == 1) { 2034 | // if (_mouseAndKeyboardStat == vs->key) { 2035 | // // Trigger verb as if the user clicked it 2036 | // runInputScript(kVerbClickArea, vs->verbid, 1); 2037 | // return; 2038 | // } 2039 | // } 2040 | // } 2041 | 2042 | // Simulate inventory picking and scrolling keys 2043 | // int object = -1; 2044 | 2045 | // switch (_mouseAndKeyboardStat) { 2046 | // case 'u': // arrow up 2047 | // if (_inventoryOffset >= 2) { 2048 | // _inventoryOffset -= 2; 2049 | // redrawV2Inventory(); 2050 | // } 2051 | // return; 2052 | // case 'j': // arrow down 2053 | // if (_inventoryOffset + 4 < getInventoryCount(_scummVars[VAR_EGO])) { 2054 | // _inventoryOffset += 2; 2055 | // redrawV2Inventory(); 2056 | // } 2057 | // return; 2058 | // case 'i': // object 2059 | // object = 0; 2060 | // break; 2061 | // case 'o': 2062 | // object = 1; 2063 | // break; 2064 | // case 'k': 2065 | // object = 2; 2066 | // break; 2067 | // case 'l': 2068 | // object = 3; 2069 | // break; 2070 | // default: 2071 | // break; 2072 | // } 2073 | 2074 | // if (object != -1) { 2075 | // object = findInventory(_scummVars[VAR_EGO], object + 1 + _inventoryOffset); 2076 | // if (object > 0) 2077 | // runInputScript(kInventoryClickArea, object, 0); 2078 | // return; 2079 | // } 2080 | 2081 | // Generic keyboard input 2082 | //runInputScript(kKeyClickArea, _mouseAndKeyboardStat, 1); 2083 | } else if (state & MBS_MOUSE_MASK) { 2084 | //DEBUG_PRINTF("Clicked at %d %d\n", cursorX, cursorY); 2085 | uint8_t y = cursorY / 8; 2086 | uint8_t zone = graphics_findVirtScreen(y); 2087 | //const uint8_t code = state & MBS_LEFT_CLICK ? 1 : 2; 2088 | // const int inventoryArea = (_game.platform == Common::kPlatformNES) ? 48: 32; 2089 | 2090 | // This could be kUnkVirtScreen. 2091 | // Fixes bug #1536932: "MANIACNES: Crash on click in speechtext-area" 2092 | // if (!zone) 2093 | // return; 2094 | 2095 | uint8_t x = cursorX / 8; 2096 | // DEBUG_PRINTF("Clicked at %d %d %d virt %d %d\n", x, y, zone, 2097 | // scummVars[VAR_VIRT_MOUSE_X], scummVars[VAR_VIRT_MOUSE_Y]); 2098 | if (zone == kVerbVirtScreen/* && _mouse.y <= zone->topline + 8*/) 2099 | { 2100 | // // Click into V2 sentence line 2101 | // runInputScript(kSentenceClickArea, 0, 0); 2102 | uint8_t over = verb_findAtPos(x, y); 2103 | if (over != 0) { 2104 | //DEBUG_PRINTF("Clicked verb %s\n", verbs[over].name); 2105 | // Verb was clicked 2106 | runInputScript(kVerbClickArea, verbs[over].verbid); 2107 | } 2108 | if (y >= INV_TOP) 2109 | { 2110 | // Click into V2 inventory 2111 | // TODO: Some coordinate hack 2112 | x += LINE_GAP; 2113 | uint16_t object = inventory_checkXY(x, y); 2114 | // DEBUG_PRINTF("Clicked object %u\n", object); 2115 | if (object > 0) 2116 | { 2117 | runInputScript(kInventoryClickArea, object); 2118 | } 2119 | else 2120 | { 2121 | inventory_checkButtons(x, y); 2122 | } 2123 | } 2124 | } 2125 | else if (zone == kMainVirtScreen) 2126 | { 2127 | // Scene was clicked 2128 | runInputScript((zone == kMainVirtScreen) ? kSceneClickArea : kVerbClickArea, 0); 2129 | } 2130 | } 2131 | } 2132 | 2133 | static uint8_t isScriptInUse(uint8_t id) 2134 | { 2135 | uint8_t i = getScriptIndex(id); 2136 | return i != MAX_SCRIPTS; 2137 | } 2138 | 2139 | void checkAndRunSentenceScript(void) 2140 | { 2141 | if (isScriptInUse(2)) 2142 | return; 2143 | 2144 | if (!sentenceNum || sentence[sentenceNum - 1].freezeCount) 2145 | return; 2146 | 2147 | sentenceNum--; 2148 | SentenceTab *st = &sentence[sentenceNum]; 2149 | 2150 | if (st->preposition && st->objectB == st->objectA) 2151 | return; 2152 | 2153 | scummVars[VAR_ACTIVE_VERB] = st->verb; 2154 | scummVars[VAR_ACTIVE_OBJECT1] = st->objectA; 2155 | scummVars[VAR_ACTIVE_OBJECT2] = st->objectB; 2156 | scummVars[VAR_VERB_ALLOWED] = (0 != object_getVerbEntrypoint(st->objectA, st->verb)); 2157 | // DEBUG_PRINTF("Run script 2: verb=%d o1=%d o2=%d allow=%d\n", 2158 | // scummVars[VAR_ACTIVE_VERB], scummVars[VAR_ACTIVE_OBJECT1], 2159 | // scummVars[VAR_ACTIVE_OBJECT2], scummVars[VAR_VERB_ALLOWED]); 2160 | 2161 | runScript(2); 2162 | } 2163 | 2164 | void runInventoryScript(uint16_t i) 2165 | { 2166 | inventory_redraw(); 2167 | //void ScummEngine_v2::redrawV2Inventory() 2168 | 2169 | // not needed 2170 | // if (scummVars[VAR_INVENTORY_SCRIPT]) { 2171 | // // int args[NUM_SCRIPT_LOCAL]; 2172 | // // memset(args, 0, sizeof(args)); 2173 | // // args[0] = i; 2174 | // runScript(scummVars[VAR_INVENTORY_SCRIPT]/*, 0, 0, args*/); 2175 | // } 2176 | } 2177 | --------------------------------------------------------------------------------