/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "common/system.h" #include "common/debug.h" #include "chamber/chamber.h" #include "chamber/common.h" #include "chamber/script.h" #include "chamber/enums.h" #include "chamber/resdata.h" #include "chamber/cga.h" #include "chamber/cursor.h" #include "chamber/portrait.h" #include "chamber/input.h" #include "chamber/menu.h" #include "chamber/room.h" #include "chamber/dialog.h" #include "chamber/print.h" #include "chamber/anim.h" #include "chamber/invent.h" #include "chamber/sound.h" #include "chamber/savegame.h" #include "chamber/ifgm.h" #if 0 #define DEBUG_SCRIPT char DEBUG_SCRIPT_LOG[] = "!script.log"; #endif #include "chamber/scrvars.h" namespace Chamber { byte rand_seed; uint16 the_command; uint16 script_res; byte *script_ptr, *script_end_ptr; byte *script_stack[5 * 2]; byte **script_stack_ptr = script_stack; void *script_vars[kScrPools_MAX] = { &script_word_vars, &script_word_vars, &script_byte_vars, inventory_items, zones_data, pers_list, inventory_items, inventory_items + kItemZapstik1 - 1, pers_list }; extern void askDisk2(void); /* Get next random byte value */ byte getRand(void) { script_byte_vars.rand_value = aleat_data[++rand_seed]; return script_byte_vars.rand_value; } /* Get next random word value */ uint16 getRandW(void) { uint16 r = getRand() << 8; return r | getRand(); } uint16 Swap16(uint16 x) { return (x << 8) | (x >> 8); } /* Script handlers exit codes */ enum CommandStatus { ScriptContinue = 0, /*run next script command normally*/ ScriptRerun = 1, /*abort current script, execute new command*/ /*TODO: maybe define ScriptRestartGame to support game restart?*/ }; uint16 CMD_TRAP(void) { warning("CMD TRAP"); promptWait(); for (;;) ; return 0; } uint16 SCR_TRAP(void) { warning("SCR TRAP 0x%02X @ 0x%lX", *script_ptr, script_ptr - templ_data); promptWait(); for (;;) ; return 0; } /* Remove "not interested in that" tag from owned items */ void reclaimRefusedItems(void) { int16 i; for (i = 0; i < MAX_INV_ITEMS; i++) { if (inventory_items[i].flags == (ITEMFLG_OWNED | ITEMFLG_DONTWANT)) inventory_items[i].flags = ITEMFLG_OWNED; } } /* Trade with a fellow Aspirant (the one that offers to swap an item) */ uint16 SCR_1_AspirantItemTrade(void) { byte *old_script, *old_script_end = script_end_ptr; item_t *item1, *item2; script_ptr++; old_script = script_ptr; for (;;) { inv_bgcolor = 0xFF; openInventory(0xFE, ITEMFLG_OWNED); if (inv_count == 0) { the_command = 0xC1BC; runCommand(); break; } if (the_command == 0) { the_command = 0xC1C0; runCommand(); break; } the_command = 0x9140; if (aspirant_ptr->item == 0) break; item1 = &inventory_items[aspirant_ptr->item - 1]; /*aspirant's item*/ item2 = (item_t *)(script_vars[kScrPool3_CurrentItem]); /*our offer*/ if (item2->flags == (ITEMFLG_OWNED | ITEMFLG_DONTWANT) || item1->name == item2->name) { the_command = 0xC1C0; runCommand(); break; } if (item2->name == 109 /*SKULL*/ || item2->name == 132 /*ZAPSTIK*/ || item2->name == 108 /*DAGGER*/ || script_byte_vars.rand_value < 154) { /*accept*/ item2->flags = ITEMFLG_ASPIR; item1->flags = ITEMFLG_OWNED; aspirant_ptr->item = script_byte_vars.inv_item_index; switch (item2->name) { case 132: /*ZAPSTIK*/ script_byte_vars.zapstiks_owned--; the_command = 0xC04B; break; case 104: /*ROPE*/ the_command = 0xC1BA; break; case 107: /*GOBLET*/ the_command = 0xC1BB; break; default: /*STONE FLY*/ the_command = 0xC1B9; } runCommand(); break; } else { /*not interested*/ item2->flags = ITEMFLG_OWNED | ITEMFLG_DONTWANT; the_command = 0xC1BD; runCommand(); continue; } } reclaimRefusedItems(); script_ptr = old_script; script_end_ptr = old_script_end; return 0; } /* Trade with a rude/passive Aspirant (the one that says nasty words about you) */ uint16 SCR_2_RudeAspirantTrade(void) { byte *old_script, *old_script_end = script_end_ptr; item_t *item1, *item2; script_ptr++; old_script = script_ptr; the_command = 0x9099; /*WHAT DO YOU WANT TO EXCHANGE?*/ runCommand(); for (;;) { inv_bgcolor = 0xFF; openInventory(0xFE, ITEMFLG_OWNED); if (inv_count == 0) { the_command = 0xC1C5; runCommand(); break; } if (the_command == 0) break; the_command = 0x9140; /*NOTHING ON HIM*/ if (aspirant_ptr->item == 0) { runCommand(); break; } item1 = &inventory_items[aspirant_ptr->item - 1]; /*aspirant's item*/ item2 = (item_t *)(script_vars[kScrPool3_CurrentItem]); /*our offer*/ if (item2->flags == (ITEMFLG_OWNED | ITEMFLG_DONTWANT)) { the_command = 0xC1C0; runCommand(); break; } /*only trade for ROPE, LANTERN, STONE FLY, GOBLET*/ if (item1->name < 104 || item1->name >= 108) { runCommand(); break; } if ((item1->name != item2->name) && (item2->name == 109 /*SKULL*/ || item2->name == 132 /*ZAPSTIK*/ || item2->name == 108 /*DAGGER*/ || script_byte_vars.rand_value < 154)) { /*show confirmation menu*/ script_byte_vars.trade_accepted = 0; the_command = 0xC1C6; runCommand(); if (script_byte_vars.trade_accepted == 0) break; item2->flags = ITEMFLG_ASPIR; item1->flags = ITEMFLG_OWNED; aspirant_ptr->item = script_byte_vars.inv_item_index; switch (item2->name) { case 132: /*ZAPSTIK*/ script_byte_vars.zapstiks_owned--; the_command = 0xC04B; break; case 104: /*ROPE*/ the_command = 0xC1BA; break; case 107: /*GOBLET*/ the_command = 0xC1BB; break; default: /*STONE FLY*/ the_command = 0xC1B9; } runCommand(); break; } else { /*not interested*/ item2->flags = ITEMFLG_OWNED | ITEMFLG_DONTWANT; the_command = 0xC1BD; runCommand(); continue; } } reclaimRefusedItems(); script_ptr = old_script; script_end_ptr = old_script_end; return 0; } /* Steal a Zapstik form Protozorq */ uint16 SCR_4_StealZapstik(void) { byte *old_script; pers_t *pers = (pers_t *)(script_vars[kScrPool8_CurrentPers]); script_ptr++; old_script = script_ptr; if ((pers->index & ~7) != 0x30) { the_command = 0x9148; /*YOU`VE ALREADY GOT IT*/ runCommand(); } else { pers->index &= ~0x18; script_vars[kScrPool3_CurrentItem] = &inventory_items[kItemZapstik1 - 1 + (script_byte_vars.cur_pers - 1) - kPersProtozorq1]; script_byte_vars.steals_count++; bounceCurrentItem(ITEMFLG_OWNED, 85); /*bounce to inventory*/ the_command = 0x9147; /*YOU GET HIS ZAPSTIK*/ if (script_byte_vars.zapstik_stolen == 0) { runCommand(); script_byte_vars.zapstik_stolen = 1; the_command = 0x9032; /*THIS SHOULD GIVE YOU THE EDGE IN MOST COMBATS!*/ } runCommand(); } script_ptr = old_script; return 0; } byte wait_delta = 0; /* Wait for a specified number of seconds (real time) or a keypress */ void wait(byte seconds) { warning("STUB: Wait(%d)", seconds); #if 0 struct time t; uint16 endtime; seconds += wait_delta; if (seconds > 127) /*TODO: is this a check for a negative value?*/ seconds = 0; gettime(&t); endtime = t.ti_sec * 100 + t.ti_hund + seconds * 100; while (buttons == 0) { uint16 current; gettime(&t); current = t.ti_sec * 100 + t.ti_hund; if (endtime >= 6000 && current < 2048) /*TODO: some kind of overflow check???*/ current += 6000; if (current >= endtime) break; } #endif } /* Wait for a 4 seconds or a keypress */ uint16 SCR_2C_Wait4(void) { script_ptr++; wait(4); return 0; } /* Wait for a specified number of seconds or a keypress TODO: Always waits for a 4 seconds due to a bug? */ uint16 SCR_2D_Wait(void) { byte seconds; script_ptr++; seconds = *script_ptr++; (void)seconds; wait(4); /*TODO: looks like a bug?*/ return 0; } /* Show blinking prompt indicator and wait for a keypress */ uint16 SCR_2E_promptWait(void) { script_ptr++; promptWait(); return 0; } #define VARTYPE_VAR 0x80 #define VARTYPE_BLOCK 0x40 #define VARTYPE_WORD 0x20 #define VARTYPE_KIND 0x1F #define VARSIZE_BYTE 0 #define VARSIZE_WORD 1 byte var_size; /* Fetch variable's value and address */ uint16 loadVar(byte **ptr, byte **varptr) { byte vartype; byte *varbase; uint16 value = 0; var_size = VARSIZE_BYTE; vartype = *((*ptr)++); if (vartype & VARTYPE_VAR) { /*variable*/ byte varoffs; varbase = (byte *)script_vars[vartype & VARTYPE_KIND]; if (vartype & VARTYPE_BLOCK) { byte *end; byte index = *((*ptr)++); varbase = seekToEntryW(varbase, index, &end); } varoffs = *((*ptr)++); #if 1 { int16 maxoffs = 0; switch (vartype & VARTYPE_KIND) { case kScrPool0_WordVars0: case kScrPool1_WordVars1: maxoffs = sizeof(script_word_vars); break; case kScrPool2_ByteVars: maxoffs = sizeof(script_byte_vars); break; case kScrPool3_CurrentItem: maxoffs = sizeof(item_t); break; case kScrPool4_ZoneSpots: maxoffs = RES_ZONES_MAX; break; case kScrPool5_Persons: maxoffs = sizeof(pers_list); break; case kScrPool6_Inventory: maxoffs = sizeof(inventory_items); break; case kScrPool7_Zapstiks: maxoffs = sizeof(inventory_items) - sizeof(item_t) * (kItemZapstik1 - 1); break; case kScrPool8_CurrentPers: maxoffs = sizeof(pers_t); break; } if (varoffs >= maxoffs) { warning("Scr var out of bounds @ %X (pool %d, ofs 0x%X, max 0x%X)", (uint16)(script_ptr - templ_data), vartype & VARTYPE_KIND, varoffs, maxoffs); promptWait(); } } #endif value = varbase[varoffs]; if (vartype & VARTYPE_WORD) { value = (value << 8) | varbase[varoffs + 1]; var_size = VARSIZE_WORD; } *varptr = &varbase[varoffs]; #if 0 /*TODO: debug stuff, remove me*/ if (varoffs == 0x48) warning("Var 2.%X = %X", varoffs, value); #endif } else { /*immediate value*/ value = *((*ptr)++); if (vartype & VARTYPE_WORD) { value = (value << 8) | *((*ptr)++); var_size = VARSIZE_WORD; } *varptr = 0; } return value; } #define MATHOP_END 0x80 #define MATHOP_CMP 0x40 #define MATHOP_ADD 0x20 #define MATHOP_SUB 0x10 #define MATHOP_AND 0x08 #define MATHOP_OR 0x04 #define MATHOP_XOR 0x02 #define MATHOP_EQ 0x20 #define MATHOP_B 0x10 #define MATHOP_A 0x08 #define MATHOP_NEQ 0x04 #define MATHOP_LE 0x02 #define MATHOP_GE 0x01 /* Perform math/logic operation on two operands */ uint16 mathOp(byte op, uint16 op1, uint16 op2) { if (op & MATHOP_CMP) { if (op & MATHOP_EQ) if (op1 == op2) return 0xffff; if (op & MATHOP_B) if (op1 < op2) return 0xffff; if (op & MATHOP_A) if (op1 > op2) return 0xffff; if (op & MATHOP_NEQ) if (op1 != op2) return 0xffff; if (op & MATHOP_LE) if ((int16)op1 <= (int16)op2) return 0xffff; if (op & MATHOP_GE) if ((int16)op1 >= (int16)op2) return 0xffff; return 0; } else { if (op & MATHOP_ADD) op1 += op2; if (op & MATHOP_SUB) op1 -= op2; if (op & MATHOP_AND) op1 &= op2; if (op & MATHOP_OR) op1 |= op2; if (op & MATHOP_XOR) op1 ^= op2; return op1; } } /* Evaluate an expression */ uint16 mathExpr(byte **ptr) { byte op; uint16 op1, op2; byte *opptr; op1 = loadVar(ptr, &opptr); while (((op = *((*ptr)++)) & MATHOP_END) == 0) { op2 = loadVar(ptr, &opptr); op1 = mathOp(op, op1, op2); } return op1; } /* Evaluate an expression and assign result to a variable */ uint16 SCR_3B_MathExpr(void) { uint16 op1, op2; byte *opptr; script_ptr++; /*get result variable pointer*/ op1 = loadVar(&script_ptr, &opptr); /*evaluate*/ op2 = mathExpr(&script_ptr); /*store result*/ /*TODO: original bug? MathExpr may overwrite global var_size, so mixed-size expressions will produce errorneous results*/ if (var_size == VARSIZE_BYTE) *opptr = op2 & 255; else { opptr[0] = op2 >> 8; /*store in big-endian*/ opptr[1] = op2 & 255; } (void)op1; /*return op1;*/ /*previous value, never used?*/ return 0; } /* Discard current callchain (the real one) and execute command */ uint16 SCR_4D_PriorityCommand(void) { script_ptr++; the_command = *script_ptr++; /*little-endian*/ the_command |= (*script_ptr++) << 8; the_command |= 0xF000; g_vm->_prioritycommand_1 = true; return ScriptRerun; } /* Jump to routine */ uint16 SCR_12_Chain(void) { script_ptr++; the_command = *script_ptr++; /*little-endian*/ the_command |= (*script_ptr++) << 8; script_ptr = getScriptSubroutine(the_command - 1); return 0; } /* Absolute jump Jumping past current routine ends the script */ uint16 SCR_33_Jump(void) { uint16 offs; script_ptr++; offs = *script_ptr++; /*little-endian*/ offs |= (*script_ptr++) << 8; script_ptr = templ_data + offs; return 0; } /* Conditional jump (IF/ELSE block) */ uint16 SCR_3C_CondExpr(void) { script_ptr++; if (mathExpr(&script_ptr)) { /*fall to IF block*/ script_ptr += 2; } else { /*branch to ELSE block*/ script_ptr -= 1; /*simulate opcode byte for Jump handler*/ return SCR_33_Jump(); } return 0; } /* Absolute subroutine call */ uint16 SCR_34_Call(void) { uint16 offs; script_ptr++; offs = *script_ptr++; /*little-endian*/ offs |= (*script_ptr++) << 8; *script_stack_ptr++ = script_ptr; *script_stack_ptr++ = script_end_ptr; script_ptr = templ_data + offs; return 0; } /* Return from script subroutine */ uint16 SCR_35_Ret(void) { script_end_ptr = *(--script_stack_ptr); script_ptr = *(--script_stack_ptr); return 0; } /* Draw portrait, pushing it from left to right */ uint16 SCR_5_DrawPortraitLiftRight(void) { byte x, y, width, height; script_ptr++; if (!drawPortrait(&script_ptr, &x, &y, &width, &height)) return 0; /*TODO: use local args instead of globals*/ cga_AnimLiftToRight(width, cur_image_pixels + width - 1, width, 1, height, CGA_SCREENBUFFER, CalcXY_p(x, y)); return 0; } /* Draw portrait, pushing it from right to left */ uint16 SCR_6_DrawPortraitLiftLeft(void) { byte x, y, width, height; script_ptr++; if (!drawPortrait(&script_ptr, &x, &y, &width, &height)) return 0; /*TODO: use local args instead of globals*/ cga_AnimLiftToLeft(width, cur_image_pixels, width, 1, height, CGA_SCREENBUFFER, CalcXY_p(x + width - 1, y)); return 0; } /* Draw portrait, pushing it from top to bottom */ uint16 SCR_7_DrawPortraitLiftDown(void) { byte x, y, width, height; script_ptr++; if (!drawPortrait(&script_ptr, &x, &y, &width, &height)) return 0; /*TODO: use local args instead of globals*/ cga_AnimLiftToDown(cur_image_pixels, cur_image_size_w, cur_image_size_w, cur_image_size_h, CGA_SCREENBUFFER, cur_image_offs); return 0; } /* Draw portrait, pushing it from bottom to top */ uint16 SCR_8_DrawPortraitLiftUp(void) { byte x, y, width, height; script_ptr++; if (!drawPortrait(&script_ptr, &x, &y, &width, &height)) return 0; /*TODO: use local args instead of globals*/ cga_AnimLiftToUp(cur_image_pixels, cur_image_size_w, cur_image_size_w, cur_image_size_h, CGA_SCREENBUFFER, x, y + height - 1); return 0; } /* Draw portrait, no special effects */ uint16 SCR_9_DrawPortrait(void) { byte x, y, width, height; script_ptr++; if (!drawPortrait(&script_ptr, &x, &y, &width, &height)) return 0; cga_BlitAndWait(cur_image_pixels, cur_image_size_w, cur_image_size_w, cur_image_size_h, CGA_SCREENBUFFER, cur_image_offs); return 0; } /* Draw portrait, no special effects */ uint16 SCR_A_DrawPortrait(void) { return SCR_9_DrawPortrait(); } /* Draw screen pixels using 2-phase clockwise twist */ void twistDraw(byte x, byte y, byte width, byte height, byte *source, byte *target) { int16 i; uint16 sx, ex, sy, ey, t; sx = x * 4; ex = x * 4 + width * 4 - 1; sy = y; ey = y + height - 1; for (i = 0; i < width * 4; i++) { cga_TraceLine(sx, ex, sy, ey, source, target); waitVBlank(); sx += 1; ex -= 1; } t = sx; sx = ex + 1; ex = t - 1; t = sy; sy = ey; ey = t; for (i = 0; i < height; i++) { cga_TraceLine(sx, ex, sy, ey, source, target); waitVBlank(); sy -= 1; ey += 1; } } /* Draw image with twist-effect */ uint16 SCR_B_DrawPortraitTwistEffect(void) { byte x, y, width, height; uint16 offs; script_ptr++; if (!drawPortrait(&script_ptr, &x, &y, &width, &height)) return 0; offs = CalcXY_p(x, y); cga_SwapScreenRect(cur_image_pixels, width, height, backbuffer, offs); twistDraw(x, y, width, height, backbuffer, frontbuffer); cga_BlitAndWait(scratch_mem2, width, width, height, backbuffer, offs); return 0; } /* Draw screen pixels using arc-like sweep */ void arcDraw(byte x, byte y, byte width, byte height, byte *source, byte *target) { int16 i; uint16 sx, ex, sy, ey; sx = x * 4; ex = x * 4 + width * 2 - 1; sy = y + height - 1; ey = y + height - 1; for (i = 0; i < height; i++) { cga_TraceLine(sx, ex, sy, ey, source, target); waitVBlank(); sy -= 1; } for (i = 0; i < width * 4; i++) { cga_TraceLine(sx, ex, sy, ey, source, target); waitVBlank(); sx += 1; } for (i = 0; i < height + 1; i++) { cga_TraceLine(sx, ex, sy, ey, source, target); waitVBlank(); sy += 1; } } /* Draw image with arc-effect */ uint16 SCR_C_DrawPortraitArcEffect(void) { byte x, y, width, height; uint16 offs; script_ptr++; if (!drawPortrait(&script_ptr, &x, &y, &width, &height)) return 0; offs = CalcXY_p(x, y); cga_SwapScreenRect(cur_image_pixels, width, height, backbuffer, offs); arcDraw(x, y, width, height, backbuffer, frontbuffer); cga_BlitAndWait(scratch_mem2, width, width, height, backbuffer, offs); return 0; } /* Draw image with slow top-to-down reveal effect by repeatedly draw its every 17th pixel */ uint16 SCR_D_DrawPortraitDotEffect(void) { //int16 i; byte x, y, width, height; uint16 offs, step = 17; byte *target = CGA_SCREENBUFFER; script_ptr++; if (!drawPortrait(&script_ptr, &x, &y, &width, &height)) return 0; cur_image_end = width * height; int16 count = 0; if (g_vm->_videoMode == Common::RenderMode::kRenderHercG) { const int START_X = (HGA_WIDTH / 8 - (2 * CGA_WIDTH) / 8) / 2; const int START_Y = (HGA_HEIGHT - CGA_HEIGHT) / 2; x += START_X; y += START_Y; } for (offs = 0; offs != cur_image_end;) { target[CalcXY_p(x + offs % cur_image_size_w, y + offs / cur_image_size_w)] = cur_image_pixels[offs]; // TODO check this if (count % 5 == 0) cga_blitToScreen(offs, g_vm->_screenPPB, 1); offs += step; if (offs > cur_image_end) offs -= cur_image_end; count++; } return 0; } /* Draw image with slow zoom-in reveal effect */ uint16 SCR_E_DrawPortraitZoomIn(void) { byte x, y, width, height; script_ptr++; if (!drawPortrait(&script_ptr, &x, &y, &width, &height)) return 0; cga_AnimZoomIn(cur_image_pixels, cur_image_size_w, cur_image_size_h, frontbuffer, cur_image_offs); return 0; } uint16 drawPortraitZoomed(byte **params) { byte x, y, width, height; byte zwidth, zheight; right_button = 0; /*prevent cancel or zoom parameters won't be consumed*/ if (!drawPortrait(params, &x, &y, &width, &height)) return 0; /*TODO: maybe just remove the if/return instead?*/ zwidth = *((*params)++); zheight = *((*params)++); /*adjust the rect for new size*/ last_dirty_rect->width = zwidth + 2; last_dirty_rect->height = zheight; cga_ZoomImage(cur_image_pixels, cur_image_size_w, cur_image_size_h, zwidth, zheight, frontbuffer, cur_image_offs); return 0; } /* Draw image with specified w/h zoom */ uint16 SCR_10_DrawPortraitZoomed(void) { script_ptr++; drawPortraitZoomed(&script_ptr); #if 0 /*TODO: debug wait*/ promptWait(); #endif return 0; } /* Hide portrait, pushing it from right to left */ uint16 SCR_19_HidePortraitLiftLeft(void) { byte index; byte kind; byte x, y; byte width, height; uint16 offs; script_ptr++; index = *script_ptr++; getDirtyRectAndFree(index, &kind, &x, &y, &width, &height, &offs); if (right_button) { cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs); return 0; } /*TODO: This originally was done by reusing door sliding routine*/ /*offs = CalcXY_p(x + 1, y);*/ offs++; while (--width) { cga_HideScreenBlockLiftToLeft(1, CGA_SCREENBUFFER, backbuffer, width, height, CGA_SCREENBUFFER, offs); } offs--; /*hide leftmost line*/ /*TODO: move this to CGA?*/ uint16 ooffs = offs; byte oh = height; while (height--) { memcpy(frontbuffer + offs, backbuffer + offs, 1); offs ^= g_vm->_line_offset; if ((offs & g_vm->_line_offset) == 0) offs += g_vm->_screenBPL; } cga_blitToScreen(ooffs, 1, oh); return 0; } /* Hide portrait, pushing it from left to right */ uint16 SCR_1A_HidePortraitLiftRight(void) { byte index; byte kind; byte x, y; byte width, height; uint16 offs; script_ptr++; index = *script_ptr++; getDirtyRectAndFree(index, &kind, &x, &y, &width, &height, &offs); if (right_button) { cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs); return 0; } /*TODO: This originally was done by reusing door sliding routine*/ offs = CalcXY_p(x + width - 2, y); while (--width) { cga_HideScreenBlockLiftToRight(1, CGA_SCREENBUFFER, backbuffer, width, height, CGA_SCREENBUFFER, offs); } offs++; /*hide leftmost line*/ /*TODO: move this to CGA?*/ uint16 ooffs = offs; byte oh = height; while (height--) { memcpy(frontbuffer + offs, backbuffer + offs, 1); offs ^= g_vm->_line_offset; if ((offs & g_vm->_line_offset) == 0) offs += g_vm->_screenBPL; } cga_blitToScreen(ooffs, 1, oh); return 0; } /* Hide portrait, pushing it from bottom to top */ uint16 SCR_1B_HidePortraitLiftUp(void) { byte index; byte kind; byte x, y; byte width, height; uint16 offs; script_ptr++; index = *script_ptr++; getDirtyRectAndFree(index, &kind, &x, &y, &width, &height, &offs); if (right_button) { cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs); return 0; } offs = CalcXY_p(x, y + 1); while (--height) { cga_HideScreenBlockLiftToUp(1, CGA_SCREENBUFFER, backbuffer, width, height, CGA_SCREENBUFFER, offs); } /*hide topmost line*/ /*TODO: move this to CGA?*/ offs ^= g_vm->_line_offset; if ((offs & g_vm->_line_offset) != 0) offs -= g_vm->_screenBPL; memcpy(CGA_SCREENBUFFER + offs, backbuffer + offs, width); cga_blitToScreen(offs, width, 1); return 0; } /* Hide portrait, pushing it from top to bottom */ uint16 SCR_1C_HidePortraitLiftDown(void) { byte index; byte kind; byte x, y; byte width, height; uint16 offs; script_ptr++; index = *script_ptr++; getDirtyRectAndFree(index, &kind, &x, &y, &width, &height, &offs); if (right_button) { cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs); return 0; } offs = CalcXY_p(x, y + height - 2); while (--height) { cga_HideScreenBlockLiftToDown(1, CGA_SCREENBUFFER, backbuffer, width, height, CGA_SCREENBUFFER, offs); } /*hide bottommost line*/ /*TODO: move this to CGA?*/ offs ^= g_vm->_line_offset; if ((offs & g_vm->_line_offset) == 0) offs += g_vm->_screenBPL; memcpy(CGA_SCREENBUFFER + offs, backbuffer + offs, width); cga_blitToScreen(offs, width, 1); return 0; } /* Hide portrait with twist effect */ uint16 SCR_1E_HidePortraitTwist(void) { byte index; byte kind; byte x, y; byte width, height; uint16 offs; script_ptr++; index = *script_ptr++; getDirtyRectAndFree(index, &kind, &x, &y, &width, &height, &offs); if (right_button) { cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs); return 0; } twistDraw(x, y, width, height, backbuffer, frontbuffer); return 0; } /* Hide portrait with arc effect */ uint16 SCR_1F_HidePortraitArc(void) { byte index; byte kind; byte x, y; byte width, height; uint16 offs; script_ptr++; index = *script_ptr++; getDirtyRectAndFree(index, &kind, &x, &y, &width, &height, &offs); if (right_button) { cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs); return 0; } arcDraw(x, y, width, height, backbuffer, frontbuffer); return 0; } /* Hide portrait with dots effect */ uint16 SCR_20_HidePortraitDots(void) { byte index; byte kind; byte x, y; byte width, height; uint16 offs; script_ptr++; index = *script_ptr++; getDirtyRectAndFree(index, &kind, &x, &y, &width, &height, &offs); if (right_button) { cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs); return 0; } dot_effect_step = 17; dot_effect_delay = 100; copyScreenBlockWithDotEffect(backbuffer, x, y, width, height, frontbuffer); return 0; } /* Play room's door open animation */ uint16 SCR_39_AnimRoomDoorOpen(void) { byte door; script_ptr++; door = *script_ptr++; animRoomDoorOpen(door); return 0; } /* Play room's door close animation */ uint16 SCR_3A_AnimRoomDoorClose(void) { byte door; script_ptr++; door = *script_ptr++; animRoomDoorClose(door); return 0; } uint16 SCR_25_ChangeZoneOnly(void) { byte index; byte old = script_byte_vars.zone_room; script_ptr++; index = *script_ptr++; changeZone(index); script_byte_vars.zone_room = old; return 0; } #define JCOUNT 16 typedef struct jpoint_t { signed short x; signed short y; } jpoint_t; static jpoint_t jdeltas[JCOUNT] = { {0, -2}, {1, -2}, {2, -2}, {2, -1}, {2, 0}, {2, 1}, {2, 2}, {1, 2}, {0, 2}, { -1, 2}, { -2, 2}, { -2, 1}, { -2, 0}, { -2, -1}, { -2, -2}, { -1, -2} }; /* Play exploding zoom animation */ void jaggedZoom(byte *source, byte *target) { int16 i; jpoint_t points[JCOUNT + 1]; uint16 outside = 0; uint16 cycle = 0; uint16 choices = 0; for (i = 0; i < JCOUNT; i++) { points[i].x = 320; points[i].y = 200; } points[i].x = 0; points[i].y = 0; for (;;) { cycle++; if (cycle % 8 == 0) choices = getRandW(); for (i = 0; i < JCOUNT; i++) { signed short t; if (choices & (1 << i)) { t = points[i].x + jdeltas[i].x; if (t < 0 || t >= 600) { /*TODO: 640?*/ outside |= 0x8000; /*TODO: should this mask be rotated?*/ t = points[i].x; } points[i].x = t; t = points[i].y + jdeltas[i].y; if (t < 0 || t >= 400) { outside |= 0x8000; t = points[i].y; } points[i].y = t; } } if (outside) break; for (i = 0; i < JCOUNT; i++) { uint16 sx = points[i].x; uint16 sy = points[i].y; uint16 ex = points[i + 1].x; uint16 ey = points[i + 1].y; if (ex == 0 && ey == 0) { ex = points[0].x; ey = points[0].y; } cga_TraceLine(sx / 2, ex / 2, sy / 2, ey / 2, source, target); /*TODO: waitVBlank(); maybe?*/ } } } typedef struct star_t { uint16 ofs; byte pixel; byte mask; signed short x; signed short y; uint16 z; } star_t; /* Generate random star */ void randomStar(star_t *star) { star->x = getRandW(); star->y = getRandW(); star->z = getRandW() & 0xFFF; } /* Generate a bunch of random stars */ star_t *initStarfield(void) { int16 i; star_t *stars = (star_t *)scratch_mem2; for (i = 0; i < 300; i++) { stars[i].ofs = 0; stars[i].pixel = 0; stars[i].mask = 0; randomStar(&stars[i]); } return stars; } /* Draw a frame of starfield animation and update stars */ void drawStars(star_t *stars, int16 iter, byte *target) { int16 i; /*TODO: bug? initialized 300 stars, but animated only 256?*/ for (i = 0; i < 256; i++, stars++) { short z, x, y; byte pixel, mask; target[stars->ofs] &= stars->mask; if (stars->z < 328) { if (iter >= 30) { randomStar(stars); stars->z |= 0x1800; } continue; } stars->z -= 328; z = 0xCFFFFul / (stars->z + 16); x = ((long)z * stars->x) >> 16; y = ((long)z * stars->y) >> 16; x += 320 / 2; y += 200 / 2; if (x < 0 || x >= 320 || y < 0 || y >= 200) { stars->z = 0; continue; } stars->ofs = cga_CalcXY(x, y); pixel = (stars->z < 0xE00) ? 0xC0 : 0x40; pixel >>= (x % 4) * 2; mask = 0xC0; mask = ~(mask >> (x % 4) * 2); stars->pixel = pixel; stars->mask = mask; target[stars->ofs] &= mask; target[stars->ofs] |= pixel; } } /* Play starfield animation */ void animStarfield(star_t *stars, byte *target) { int16 i; for (i = 100; i; i--) drawStars(stars, i, target); } /* Play Game Over sequence and restart the game */ uint16 SCR_26_GameOver(void) { IFGM_PlaySample(160); in_de_profundis = 0; script_byte_vars.game_paused = 1; memset(backbuffer, 0, sizeof(backbuffer) - 2); /*TODO: original bug?*/ jaggedZoom(backbuffer, frontbuffer); cga_BackBufferToRealFull(); cga_ColorSelect(0x30); animStarfield(initStarfield(), frontbuffer); playAnim(44, 156 / 4, 95); script_byte_vars.zone_index = 135; /*reload background*/ while (!loadFond()) askDisk2(); jaggedZoom(backbuffer, frontbuffer); cga_BackBufferToRealFull(); restartGame(); return 0; } /* Draw all active room's persons */ uint16 SCR_4C_DrawPersons(void) { script_ptr++; drawPersons(); return 0; } /* Redraw all room's static objects */ uint16 SCR_13_RedrawRoomStatics(void) { byte index; script_ptr++; index = *script_ptr++; redrawRoomStatics(index, 0); return 0; } /* Go to a new zone If go through a door, play door's opening animation */ uint16 SCR_42_LoadZone(void) { byte index; script_ptr++; index = *script_ptr++; skip_zone_transition = 0; if (right_button) script_byte_vars.last_door = 0; else { if ((script_byte_vars.cur_spot_flags & (SPOTFLG_20 | SPOTFLG_10 | SPOTFLG_8)) == 0) script_byte_vars.last_door = script_byte_vars.cur_spot_flags & 7; else if ((script_byte_vars.cur_spot_flags & ((SPOTFLG_20 | SPOTFLG_10 | SPOTFLG_8))) == SPOTFLG_8) { skip_zone_transition = 1; animRoomDoorOpen(script_byte_vars.cur_spot_idx); script_byte_vars.last_door = script_byte_vars.cur_spot_flags & 7; } else script_byte_vars.last_door = 0; } beforeChangeZone(index); changeZone(index); script_byte_vars.zone_area_copy = script_byte_vars.zone_area; script_byte_vars.cur_spot_idx = findInitialSpot(); skip_zone_transition |= script_byte_vars.cur_spot_idx; drawRoomStatics(); if (script_byte_vars.bvar_5F != 0) { redrawRoomStatics(script_byte_vars.bvar_5F, 0); script_byte_vars.bvar_5F = 0; } backupSpotsImages(); prepareVorts(); prepareTurkey(); prepareAspirant(); drawPersons(); script_byte_vars.cur_spot_flags = 0; return 0; } /* Draw current sprites */ uint16 SCR_59_blitSpritesToBackBuffer(void) { script_ptr++; blitSpritesToBackBuffer(); return 0; } /* Apply current palette */ uint16 SCR_5A_SelectPalette(void) { script_ptr++; selectPalette(); return 0; } /* Apply specific palette */ uint16 SCR_5E_SelectTempPalette(void) { byte index; script_ptr++; index = *script_ptr++; selectSpecificPalette(index); return 0; } /* Draw new zone */ uint16 SCR_43_RefreshZone(void) { script_ptr++; refreshZone(); return 0; } /* Go to new zone and draw it */ uint16 SCR_36_ChangeZone(void) { SCR_42_LoadZone(); refreshZone(); return 0; } /* Draw a static sprite in the room */ void SCR_DrawRoomObjectBack(byte *x, byte *y, byte *w, byte *h) { byte obj[3]; script_ptr++; obj[0] = *script_ptr++; /*spr*/ obj[1] = *script_ptr++; /*x*/ obj[2] = *script_ptr++; /*y*/ drawRoomStaticObject(obj, x, y, w, h); } /* Draw a static sprite in the room (to backbuffer) */ uint16 SCR_5F_DrawRoomObjectBack(void) { byte x, y, w, h; SCR_DrawRoomObjectBack(&x, &y, &w, &h); return 0; } /* Display a static sprite in the room (to screen) */ uint16 SCR_11_DrawRoomObject(void) { byte x, y, w, h; SCR_DrawRoomObjectBack(&x, &y, &w, &h); cga_CopyScreenBlock(backbuffer, w, h, frontbuffer, CalcXY_p(x, y)); return 0; } /* Draw box with item sprite and its name */ uint16 SCR_3_DrawItemBox(void) { byte current; item_t *item; byte x, y; byte *msg; script_ptr++; current = *script_ptr++; if (current) item = (item_t *)script_vars[kScrPool3_CurrentItem]; else item = &inventory_items[aspirant_ptr->item - 1]; x = dirty_rects[0].x; y = dirty_rects[0].y + 70; msg = seekToString(desci_data, 274 + item->name); desciTextBox(x, y, 18, msg); drawSpriteN(item->sprite, x, y + 1, frontbuffer); return 0; } /* Draw simple bubble with text */ uint16 SCR_37_desciTextBox(void) { byte x, y, w; byte *msg; script_ptr++; msg = seekToStringScr(desci_data, *script_ptr, &script_ptr); script_ptr++; x = *script_ptr++; y = *script_ptr++; w = *script_ptr++; desciTextBox(x, y, w, msg); return 0; } /* Play portrait animation */ uint16 SCR_18_AnimPortrait(void) { byte layer, index, delay; script_ptr++; layer = *script_ptr++; index = *script_ptr++; delay = *script_ptr++; animPortrait(layer, index, delay); return 0; } /* Play animation */ uint16 SCR_38_PlayAnim(void) { byte index, x, y; script_ptr++; index = *script_ptr++; x = *script_ptr++; y = *script_ptr++; playAnim(index, x, y); return 0; } /* Pop up the actions menu and handle its commands */ uint16 SCR_3D_ActionsMenu(void) { uint16 cmd; byte *old_script = script_ptr; byte *old_script_end = script_end_ptr; act_menu_x = 0xFF; for (;;) { script_ptr++; actionsMenu(&script_ptr); if (the_command == 0xFFFF) break; cmd = the_command & 0xF000; if (cmd == 0xC000 || cmd == 0xA000) { return ScriptRerun; } runCommand(); // For properly returning to RunScript() during the 2nd Call from SCR_4D_PriorityCommand() to runCommand() if (g_vm->_prioritycommand_1) { g_vm->_prioritycommand_2 = true; break; } script_byte_vars.used_commands++; if (script_byte_vars.bvar_43 == 0 && script_byte_vars.used_commands > script_byte_vars.check_used_commands) { the_command = Swap16(script_word_vars.next_aspirant_cmd); if (the_command) return ScriptRerun; } script_ptr = old_script; if (--script_byte_vars.tries_left == 0) resetAllPersons(); } script_end_ptr = old_script_end; return ScriptContinue; } /* The Wall room puzzle */ uint16 SCR_3E_TheWallAdvance(void) { script_ptr++; IFGM_PlaySample(29); script_byte_vars.the_wall_phase = (script_byte_vars.the_wall_phase + 1) % 4; switch (script_byte_vars.the_wall_phase) { default: theWallPhase3_DoorOpen1(); break; case 0: theWallPhase0_DoorOpen2(); break; case 1: theWallPhase1_DoorClose1(); break; case 2: theWallPhase2_DoorClose2(); break; } return 0; } /* When playing cups with proto */ uint16 SCR_28_MenuLoop(void) { byte cursor; byte mask, value; script_ptr++; cursor = *script_ptr++; mask = *script_ptr++; value = *script_ptr++; selectCursor(cursor); menuLoop(mask, value); return 0; } /* Restore screen data from back buffer as specified by dirty rects of specified index */ uint16 SCR_2A_PopDialogRect(void) { byte index; byte kind; byte x, y; byte width, height; uint16 offs; script_ptr++; index = *script_ptr++; getDirtyRectAndFree(index, &kind, &x, &y, &width, &height, &offs); cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs); /*TODO: implicit target*/ cga_CopyScreenBlock(backbuffer, 2, 21, CGA_SCREENBUFFER, offs = (x << 8) | y); /*TODO: implicit target*/ cur_dlg_index = 0; return 0; } /* Restore screen data from back buffer as specified by dirty rect of kind dialog bubble */ uint16 SCR_2B_PopAllBubbles(void) { script_ptr++; popDirtyRects(DirtyRectBubble); return 0; } /* Hide a portrait, with shatter effect */ uint16 SCR_22_HidePortraitShatter(void) { byte index; byte kind; byte x, y; byte width, height; uint16 offs; script_ptr++; index = *script_ptr++; getDirtyRectAndFree(index, &kind, &x, &y, &width, &height, &offs); if (right_button) { cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs); return 0; } cga_HideShatterFall(CGA_SCREENBUFFER, backbuffer, width, height, CGA_SCREENBUFFER, offs); return 0; } /* Hide a portrait, no special effects */ uint16 SCR_23_HidePortrait(void) { byte index; byte kind; byte x, y; byte width, height; uint16 offs; script_ptr++; index = *script_ptr++; getDirtyRectAndFree(index, &kind, &x, &y, &width, &height, &offs); if (right_button) { cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs); return 0; } cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs); return 0; } /* Hide a portrait, no special effects */ uint16 SCR_1D_HidePortrait(void) { return SCR_23_HidePortrait(); } /* Hide a portrait, no special effects */ uint16 SCR_21_HidePortrait(void) { return SCR_23_HidePortrait(); } /* Hide a portrait, no special effects */ uint16 SCR_3F_HidePortrait(void) { return SCR_23_HidePortrait(); } /* Restore screen data from back buffer for all portraits */ uint16 SCR_24_PopAllPortraits(void) { script_ptr++; popDirtyRects(DirtyRectSprite); return 0; } /* Restore screen data from back buffer for all text bubbles */ uint16 SCR_40_PopAllTextBoxes() { script_ptr++; popDirtyRects(DirtyRectText); return 0; } /* Draw updated Hands in Who Will Be Saved */ uint16 SCR_41_LiftHand(void) { script_ptr++; redrawRoomStatics(92, script_byte_vars.hands); cga_BackBufferToRealFull(); playSound(31); return 0; } byte fight_mode = 0; uint16 SCR_30_Fight(void) { static byte player_image[] = {26, 0, 0}; byte *image = player_image; byte x, y, width, height, kind; uint16 offs; byte *old_script, *old_script_end = script_end_ptr; pers_t *pers = (pers_t *)(script_vars[kScrPool8_CurrentPers]); byte strength, win, rnd; script_ptr++; old_script = script_ptr; x = 140 / 4; y = 20; fight_mode = 1; if (pers->name != 44) { /*VORT*/ if (next_vorts_cmd == 0xA015) { the_command = 0xA015; runCommand(); selectPerson(PersonOffset(pers - pers_list)); } if (Swap16(script_word_vars.next_aspirant_cmd) == 0xC357) { the_command = 0xC357; runCommand(); } pers = (pers_t *)(script_vars[kScrPool8_CurrentPers]); if (pers->name != 56 && pers->name != 51) { /*MONKEY, TURKEY*/ x = dirty_rects[0].x + 64 / 4; y = dirty_rects[0].y; fight_mode = 0; } } /*draw player portrait*/ player_image[1] = x; player_image[2] = y; if (drawPortrait(&image, &x, &y, &width, &height)) cga_AnimLiftToLeft(width, cur_image_pixels, width, 1, height, CGA_SCREENBUFFER, CalcXY_p(x + width - 1, y)); blinkToWhite(); if (pers->name != 44 && pers->name != 56 && pers->name != 51) { /*VORT, MONKEY, TURKEY*/ getDirtyRectAndFree(1, &kind, &x, &y, &width, &height, &offs); cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs); } /*check fight outcome*/ strength = 0; script_byte_vars.fight_status = 0; if (script_byte_vars.extreme_violence == 0) { static byte character_strength[] = { 1, /*THE MASTER OF ORDEALS*/ 3, /*PROTOZORQ*/ 1, /*VORT*/ 1, /*THE POORMOUTH*/ 1, /*KHELE*/ 1, /*THE MISTRESS*/ 5, /*DEILOS*/ 3, /*ASPIRANT*/ 2, /*DIVO*/ 1, /*TURKEY*/ 1, /*PRIESTESS*/ 1, /*SCI FI*/ 1, /*NORMAJEEN*/ 1, /*ASH*/ 1, /*MONKEY*/ 1, /*HARSSK*/ 1 /*ZORQ*/ }; strength = character_strength[pers->name - 42]; /*check if can decrease*/ if (strength != 1 && (pers->flags & PERSFLG_80)) strength--; if (script_byte_vars.zapstiks_owned != 0 || script_byte_vars.bvar_66 != 0) strength--; } /*check if can increase*/ if (strength != 5) { if ((pers->item >= kItemDagger1 && pers->item <= kItemDagger4) || (pers->item >= kItemZapstik1 && pers->item <= kItemZapstik13) /*TODO: ignore kItemZapstik14?*/ || pers->item == kItemBlade || pers->item == kItemChopper || ((pers->index >> 3) == 6)) strength++; } /* win flags: 1 - player win 2 - player lose 4 - "YOU RUN..." 8 - "THE ASPIRANT STEALS EVERYTHING YOU HAVE!" 0x20 - "TU NUH RAY VUN IN FAY VRABLE SIT YOU AISHUN." 0x40 - "OUT KUM UNSER TUN." 0x80 - "SIT YOU ASHUN KRITI KAL FOR TOONUH RAY VUN." */ win = 1; rnd = script_byte_vars.rand_value; #ifdef CHEAT strength = 1; #endif if (strength >= 2) { if (strength == 2) { if (rnd >= 205) win = getRand() < 128 ? (0x40 | 0x10 | 1) : (0x40 | 0x10 | 2); } else if (strength == 4 && rnd < 100) { win = getRand() < 128 ? (0x40 | 0x10 | 1) : (0x40 | 0x10 | 2); } else { win = 2; if (strength == 3) { if (rnd < 128) /*TODO: check me, maybe original bug (checks against wrong reg?)*/ win = getRand() < 51 ? (0x80 | 0x10 | 1) : (0x80 | 0x10 | 2); else win = getRand() < 205 ? (0x20 | 0x10 | 1) : (0x20 | 0x10 | 2); } } } script_byte_vars.fight_status = win; script_ptr = old_script; script_end_ptr = old_script_end; return 0; } byte prev_fight_mode = 0; uint16 fight_pers_ofs = 0; typedef struct fightentry_t { byte room; animdesc_t anim; } fightentry_t; fightentry_t fightlist1[] = { {50, {47, {{36, 153}}}}, {51, {47, {{36, 153}}}}, {53, {47, {{37, 160}}}}, {54, {47, {{36, 153}}}}, {56, {47, {{31, 128}}}}, {57, {47, {{27, 161}}}}, {58, {47, {{28, 152}}}}, {59, {47, {{25, 153}}}}, {60, {47, {{22, 155}}}}, {61, {47, {{27, 160}}}} }; fightentry_t fightlist2[] = { { 1, {24, {{42, 128}}}}, { 2, {24, {{44, 126}}}}, { 3, {24, {{47, 126}}}}, { 4, {24, {{44, 126}}}}, { 5, {24, {{47, 126}}}}, { 6, {24, {{28, 126}}}}, { 7, {24, {{55, 126}}}}, { 8, {24, {{49, 126}}}}, {10, {24, {{41, 147}}}}, {11, {24, {{41, 147}}}}, {18, {24, {{41, 147}}}}, {19, {24, {{41, 147}}}}, {90, {24, {{44, 121}}}}, {91, {28, {{24, 123}}}}, {12, {24, {{41, 147}}}}, {13, {24, {{41, 147}}}}, {35, {24, {{39, 147}}}}, {42, {24, {{39, 147}}}}, {50, {55, {{46, 130}}}}, {52, {24, {{42, 121}}}}, {54, {55, {{46, 130}}}}, {61, {67, {{37, 125}}}}, {62, {55, {{32, 133}}}}, {63, {55, {{32, 133}}}}, {64, {55, {{32, 133}}}}, {65, {55, {{32, 133}}}} }; fightentry_t fightlist3[] = { { 2, {25, {{35, 144}}}}, { 3, {25, {{38, 144}}}}, { 4, {25, {{35, 144}}}}, { 5, {25, {{38, 144}}}}, { 6, {25, {{19, 144}}}}, { 7, {25, {{46, 144}}}}, { 8, {26, {{64, 132}}}}, {10, {25, {{32, 165}}}}, {11, {25, {{32, 165}}}}, {12, {25, {{32, 165}}}}, {13, {25, {{32, 165}}}}, {18, {25, {{32, 165}}}}, {19, {25, {{32, 165}}}}, {90, {36, {{27, 127}}}}, {91, {27, {{44, 123}}}}, {35, {25, {{30, 165}}}}, {42, {25, {{30, 165}}}}, {50, {56, {{36, 153}}}}, {54, {56, {{36, 153}}}}, {62, {56, {{22, 156}}}}, {63, {56, {{22, 156}}}}, {64, {56, {{22, 156}}}}, {65, {56, {{22, 156}}}} }; /*Draw defeated enemy*/ uint16 SCR_31_Fight2(void) { script_ptr++; if (script_byte_vars.bvar_43 != 18) { pers_t *pers = (pers_t *)(script_vars[kScrPool8_CurrentPers]); fight_pers_ofs = (byte *)pers - (byte *)pers_list; /*TODO check size*/ pers->flags |= PERSFLG_40; pers->area = 0; found_spot->flags &= ~SPOTFLG_80; if (pers->index == 16) { /*Vort trio*/ pers_list[kPersVort2].area = script_byte_vars.zone_area; pers_list[kPersVort2].flags = pers->flags; if (script_byte_vars.zapstiks_owned == 0) { static const animdesc_t anim19 = {ANIMFLG_USESPOT | 19, { { 0, 0 } }}; animateSpot(&anim19); } the_command = next_vorts_cmd; runCommand(); } else if (pers->index == 8) { /*Vort duo*/ pers_list[kPersVort3].area = script_byte_vars.zone_area; pers_list[kPersVort3].flags = pers->flags; if (script_byte_vars.zapstiks_owned == 0) { static const animdesc_t anim20 = {ANIMFLG_USESPOT | 20, { { 0, 0 } }}; animateSpot(&anim20); } the_command = next_vorts_cmd; runCommand(); } else { if (prev_fight_mode == 0 && script_byte_vars.zapstiks_owned != 0 && fight_mode == 0) { script_byte_vars.fight_status &= ~1; } else { uint16 i; fightentry_t *fightlist; uint16 fightlistsize; byte animidx; prev_fight_mode = 0; switch (pers->name) { case 56: /*MONKEY*/ animidx = 47; fightlist = fightlist1; fightlistsize = 10; break; case 51: /*TURKEY*/ next_turkey_cmd = 0; animidx = 66; fightlist = fightlist1; fightlistsize = 10; break; default: animidx = 0; fightlist = fightlist2; fightlistsize = 26; } for (i = 0; i < fightlistsize; i++) { if (fightlist[i].room == script_byte_vars.zone_room) { if (animidx != 0) { fightlist[i].anim.index = animidx; IFGM_PlaySample(150); } if (fightlist[i].anim.index == 55) playSound(151); playAnim(fightlist[i].anim.index, fightlist[i].anim.params.coords.x, fightlist[i].anim.params.coords.y); break; } } } } } return 0; } void FightWin(void) { script_byte_vars.bvar_67 = 0; if (script_byte_vars.bvar_43 != 18 && *spot_sprite != 0) { cga_RestoreImage(*spot_sprite, frontbuffer); cga_RestoreImage(*spot_sprite, backbuffer); if (script_byte_vars.extreme_violence == 0 && script_byte_vars.bvar_60 == 0 && script_byte_vars.zapstiks_owned != 0 && fight_mode == 0) { script_byte_vars.bvar_67 = 1; playSound(149); playAnim(40, found_spot->sx, found_spot->sy); } } prev_fight_mode = script_byte_vars.extreme_violence; script_byte_vars.extreme_violence = 0; } uint16 SCR_32_FightWin(void) { script_ptr++; FightWin(); return 0; } void DrawDeathAnim(void) { int16 i; /*remove existing cadaver if any*/ if (selectPerson(PersonOffset(kPersCadaver))) { found_spot->flags &= ~SPOTFLG_80; cga_RestoreImage(*spot_sprite, backbuffer); } for (i = 0; i < 23; i++) { if (fightlist3[i].room == script_byte_vars.zone_room) { playAnim(fightlist3[i].anim.index, fightlist3[i].anim.params.coords.x, fightlist3[i].anim.params.coords.y); break; } } } uint16 SCR_16_DrawDeathAnim(void) { script_ptr++; DrawDeathAnim(); return 0; } uint16 SCR_60_ReviveCadaver(void) { pers_t *pers; script_ptr++; blitSpritesToBackBuffer(); selectPerson(PersonOffset(kPersCadaver)); script_byte_vars.bvar_60 = 1; FightWin(); script_byte_vars.bvar_60 = 0; pers_list[kPersCadaver].area = 0; selectPerson(fight_pers_ofs); zone_spots[5].flags = SPOTFLG_40 | SPOTFLG_10 | SPOTFLG_2 | SPOTFLG_1; found_spot->flags |= SPOTFLG_80; pers = (pers_t *)script_vars[kScrPool8_CurrentPers]; pers->flags &= ~PERSFLG_40; pers->area = script_byte_vars.zone_area; drawPersons(); cga_BackBufferToRealFull(); return 0; } uint16 SCR_57_ShowCharacterSprite(void) { byte index, x, y; script_ptr++; index = *script_ptr++; x = *script_ptr++; y = *script_ptr++; drawCharacterSprite(index, x, y, frontbuffer); return 0; } uint16 SCR_58_DrawCharacterSprite(void) { byte index, x, y; script_ptr++; index = *script_ptr++; x = *script_ptr++; y = *script_ptr++; drawCharacterSprite(index, x, y, backbuffer); return 0; } extern void exitGame(void); uint16 SCR_15_SelectSpot(void) { byte mask, index; script_ptr++; mask = *script_ptr++; index = *script_ptr++; if (mask != 0) { index = findSpotByFlags(mask, index); /*TODO: return 0 if not found?*/ if (index == 0xFF) { TODO("ERROR: SelectSpot: spot not found"); exitGame(); /*hard abort*/ } } found_spot = &zone_spots[index - 1]; script_byte_vars.cur_spot_idx = index; spot_sprite = &sprites_list[index - 1]; findPerson(); if (script_byte_vars.cur_pers == 0) script_vars[kScrPool8_CurrentPers] = &pers_list[kPersProtozorq12]; return 0; } uint16 SCR_44_BackBufferToScreen(void) { script_ptr++; cga_BackBufferToRealFull(); return 0; } /* Animate De Profundis room on entry */ uint16 SCR_45_DeProfundisRoomEntry(void) { uint16 w, h; uint16 sprofs, ofs; script_ptr++; /*draw Platform*/ sprofs = getPuzzlSprite(3, 140 / 4, 174, &w, &h, &ofs); cga_BlitScratchBackSprite(sprofs, w, h, CGA_SCREENBUFFER, ofs); /*draw Granite Monster*/ sprofs = getPuzzlSprite(119, 128 / 4, 94, &w, &h, &ofs); cga_BlitScratchBackSprite(sprofs, w, h, CGA_SCREENBUFFER, ofs); promptWait(); for (; h; h--) { waitVBlank(); waitVBlank(); cga_BlitFromBackBuffer(w, 1, CGA_SCREENBUFFER, ofs); ofs ^= g_vm->_line_offset; if ((ofs & g_vm->_line_offset) == 0) ofs += g_vm->_screenBPL; cga_BlitScratchBackSprite(sprofs, w, h, CGA_SCREENBUFFER, ofs); } cga_BlitFromBackBuffer(w, 1, CGA_SCREENBUFFER, ofs); return 0; } /* Animate De Profundis hook (lower) */ uint16 SCR_46_DeProfundisLowerHook(void) { byte y; uint16 w, h; uint16 sprofs, ofs; script_ptr++; /*draw Hook*/ sprofs = getPuzzlSprite(96, 140 / 4, 18, &w, &h, &ofs); h = 1; y = 15; sprofs = y * 20 / 4 * 2; /*TODO: 20 is the sprite width. replace with w?*/ for (; y; y--) { waitVBlank(); cga_BlitFromBackBuffer(w, h, CGA_SCREENBUFFER, ofs); h++; sprofs -= 20 / 4 * 2; cga_BlitScratchBackSprite(sprofs, w, h, CGA_SCREENBUFFER, ofs); } return 0; } /* Animate De Profundis monster (rise) */ uint16 SCR_47_DeProfundisRiseMonster(void) { byte y; uint16 w, h; uint16 sprofs, ofs; script_ptr++; /*draw Granite Monster head*/ sprofs = getPuzzlSprite(118, 112 / 4, 174, &w, &h, &ofs); h = 1; y = 68; for (; y; y--) { waitVBlank(); ofs ^= g_vm->_line_offset; if ((ofs & g_vm->_line_offset) != 0) ofs -= g_vm->_screenBPL; h++; cga_BlitScratchBackSprite(sprofs, w, h, CGA_SCREENBUFFER, ofs); } return 0; } /* Animate De Profundis monster (lower) */ uint16 SCR_48_DeProfundisLowerMonster(void) { byte y; uint16 w, h; uint16 sprofs, ofs; script_ptr++; /*draw Hook*/ sprofs = getPuzzlSprite(118, 112 / 4, 106, &w, &h, &ofs); y = 34; for (; y; y--) { waitVBlank(); cga_BlitFromBackBuffer(w, 1, CGA_SCREENBUFFER, ofs); ofs ^= g_vm->_line_offset; if ((ofs & g_vm->_line_offset) == 0) ofs += g_vm->_screenBPL; h--; cga_BlitScratchBackSprite(sprofs, w, h, CGA_SCREENBUFFER, ofs); } return 0; } /* Animate De Profundis hook (rise) */ uint16 SCR_49_DeProfundisRiseHook(void) { byte y; uint16 w, h; uint16 sprofs, ofs; script_ptr++; /*draw Hook*/ sprofs = getPuzzlSprite(96, 140 / 4, 18, &w, &h, &ofs); h = 16; y = 15; for (; y; y--) { waitVBlank(); cga_BlitFromBackBuffer(w, h, CGA_SCREENBUFFER, ofs); h--; sprofs += 20 / 4 * 2; cga_BlitScratchBackSprite(sprofs, w, h, CGA_SCREENBUFFER, ofs); } cga_BlitFromBackBuffer(w, 1, CGA_SCREENBUFFER, ofs); return 0; } /* Animate De Profundis platform */ uint16 SCR_65_DeProfundisMovePlatform(void) { byte state; byte x, y; uint16 w, h; uint16 sprofs, ofs; script_ptr++; state = *script_ptr++; x = 140 / 4; y = 174; if (state != 0) y += 4; /*draw Platform*/ sprofs = getPuzzlSprite(3, x, y, &w, &h, &ofs); y = 4; if (state) { h -= 4; y--; } for (; y; y--) { waitVBlank(); cga_BlitFromBackBuffer(w, h, CGA_SCREENBUFFER, ofs); ofs ^= g_vm->_line_offset; if ((ofs & g_vm->_line_offset) == 0) ofs += g_vm->_screenBPL; h--; cga_BlitScratchBackSprite(sprofs, w, h, CGA_SCREENBUFFER, ofs); } if (state) cga_BlitFromBackBuffer(w, h, CGA_SCREENBUFFER, ofs); return 0; } /* Animate De Profundis monster ride to exit door */ uint16 SCR_66_DeProfundisRideToExit(void) { uint16 w, h; uint16 sprofs, ofs; script_ptr++; /*draw Granite Monster*/ sprofs = getPuzzlSprite(119, 128 / 4, 139, &w, &h, &ofs); cga_BlitScratchBackSprite(sprofs, w, 20, backbuffer, ofs); dot_effect_delay = 1; dot_effect_step = 17; copyScreenBlockWithDotEffect(backbuffer, 112 / 4, 139, 72 / 4, 40, frontbuffer); return 0; } /* Draw item bounce to room objects animation */ uint16 SCR_4E_BounceCurrentItemToRoom(void) { script_ptr++; bounceCurrentItem(ITEMFLG_ROOM, 43); return 0; } /* Draw item bounce to inventory animation */ uint16 SCR_4F_BounceCurrentItemToInventory(void) { script_ptr++; bounceCurrentItem(ITEMFLG_OWNED, 85); return 0; } /* Draw item bounce to inventory animation */ uint16 SCR_50_BounceItemToInventory(void) { byte itemidx; script_ptr++; itemidx = *script_ptr++; script_vars[kScrPool3_CurrentItem] = &inventory_items[itemidx - 1]; bounceCurrentItem(ITEMFLG_OWNED, 85); return 0; } /* Take away Protozorq's zapstik and bounce it to room */ uint16 SCR_4B_ProtoDropZapstik(void) { pers_t *pers = (pers_t *)(script_vars[kScrPool8_CurrentPers]); script_ptr++; if ((pers->index & 0x38) != 0x30) return 0; pers->index &= ~0x18; script_vars[kScrPool3_CurrentItem] = &inventory_items[kItemZapstik1 - 1 + (script_byte_vars.cur_pers - 1) - kPersProtozorq1]; bounceCurrentItem(ITEMFLG_ROOM, 43); return 0; } /* Take away Aspirant's item and bounce it to the inventory */ void LootAspirantsItem(void) { if (aspirant_ptr->item != 0) { item_t *item = &inventory_items[aspirant_ptr->item - 1]; aspirant_ptr->item = 0; script_vars[kScrPool3_CurrentItem] = item; script_byte_vars.steals_count++; script_byte_vars.bvar_6D[aspirant_ptr->index >> 6] = item->name; /*TODO: check these index bits*/ bounceCurrentItem(ITEMFLG_OWNED, 85); the_command = 0x90AA; /*OK*/ } else the_command = 0x9140; /*NOTHING ON HIM*/ } /* Take away Aspirant's item and bounce it to the inventory */ uint16 SCR_2F_LootAspirantsItem() { script_ptr++; LootAspirantsItem(); return ScriptRerun; } /* Trade with Skull Trader */ uint16 SCR_51_SkullTraderItemTrade(void) { byte *old_script, *old_script_end = script_end_ptr; byte status; if (script_byte_vars.bvar_26 >= 63) /*TODO: hang?*/ return 0; script_ptr++; old_script = script_ptr; inv_bgcolor = 0xFF; openInventory(0xFF, ITEMFLG_OWNED); status = 1; if (inv_count != 0) { status = 2; if (the_command != 0) { status = 3; if (script_byte_vars.inv_item_index >= kItemRope1 && script_byte_vars.inv_item_index <= kItemLantern4) { the_command = 0xC204; /*WHICH ONE DO YOU WANT?*/ runCommand(); ((item_t *)(script_vars[kScrPool3_CurrentItem]))->flags = 0; openInventory(0xFF, ITEMFLG_TRADER); status = 4; if (the_command != 0) { /*50% chance to win the item*/ status = 5; /*lose*/ #ifdef CHEAT_TRADER { /*always win at the Skull Trader*/ #else if (script_byte_vars.rand_value < 128) { #endif status = 6; /*win*/ ((item_t *)(script_vars[kScrPool3_CurrentItem]))[-1].flags = ITEMFLG_TRADER; /*offer previous item copy for next trade*/ ((item_t *)(script_vars[kScrPool3_CurrentItem]))->flags = 0; /*consume selected item*/ } } } } } script_byte_vars.skull_trader_status = status; script_ptr = old_script; script_end_ptr = old_script_end; return 0; } uint16 SCR_52_RefreshSpritesData(void) { script_ptr++; refreshSpritesData(); return 0; } uint16 SCR_53_FindInvItem(void) { byte first, count, flags, i; item_t *item; script_ptr++; first = *script_ptr++; count = *script_ptr++; flags = *script_ptr++; item = &inventory_items[first - 1]; for (i = 0; i < count; i++) { if (item[i].flags == flags) { script_vars[kScrPool3_CurrentItem] = &item[i]; return 0; } } script_vars[kScrPool3_CurrentItem] = &item[count - 1]; return 0; } /* Restore whole room from backbuffer, with dot effect */ uint16 SCR_54_DotFadeRoom(void) { script_ptr++; dot_effect_delay = 1; dot_effect_step = 17; copyScreenBlockWithDotEffect(backbuffer, room_bounds_rect.sx, room_bounds_rect.sy, room_bounds_rect.ex - room_bounds_rect.sx, room_bounds_rect.ey - room_bounds_rect.sy, frontbuffer); return 0; } uint16 SCR_55_DrawRoomItemsIndicator(void) { script_ptr++; drawRoomItemsIndicator(); return 0; } /* TODO: check and rename me */ uint16 SCR_56_MorphRoom98(void) { int16 h; uint16 ofs; script_ptr++; IFGM_PlaySample(242); redrawRoomStatics(98, 0); ofs = cga_CalcXY(0, 136); for (h = 60; h; h--) { memcpy(frontbuffer + ofs, backbuffer + ofs, g_vm->_screenBPL); waitVBlank(); ofs ^= g_vm->_line_offset; if ((ofs & g_vm->_line_offset) != 0) ofs -= g_vm->_screenBPL; } backupSpotImage(&zone_spots[3], &sprites_list[3], sprites_list[3]); IFGM_StopSample(); return 0; } /* Copy backbuffer to screen, with added vertical mirror */ void ShowMirrored(uint16 h, uint16 ofs) { uint16 x, ofs2 = ofs; /*move 1 line up*/ ofs2 ^= g_vm->_line_offset; if ((ofs2 & g_vm->_line_offset) != 0) ofs2 -= g_vm->_screenBPL; while (h--) { for (x = 0; x < g_vm->_screenBPL; x++) { frontbuffer[ofs2 + x] = frontbuffer[ofs + x] = backbuffer[ofs + x]; backbuffer[ofs + x] = 0; } /*move 1 line down*/ ofs += g_vm->_screenBPL; ofs ^= g_vm->_line_offset; if ((ofs & g_vm->_line_offset) != 0) ofs -= g_vm->_screenBPL; /*move 1 line up*/ ofs2 ^= g_vm->_line_offset; if ((ofs2 & g_vm->_line_offset) != 0) ofs2 -= g_vm->_screenBPL; } } void LiftLines(int16 n, byte *source, uint16 sofs, byte *target, uint16 tofs) { while (n--) { memcpy(target + tofs, source + sofs, g_vm->_screenBPL); sofs += g_vm->_screenBPL; sofs ^= g_vm->_line_offset; if ((sofs & g_vm->_line_offset) != 0) sofs -= g_vm->_screenBPL; tofs += g_vm->_screenBPL; tofs ^= g_vm->_line_offset; if ((tofs & g_vm->_line_offset) != 0) tofs -= g_vm->_screenBPL; } } #define kSaucerAnimFrames 53 static void AnimSaucer(void) { static byte image1[] = {167, 0, 146}; byte *pimage1 = image1; byte *sequence = souco_data; byte x, y, width, height; uint16 xx, yy, ww, hh; byte height_new, height_prev; uint16 delay; byte scroll_done = 0; memset(backbuffer, 0, sizeof(backbuffer) - 2); /*TODO: original bug?*/ cga_BackBufferToRealFull(); cga_ColorSelect(0x30); right_button = 0; if (!drawPortrait(&pimage1, &x, &y, &width, &height)) return; height_prev = 200 - 1; delay = 10000; xx = x; /*TODO: is it ok? maybe need *4*/ yy = y; ww = 254; hh = 107; for (; sequence < souco_data + kSaucerAnimFrames * 8; sequence += 8) { uint16 i, ofs, ofs2, baseofs; if (sequence != souco_data) { /*reuse portrait's params for initial state*/ xx = (sequence[0] << 8) | sequence[1]; yy = (sequence[2] << 8) | sequence[3]; ww = (sequence[4] << 8) | sequence[5]; hh = (sequence[6] << 8) | sequence[7]; } hh >>= 1; height_new = yy + hh; height_prev -= (yy - 1); /*scale the saucer*/ cga_ZoomInplaceXY(cur_image_pixels, width, height, ww, hh, xx, yy, backbuffer); baseofs = cga_CalcXY(0, yy); if (!scroll_done) { /*scroll the saucer*/ scroll_done = 1; ofs2 = ofs = baseofs; /*previous line*/ ofs ^= g_vm->_line_offset; if ((ofs & g_vm->_line_offset) != 0) ofs -= g_vm->_screenBPL; for (i = 0; i < 55; i++) { memcpy(backbuffer + ofs, backbuffer + ofs2, g_vm->_screenBPL); /*next line*/ ofs2 += g_vm->_screenBPL; ofs2 ^= g_vm->_line_offset; if ((ofs2 & g_vm->_line_offset) != 0) ofs2 -= g_vm->_screenBPL; /*previous line line*/ ofs ^= g_vm->_line_offset; if ((ofs & g_vm->_line_offset) != 0) ofs -= g_vm->_screenBPL; } ofs2 = cga_CalcXY(0, 200 - 1); for (i = 0; i < 108; i++) { LiftLines(i + 1, backbuffer, ofs, frontbuffer, ofs2); ofs2 ^= g_vm->_line_offset; if ((ofs2 & g_vm->_line_offset) != 0) ofs2 -= g_vm->_screenBPL; waitVBlank(); waitVBlank(); } /*wipe 56 lines*/ memset(backbuffer + ofs2, 0, 56 / 2 * g_vm->_screenBPL); ofs2 ^= g_vm->_line_offset; if ((ofs2 & g_vm->_line_offset) == 0) ofs2 += g_vm->_screenBPL; memset(backbuffer + ofs2, 0, 54 / 2 * g_vm->_screenBPL); for (i = 0xFFFF; i--;) ; /*TODO: weak delay*/ IFGM_PlaySample(240); } /*draw the full saucer on screen*/ ShowMirrored(height_prev + 1, baseofs); height_prev = height_new; waitVBlank(); for (i = delay; i--;) ; /*TODO: weak delay*/ delay += 500; } } extern int16 loadSplash(const char *filename); /* TODO: check me */ void theEnd(void) { static byte image2[] = {168, 28, 85, 22, 15}; byte *pimage2 = image2; AnimSaucer(); if (g_vm->getLanguage() == Common::EN_USA) { drawPortraitZoomed(&pimage2); script_byte_vars.zone_index = 135; do { pollInputButtonsOnly(); } while(buttons == 0); while (!loadFond()) askDisk2(); jaggedZoom(backbuffer, frontbuffer); cga_BackBufferToRealFull(); } else { while (!loadSplash("PRES.BIN")) askDisk2(); cga_BackBufferToRealFull(); } } uint16 SCR_5B_TheEnd(void) { script_ptr++; /*Useless since this handler never returns*/ script_byte_vars.game_paused = 5; theEnd(); if (g_vm->getLanguage() == Common::EN_USA) restartGame(); else for (;;) ; /*HANG*/ return 0; } /* Discard all inventory items */ uint16 SCR_5C_ClearInventory(void) { int16 i; script_ptr++; for (i = 0; i < MAX_INV_ITEMS; i++) { if (inventory_items[i].flags == ITEMFLG_OWNED) inventory_items[i].flags = 0; } script_byte_vars.zapstiks_owned = 0; return 0; } /* Drop group of items from inventory to room */ void DropItems(int16 first, int16 count) { int16 i; for (i = 0; i < count; i++) { if (inventory_items[first + i].flags == ITEMFLG_OWNED) { inventory_items[first + i].flags = ITEMFLG_ROOM; inventory_items[first + i].area = script_byte_vars.zone_area; } } } /* Drop weapon-like items from inventory to room */ uint16 SCR_5D_DropWeapons(void) { script_ptr++; DropItems(kItemDagger1 - 1, 4); /*DAGGER*/ DropItems(kItemZapstik1 - 1, 14); /*ZAPSTIK*/ DropItems(kItemBlade - 1, 2); /*SACRIFICIAL BLADE , CHOPPER*/ script_byte_vars.zapstiks_owned = 0; return 0; } /* React to Psi power */ uint16 SCR_62_PsiReaction(void) { byte power; uint16 cmd; script_ptr++; power = *script_ptr++; cmd = script_word_vars.zone_obj_cmds[(script_byte_vars.cur_spot_idx - 1) * 5 + power]; if (cmd == 0) cmd = script_word_vars.psi_cmds[power]; /*TODO: is this consistent with zone_obj_cmds?*/ the_command = Swap16(cmd); return 0; } /*TODO: rename me*/ uint16 SCR_63_LiftSpot6(void) { script_ptr++; blitSpritesToBackBuffer(); zone_spots[6].sy -= 5; zone_spots[6].ey -= 5; backupSpotsImages(); drawPersons(); cga_BackBufferToRealFull(); return 0; } uint16 SCR_64_DrawBoxAroundSpot(void) { script_ptr++; drawBoxAroundSpot(); return 0; } /* Draw text box */ uint16 SCR_14_DrawDesc(void) { byte *msg; script_ptr++; msg = seekToStringScr(desci_data, *script_ptr, &script_ptr); script_ptr++; drawMessage(msg, CGA_SCREENBUFFER); return 0; } /* Draw dialog bubble with text for a person, wait for a key, then hide. Auto find bubble location Use "thought" spike */ uint16 SCR_17_DrawPersonThoughtBubbleDialog(void) { byte x, y; byte *msg; script_ptr++; msg = seekToStringScr(diali_data, *script_ptr, &script_ptr); script_ptr++; x = found_spot->sx; y = found_spot->sy; if (x < 140 / 4) drawPersonBubble(found_spot->ex, y - 40, SPIKE_BUBLEFT | 20, msg); else drawPersonBubble(x - 80 / 4, y - 40, SPIKE_BUBRIGHT | 20, msg); promptWait(); popDirtyRects(DirtyRectBubble); return 0; } /* Draw dialog bubble with text for a person, wait for a key, then hide. Auto find bubble location Use normal spike */ uint16 SCR_61_drawPersonBubbleDialog(void) { byte x, y; byte *msg; script_ptr++; msg = seekToStringScr(diali_data, *script_ptr, &script_ptr); script_ptr++; x = found_spot->sx; y = found_spot->sy; if (x < 140 / 4) drawPersonBubble(found_spot->ex, y - 40, SPIKE_DNLEFT | 20, msg); else drawPersonBubble(x - 80 / 4, y - 40, SPIKE_DNRIGHT | 20, msg); promptWait(); popDirtyRects(DirtyRectBubble); return 0; } #if 1 byte *DebugString(char *msg, ...) { int16 i; byte c; static byte m[256]; va_list ap; va_start(ap, msg); vsnprintf((char *)m, 256, msg, ap); va_end(ap); for (i = 0; m[i]; i++) { c = m[i]; if (c >= 'A' && c <= 'Z') c = 0x21 + (c - 'A'); else if (c >= 'a' && c <= 'z') c = 0x21 + (c - 'a'); else if (c >= '0' && c <= '9') c = 0x10 + (c - '0'); else if (c == ' ') c = 0x20; else if (c == '!') c = 0x01; else if (c == ',') c = 0x0C; else if (c == '.') c = 0x0E; else if (c == '\n') c = 0x00; else c = 0x1F; m[i] = c; } cur_str_end = m + i; return m; } #endif /* Draw dialog bubble with text for gauss */ uint16 SCR_27_DrawGaussBubble(void) { byte *msg; script_ptr++; msg = seekToStringScr(diali_data, *script_ptr, &script_ptr); script_ptr++; drawPersonBubble(32 / 4, 20, 15, msg); return 0; } /* Draw dialog bubble with text */ uint16 SCR_29_DialiTextBox(void) { byte x, y, f; byte *msg; script_ptr++; msg = seekToStringScr(diali_data, *script_ptr, &script_ptr); cur_dlg_index = cur_str_index; /*TODO: useless?*/ script_ptr++; x = *script_ptr++; y = *script_ptr++; f = *script_ptr++; drawPersonBubble(x, y, f, msg); return 0; } /* Do nothing in PC/CGA version */ uint16 SCR_F_Unused(void) { script_ptr++; script_ptr++; script_ptr++; script_ptr++; return 0; } /* Do nothing in PC/CGA version */ uint16 SCR_4A_Unused(void) { script_ptr++; return 0; } /* Do nothing in PC/CGA version */ uint16 SCR_67_Unused(void) { script_ptr++; script_ptr++; return 0; } /* Play Sfx NB! Do nothing in EU PC/CGA version */ uint16 SCR_68_PlaySfx(void) { byte index; script_ptr++; index = *script_ptr++; script_ptr++; IFGM_PlaySfx(index); return 0; } /* Play sound */ uint16 SCR_69_playSound(void) { byte index; script_ptr++; index = *script_ptr++; script_ptr++; playSound(index); return 0; } /* Do nothing in PC/CGA version */ uint16 SCR_6A_Unused(void) { script_ptr++; return 0; } /* Open room's items inventory */ uint16 CMD_1_RoomObjects(void) { updateUndrawCursor(CGA_SCREENBUFFER); inv_bgcolor = 0xAA; openInventory((0xFF << 8) | ITEMFLG_ROOM, (script_byte_vars.zone_area << 8) | ITEMFLG_ROOM); return ScriptRerun; } /* Open Psi Powers menu */ uint16 CMD_2_PsiPowers(void) { /*Psi powers bar*/ backupAndShowSprite(3, 280 / 4, 40); processInput(); do { pollInput(); selectCursor(CURSOR_FINGER); checkPsiCommandHover(); if (command_hint != 100) command_hint += 109; if (command_hint != last_command_hint) drawCommandHint(); drawHintsAndCursor(CGA_SCREENBUFFER); } while (buttons == 0); undrawCursor(CGA_SCREENBUFFER); cga_RestoreBackupImage(CGA_SCREENBUFFER); return ScriptRerun; } /* Open normal inventory box */ uint16 CMD_3_Possessions(void) { updateUndrawCursor(CGA_SCREENBUFFER); inv_bgcolor = 0x55; openInventory(ITEMFLG_OWNED, ITEMFLG_OWNED); return ScriptRerun; } /* Show energy level */ uint16 CMD_4_EnergyLevel(void) { static byte energy_image[] = {130, 236 / 4, 71}; byte x, y, width, height; byte *image = energy_image; byte anim = 40; popDirtyRects(DirtyRectSprite); popDirtyRects(DirtyRectBubble); cur_dlg_index = 0; ifgm_flag2 = 0xff; if (script_byte_vars.psy_energy != 0) anim = 41 + (script_byte_vars.psy_energy / 16); if (drawPortrait(&image, &x, &y, &width, &height)) { cga_BlitAndWait(cur_image_pixels, cur_image_size_w, cur_image_size_w, cur_image_size_h, CGA_SCREENBUFFER, cur_image_offs); } do { IFGM_PlaySample(28); animPortrait(1, anim, 10); animPortrait(1, anim + 14, 10); pollInputButtonsOnly(); } while (buttons == 0); popDirtyRects(DirtyRectSprite); ifgm_flag2 = 0; IFGM_StopSample(); return 0; } /* Advance time */ uint16 CMD_5_Wait(void) { script_byte_vars.bvar_25++; script_word_vars.timer_ticks2 = Swap16(Swap16(script_word_vars.timer_ticks2) + 300); the_command = next_vorts_cmd; runCommand(); the_command = next_turkey_cmd; runCommand(); script_byte_vars.used_commands = script_byte_vars.check_used_commands; the_command = Swap16(script_word_vars.wvar_0E); if (the_command == 0) { if (script_word_vars.next_aspirant_cmd == 0) { the_command = 0x9005; runCommand(); } } else { if (script_byte_vars.bvar_26 >= 63 && script_byte_vars.zone_area < 22 && script_byte_vars.zone_area != 1) the_command = 0x9005; return ScriptRerun; } return 0; } /* Load game (menu) */ uint16 CMD_6_Load(void) { the_command = 0xC35C; return ScriptRerun; } /* Save game (menu) */ uint16 CMD_7_Save(void) { the_command = 0xC35D; return ScriptRerun; } /* Show timer */ uint16 CMD_8_Timer(void) { static byte timer_image[] = {163, 244 / 4, 104}; byte x, y, width, height; byte *image = timer_image; if (drawPortrait(&image, &x, &y, &width, &height)) { cga_BlitAndWait(cur_image_pixels, cur_image_size_w, cur_image_size_w, cur_image_size_h, CGA_SCREENBUFFER, cur_image_offs); } do { uint16 timer = Swap16(script_word_vars.timer_ticks2); uint16 minutes = timer % (60 * 60); char_draw_coords_x = 260 / 4; char_draw_coords_y = 120; waitVBlank(); cga_PrintChar(timer / (60 * 60) + 16, CGA_SCREENBUFFER); cga_PrintChar((minutes & 1) ? 26 : 0, CGA_SCREENBUFFER); /*colon*/ cga_PrintChar(minutes / (60 * 10) + 16, CGA_SCREENBUFFER); cga_PrintChar(minutes / 60 + 16, CGA_SCREENBUFFER); pollInputButtonsOnly(); } while (buttons == 0); popDirtyRects(DirtyRectSprite); return 0; } int16 ConsumePsiEnergy(byte amount) { byte current = script_byte_vars.psy_energy; if (current < amount) { /*no energy left*/ playAnim(68, 296 / 4, 71); return 0; } script_byte_vars.psy_energy = current - amount; /*significantly changed?*/ if ((current & 0xF0) != (script_byte_vars.psy_energy & 0xF0)) playAnim(68, 296 / 4, 71); return 1; } uint16 CMD_A_PsiSolarEyes(void) { if (!ConsumePsiEnergy(2)) return 0; if (zone_palette == 14) { redrawRoomStatics(script_byte_vars.zone_room, zone_palette); zone_palette = 0; cga_BackBufferToRealFull(); } the_command = Swap16(script_word_vars.wvar_AA); runCommand(); script_byte_vars.cur_spot_flags = 0xFF; return 0; } uint16 GetZoneObjCommand(uint16 offs) { /*TODO: fix me: change offs/2 to index*/ the_command = Swap16(script_word_vars.zone_obj_cmds[(script_byte_vars.cur_spot_idx - 1) * 5 + offs / 2]); return the_command; } void DrawStickyNet(void) { byte x, y, w, h; uint16 ofs; byte *sprite = loadPuzzlToScratch(80); x = room_bounds_rect.sx; y = room_bounds_rect.sy; w = room_bounds_rect.ex - x; h = room_bounds_rect.ey - y; ofs = CalcXY_p(x, y); /*16x30 is the net sprite size*/ for (; h; h -= 30) { int16 i; for (i = 0; i < w; i += 16 / 4) drawSprite(sprite, frontbuffer, ofs + i); ofs += g_vm->_screenBPL * 30 / 2; } } uint16 CMD_B_PsiStickyFingers(void) { if (!ConsumePsiEnergy(3)) return 0; if (script_byte_vars.bvar_43 != 0) { the_command = Swap16(script_word_vars.wvar_AC); return ScriptRerun; } backupScreenOfSpecialRoom(); DrawStickyNet(); selectCursor(CURSOR_FLY); menuLoop(0, 0); playSound(224); cga_BackBufferToRealFull(); restoreScreenOfSpecialRoom(); if (script_byte_vars.cur_spot_idx == 0 || GetZoneObjCommand(0 * 2) == 0) the_command = Swap16(script_word_vars.psi_cmds[0]); if (script_byte_vars.bvar_26 >= 63 && script_byte_vars.zone_area < 22 && script_byte_vars.zone_area != 1) the_command = 0x9005; return ScriptRerun; } uint16 CMD_C_PsiKnowMind(void) { if (!ConsumePsiEnergy(1)) return 0; if (script_byte_vars.bvar_43 != 0) { the_command = Swap16(script_word_vars.wvar_AE); return ScriptRerun; } processMenu(); if (script_byte_vars.cur_spot_idx == 0 || GetZoneObjCommand(2 * 2) == 0) the_command = Swap16(script_word_vars.psi_cmds[1]); return ScriptRerun; } uint16 CMD_D_PsiBrainwarp(void) { if (!ConsumePsiEnergy(2)) return 0; if (script_byte_vars.bvar_43 == 0) { backupScreenOfSpecialRoom(); processMenu(); if (script_byte_vars.cur_spot_idx == 0) { the_command = Swap16(script_word_vars.wvar_0C); script_byte_vars.dead_flag = 0; return ScriptRerun; } if (GetZoneObjCommand(1 * 2) != 0) { playAnim(39, found_spot->sx + 8 / 4, found_spot->sy - 10); restoreScreenOfSpecialRoom(); return ScriptRerun; } } if (script_byte_vars.bvar_43 == 18) { script_byte_vars.dead_flag = 1; script_byte_vars.tries_left = 2; return 0; } ((pers_t *)script_vars[kScrPool8_CurrentPers])->flags |= PERSFLG_80; script_byte_vars.dead_flag = script_byte_vars.cur_spot_idx; script_byte_vars.tries_left = 2; the_command = 0; if (script_byte_vars.bvar_43 == 0) { playAnim(39, found_spot->sx + 8 / 4, found_spot->sy - 10); restoreScreenOfSpecialRoom(); return ScriptRerun; } the_command = 0x90AA; return ScriptRerun; } uint16 CMD_E_PsiZoneScan(void) { byte x, y, w, h; uint16 offs; if (!ConsumePsiEnergy(1)) return 0; if (script_byte_vars.bvar_43 != 0) { the_command = Swap16(script_word_vars.wvar_B8); return ScriptRerun; } backupScreenOfSpecialRoom(); IFGM_PlaySample(26); offs = CalcXY_p(room_bounds_rect.sx, room_bounds_rect.sy); w = room_bounds_rect.ex - room_bounds_rect.sx; h = room_bounds_rect.ey - room_bounds_rect.sy; for (y = room_bounds_rect.sy; h; y++, h--) { spot_t *spot; for (x = 0; x < w; x++) frontbuffer[offs + x] = ~frontbuffer[offs + x]; cga_blitToScreen(offs, w, 1); waitVBlank(); for (x = 0; x < w; x++) frontbuffer[offs + x] = ~frontbuffer[offs + x]; cga_blitToScreen(offs, w, 1); for (spot = zone_spots; spot != zone_spots_end; spot++) { if ((spot->flags & ~(SPOTFLG_40 | 7)) == (SPOTFLG_20 | SPOTFLG_8) && spot->sy == y) { playSound(27); spot->flags |= SPOTFLG_80; playAnim(38, spot->sx, spot->sy); break; } } offs ^= g_vm->_line_offset; if ((offs & g_vm->_line_offset) == 0) offs += g_vm->_screenBPL; } restoreScreenOfSpecialRoom(); IFGM_StopSample(); the_command = Swap16(script_word_vars.psi_cmds[2]); return ScriptRerun; } uint16 CMD_F_PsiPsiShift(void) { if (!ConsumePsiEnergy(3)) return 0; if (script_byte_vars.bvar_43 != 0) { the_command = Swap16(script_word_vars.wvar_B0); return ScriptRerun; } selectCursor(CURSOR_GRAB); menuLoop(0, 0); backupScreenOfSpecialRoom(); playSound(25); playAnim(39, cursor_x / 4, cursor_y); restoreScreenOfSpecialRoom(); if (script_byte_vars.cur_spot_idx == 0 || GetZoneObjCommand(3 * 2) == 0) the_command = Swap16(script_word_vars.psi_cmds[5]); return ScriptRerun; } uint16 CMD_10_PsiExtremeViolence(void) { uint16 command; if (!ConsumePsiEnergy(8)) return 0; script_byte_vars.extreme_violence = 1; if (script_byte_vars.bvar_43 != 0) { the_command = Swap16(script_word_vars.wvar_B2); return ScriptRerun; } processMenu(); if (script_byte_vars.cur_spot_idx == 0) { the_command = Swap16(script_word_vars.psi_cmds[4]); script_byte_vars.extreme_violence = 0; return ScriptRerun; } command = GetZoneObjCommand(4 * 2); if ((command & 0xF000) == 0x9000) script_byte_vars.extreme_violence = 0; else if (command == 0) { the_command = Swap16(script_word_vars.psi_cmds[4]); script_byte_vars.extreme_violence = 0; } return ScriptRerun; } uint16 CMD_11_PsiTuneIn(void) { uint16 command; byte *msg; if (!ConsumePsiEnergy(4)) return 0; if (script_byte_vars.bvar_43 != 0) command = Swap16(script_word_vars.wvar_B4); else { if (script_byte_vars.bvar_26 < 63 || script_byte_vars.zone_area >= 22) command = Swap16(script_word_vars.psi_cmds[3]); else command = 275; } /*TODO: is this really necessary? Maybe it's always set when loaded from script vars?*/ if (command & 0x8000) { the_command = command; return ScriptRerun; } msg = seekToString(diali_data, command); cur_dlg_index = cur_str_index; /*TODO: useless?*/ drawPersonBubble(32 / 4, 20, 15, msg); promptWait(); popDirtyRects(DirtyRectBubble); return 0; } void ActionForPersonChoice(uint16 *actions) { processMenu(); the_command = 0x9183; /*THERE`S NOBODY.*/ if (script_byte_vars.cur_spot_idx != 0 && script_byte_vars.cur_pers != 0) { pers_t *pers = (pers_t *)script_vars[kScrPool8_CurrentPers]; byte index = pers->name; if (index == 93) /*CADAVER*/ index = 19 + 42; else if (index == 133) /*SCI FI*/ index = 18 + 42; index -= 42; /*Person names: THE MASTER OF ORDEALS, etc*/ the_command = actions[index]; playSound(22); } } /*TODO: ensure these are never accessed/modified from the scripts*/ uint16 menu_commands_12[] = { 0xC0F0, 0xC0D7, 0x9019, 0xC0DA, 0x9019, 0xC0F1, 0x9007, 0xC0D8, 0x9007, 0x9019, 0x9007, 0x9007, 0xC34E, 0xC34F, 0x9019, 0x9019, 0x9019, 0x9019, 0xC319, 0x9007 }; uint16 menu_commands_22[] = { 0xC325, 0xC326, 0xC31B, 0xC31D, 0xC31F, 0xC31E, 0xC320, 0xC327, 0x90EC, 0xC31C, 0xC328, 0, 0xC077, 0xC077, 0xC321, 0xC322, 0x9007, 0xC323, 0xC2A7, 0xC324 }; uint16 menu_commands_24[] = { 0xC344, 0xC34A, 0xC343, 0xC34D, 0x9019, 0xC0F1, 0x9007, 0xC354, 0x90EC, 0xC343, 0xC345, 0, 0xC343, 0xC343, 0xC343, 0x9019, 0x9019, 0x9019, 0xC343, 0xC343 }; uint16 menu_commands_23[] = { 0xC002, 0xC32A, 0x9019, 0xC325, 0xC114, 0xC32B, 0xC32C, 0xC355, 0x90EC, 0x9019, 0xC328, 0, 0xC077, 0xC077, 0x9019, 0x9113, 0, 0xC323, 0xC2A7, 0xC32D }; uint16 CMD_12_(void) { warning("cmd 12"); ActionForPersonChoice(menu_commands_12); return ScriptRerun; } uint16 CMD_13_ActivateFountain(void) { static byte water1[] = {125, 156 / 4, 58}; static byte water2[] = {126, 156 / 4, 58}; static byte headl[] = {88, 152 / 4, 52}; static byte headr[] = {88, (160 / 4) | 0x80, 52}; byte x, y, w, h; uint16 i, j; script_byte_vars.bvar_6A = 1; for (i = 0; i < 10; i++) { drawRoomStaticObject(water1, &x, &y, &w, &h); waitVBlank(); cga_BackBufferToRealFull(); for (j = 0; j < 0x1FFF; j++) ; /*TODO: weak delay*/ drawRoomStaticObject(water2, &x, &y, &w, &h); waitVBlank(); cga_BackBufferToRealFull(); for (j = 0; j < 0x1FFF; j++) ; /*TODO: weak delay*/ } drawRoomStaticObject(headl, &x, &y, &w, &h); drawRoomStaticObject(headr, &x, &y, &w, &h); cga_BackBufferToRealFull(); return 0; } /* Vorts walking into the room */ uint16 CMD_14_VortAppear(void) { /*TODO: check me*/ pers_list[kPersVort].area = script_byte_vars.zone_area; selectPerson(0); animateSpot(&vortanims_ptr->field_1); IFGM_StopSample(); next_vorts_cmd = 0xA015; blitSpritesToBackBuffer(); drawPersons(); cga_BackBufferToRealFull(); next_vorts_ticks = Swap16(script_word_vars.timer_ticks2) + 5; return 0; } pers_t *vort_ptr; #define ADJACENT_AREAS_MAX 19 struct { byte zone; /* current zone */ byte area; /* area accessible from this zone */ } adjacent_areas[ADJACENT_AREAS_MAX] = { { 2, 5}, { 3, 8}, { 4, 8}, { 5, 2}, {120, 3}, {121, 3}, { 8, 10}, { 10, 8}, { 60, 62}, { 62, 66}, { 68, 66}, { 69, 66}, { 67, 65}, { 65, 66}, { 70, 71}, { 71, 70}, { 59, 60}, { 60, 62}, { 63, 65} }; /* Vorts walking out of the room */ uint16 CMD_15_VortLeave(void) { /*TODO: check me*/ uint16 i; animdesc_t *anim; pers_t *pers; if (pers_list[kPersVort].area != 0) { pers = &pers_list[kPersVort]; anim = &vortanims_ptr->field_4; } else if (pers_list[kPersVort2].area != 0) { pers = &pers_list[kPersVort2]; anim = &vortanims_ptr->field_7; } else { script_byte_vars.bvar_36 |= 0x80; pers_list[kPersVort3].area = 0; pers_list[kPersVort].flags = pers_list[kPersVort3].flags; pers = &pers_list[kPersVort]; anim = &vortanims_ptr->field_A; } pers->area = 0; next_vorts_cmd = 0; for (i = 0; i < ADJACENT_AREAS_MAX; i++) { if (adjacent_areas[i].zone == script_byte_vars.zone_index) { next_vorts_cmd = 0xA016; next_vorts_ticks = Swap16(script_word_vars.timer_ticks2) + 5; pers->area = adjacent_areas[i].area; } } vort_ptr = pers; zone_spots[(pers->flags & 15) - 1].flags &= ~SPOTFLG_80; selectPerson(0); animateSpot(anim); IFGM_StopSample(); script_byte_vars.bvar_36 &= 0x80; return 0; } /* Vorts left the room */ uint16 CMD_16_VortGone(void) { vort_ptr->area = 0; next_vorts_cmd = 0; return 0; } /* Take away Aspirant's item and bounce it to the inventory */ uint16 CMD_17_LootAspirantsItem() { LootAspirantsItem(); return ScriptRerun; } /* Aspirant walking out of the room */ uint16 CMD_18_AspirantLeave(void) { /*TODO: check me*/ static const animdesc_t anim33 = {ANIMFLG_USESPOT | 33, { { 0, 0 } }}; popDirtyRects(DirtyRectSprite); popDirtyRects(DirtyRectText); aspirant_ptr->area = 0; script_word_vars.next_aspirant_cmd = BE(0); if ((aspirant_ptr->flags & PERSFLG_40) == 0) { aspirant_spot->flags &= ~SPOTFLG_80; selectPerson(script_byte_vars.aspirant_pers_ofs); script_byte_vars.aspirant_flags = 0; animateSpot(&anim33); } return 0; } /* Aspirant walking into the room */ uint16 CMD_19_AspirantAppear(void) { /*TODO: check me*/ static const animdesc_t anim23 = {ANIMFLG_USESPOT | 23, { { 0, 0 } }}; popDirtyRects(DirtyRectSprite); aspirant_ptr->area = script_byte_vars.zone_area; script_word_vars.next_aspirant_cmd = BE(0xA018); /*leave*/ script_byte_vars.check_used_commands = 3; script_byte_vars.used_commands = 0; selectPerson(script_byte_vars.aspirant_pers_ofs); animateSpot(&anim23); blitSpritesToBackBuffer(); drawPersons(); cga_BackBufferToRealFull(); if (script_byte_vars.aspirant_flags == 5) { the_command = 0xC029; script_byte_vars.aspirant_flags = 0; return ScriptRerun; } if (script_byte_vars.aspirant_flags == 6) { the_command = 0xC165; script_byte_vars.aspirant_flags = 0; return ScriptRerun; } return 0; } /* Aspirant is dead */ uint16 CMD_1A_AspirantDie(void) { /*TODO: check me, unused in game?*/ script_byte_vars.bvar_45 = 0; zone_spots[5].flags = SPOTFLG_40 | SPOTFLG_10 | SPOTFLG_2 | SPOTFLG_1; script_word_vars.next_aspirant_cmd = 0; DrawDeathAnim(); return 0; } /* Show Holo screen anim and speech */ uint16 CMD_1B_Holo(void) { byte x, y; uint16 num; byte *msg; IFGM_PlaySample(225); x = found_spot->sx; y = found_spot->sy; playAnim(42, x + 4 / 4, y + 6); num = 321 + ((Swap16(script_word_vars.timer_ticks2) < 60 * 60) ? 0 : 4) + (script_byte_vars.rand_value % 4); msg = seekToString(diali_data, num); cur_dlg_index = cur_str_index; /*TODO: useless?*/ if (x < 140 / 4) drawPersonBubble(x + 32 / 4, y - 40, SPIKE_DNLEFT | 20, msg); else drawPersonBubble(x - 92 / 4, y - 40, SPIKE_DNRIGHT | 20, msg); IFGM_PlaySfx(0); playAnim(43, x, y); promptWait(); popDirtyRects(DirtyRectBubble); IFGM_PlaySample(225); playAnim(45, x, y); return 0; } /* Turkey walking into the room */ uint16 CMD_1E_TurkeyAppear(void) { /*TODO: check me*/ pers_list[kPersTurkey].area = script_byte_vars.zone_area; selectPerson(PersonOffset(kPersTurkey)); animateSpot(&turkeyanims_ptr->field_1); next_turkey_cmd = 0xA01F; blitSpritesToBackBuffer(); drawPersons(); cga_BackBufferToRealFull(); next_turkey_ticks = Swap16(script_word_vars.timer_ticks2) + 5; return 0; } /* Turkey leaving the room */ uint16 CMD_1F_TurkeyLeave(void) { uint16 i; animdesc_t *anim; pers_t *pers; pers = &pers_list[kPersTurkey]; anim = &turkeyanims_ptr->field_4; pers->area = 0; next_turkey_cmd = 0; for (i = 0; i < ADJACENT_AREAS_MAX; i++) { if (adjacent_areas[i].zone == script_byte_vars.zone_index) { next_turkey_cmd = 0xA020; next_turkey_ticks = Swap16(script_word_vars.timer_ticks2) + 5; pers->area = adjacent_areas[i].area; } } zone_spots[(pers->flags & 15) - 1].flags &= ~SPOTFLG_80; selectPerson(PersonOffset(kPersTurkey)); animateSpot(anim); return 0; } /* Turkey left the room */ uint16 CMD_20_TurkeyGone(void) { pers_list[kPersTurkey].area = 0; next_turkey_cmd = 0; return 0; } /* Talk to Vorts */ uint16 CMD_21_VortTalk(void) { byte x, y; uint16 num; byte *msg; if (script_byte_vars.rand_value >= 85) num = 6; else if (script_byte_vars.rand_value >= 170) num = 7; else num = 35; msg = seekToString(diali_data, num); cur_dlg_index = cur_str_index; /*TODO: useless?*/ x = found_spot->sx; y = found_spot->sy; if (x < 140 / 4) drawPersonBubble(found_spot->ex, y - 40, SPIKE_DNLEFT | 20, msg); else drawPersonBubble(x - 80 / 4, y - 40, SPIKE_DNRIGHT | 20, msg); IFGM_PlaySfx(0); promptWait(); popDirtyRects(DirtyRectBubble); return 0; } uint16 CMD_22_(void) { ActionForPersonChoice(menu_commands_22); return ScriptRerun; } uint16 CMD_23_(void) { ActionForPersonChoice(menu_commands_23); return ScriptRerun; } uint16 CMD_24_(void) { ActionForPersonChoice(menu_commands_24); return ScriptRerun; } /* Load save file */ uint16 CMD_25_LoadGame(void) { IFGM_StopSample(); if (loadScena()) the_command = 0x918F; /*error loading*/ else the_command = 0x90AA; return ScriptRerun; } /* Write save file */ uint16 CMD_26_SaveGame(void) { if (saveScena()) the_command = 0x9190; /*error saving*/ else the_command = 0x90AA; return ScriptRerun; } typedef uint16 (*cmdhandler_t)(void); cmdhandler_t command_handlers[] = { 0, CMD_1_RoomObjects, CMD_2_PsiPowers, CMD_3_Possessions, CMD_4_EnergyLevel, CMD_5_Wait, CMD_6_Load, CMD_7_Save, CMD_8_Timer, CMD_TRAP, CMD_A_PsiSolarEyes, CMD_B_PsiStickyFingers, CMD_C_PsiKnowMind, CMD_D_PsiBrainwarp, CMD_E_PsiZoneScan, CMD_F_PsiPsiShift, CMD_10_PsiExtremeViolence, /*10*/ CMD_11_PsiTuneIn, CMD_12_, CMD_13_ActivateFountain, CMD_14_VortAppear, CMD_15_VortLeave, CMD_16_VortGone, CMD_17_LootAspirantsItem, CMD_18_AspirantLeave, CMD_19_AspirantAppear, CMD_1A_AspirantDie, CMD_1B_Holo, CMD_TRAP, CMD_TRAP, CMD_1E_TurkeyAppear, CMD_1F_TurkeyLeave, CMD_20_TurkeyGone, /*20*/ CMD_21_VortTalk, CMD_22_, CMD_23_, CMD_24_, CMD_25_LoadGame, CMD_26_SaveGame }; #define MAX_CMD_HANDLERS (sizeof(command_handlers) / sizeof(command_handlers[0])) cmdhandler_t script_handlers[] = { 0, SCR_1_AspirantItemTrade, SCR_2_RudeAspirantTrade, SCR_3_DrawItemBox, SCR_4_StealZapstik, SCR_5_DrawPortraitLiftRight, SCR_6_DrawPortraitLiftLeft, SCR_7_DrawPortraitLiftDown, SCR_8_DrawPortraitLiftUp, SCR_9_DrawPortrait, SCR_A_DrawPortrait, /*TODO: same as SCR_9_DrawPortrait , unused*/ SCR_B_DrawPortraitTwistEffect, SCR_C_DrawPortraitArcEffect, SCR_D_DrawPortraitDotEffect, SCR_E_DrawPortraitZoomIn, SCR_F_Unused, SCR_10_DrawPortraitZoomed, /*10*/ SCR_11_DrawRoomObject, SCR_12_Chain, SCR_13_RedrawRoomStatics, SCR_14_DrawDesc, SCR_15_SelectSpot, SCR_16_DrawDeathAnim, SCR_17_DrawPersonThoughtBubbleDialog, SCR_18_AnimPortrait, SCR_19_HidePortraitLiftLeft, SCR_1A_HidePortraitLiftRight, SCR_1B_HidePortraitLiftUp, SCR_1C_HidePortraitLiftDown, SCR_1D_HidePortrait, /*TODO: same as SCR_23_HidePortrait , unused*/ SCR_1E_HidePortraitTwist, SCR_1F_HidePortraitArc, SCR_20_HidePortraitDots, /*20*/ SCR_21_HidePortrait, /*TODO: same as SCR_23_HidePortrait , unused*/ SCR_22_HidePortraitShatter, SCR_23_HidePortrait, SCR_24_PopAllPortraits, SCR_25_ChangeZoneOnly, SCR_26_GameOver, SCR_27_DrawGaussBubble, SCR_28_MenuLoop, SCR_29_DialiTextBox, SCR_2A_PopDialogRect, SCR_2B_PopAllBubbles, SCR_2C_Wait4, SCR_2D_Wait, SCR_2E_promptWait, SCR_2F_LootAspirantsItem, SCR_30_Fight, /*30*/ SCR_31_Fight2, SCR_32_FightWin, SCR_33_Jump, SCR_34_Call, SCR_35_Ret, SCR_36_ChangeZone, SCR_37_desciTextBox, SCR_38_PlayAnim, SCR_39_AnimRoomDoorOpen, SCR_3A_AnimRoomDoorClose, SCR_3B_MathExpr, SCR_3C_CondExpr, SCR_3D_ActionsMenu, SCR_3E_TheWallAdvance, SCR_3F_HidePortrait, /*TODO: same as SCR_23_HidePortrait , unused*/ SCR_40_PopAllTextBoxes, /*40*/ SCR_41_LiftHand, SCR_42_LoadZone, SCR_43_RefreshZone, SCR_44_BackBufferToScreen, SCR_45_DeProfundisRoomEntry, SCR_46_DeProfundisLowerHook, SCR_47_DeProfundisRiseMonster, SCR_48_DeProfundisLowerMonster, SCR_49_DeProfundisRiseHook, SCR_4A_Unused, SCR_4B_ProtoDropZapstik, SCR_4C_DrawPersons, SCR_4D_PriorityCommand, SCR_4E_BounceCurrentItemToRoom, SCR_4F_BounceCurrentItemToInventory, SCR_50_BounceItemToInventory, /*50*/ SCR_51_SkullTraderItemTrade, SCR_52_RefreshSpritesData, SCR_53_FindInvItem, SCR_54_DotFadeRoom, SCR_55_DrawRoomItemsIndicator, SCR_56_MorphRoom98, SCR_57_ShowCharacterSprite, SCR_58_DrawCharacterSprite, SCR_59_blitSpritesToBackBuffer, SCR_5A_SelectPalette, SCR_5B_TheEnd, SCR_5C_ClearInventory, SCR_5D_DropWeapons, SCR_5E_SelectTempPalette, SCR_5F_DrawRoomObjectBack, SCR_60_ReviveCadaver, /*60*/ SCR_61_drawPersonBubbleDialog, SCR_62_PsiReaction, SCR_63_LiftSpot6, SCR_64_DrawBoxAroundSpot, SCR_65_DeProfundisMovePlatform, SCR_66_DeProfundisRideToExit, SCR_67_Unused, SCR_68_PlaySfx, SCR_69_playSound, SCR_6A_Unused, }; #define MAX_SCR_HANDLERS (sizeof(script_handlers) / sizeof(script_handlers[0])) #ifdef DEBUG_SCRIPT int16 runscr_reentr = 0; int16 runcmd_reentr = 0; #endif /* Run script routine */ uint16 RunScript(byte *code) { uint16 status = ScriptContinue; #ifdef DEBUG_SCRIPT runscr_reentr += 1; #endif script_ptr = code; while (script_ptr != script_end_ptr) { byte opcode = *script_ptr; #ifdef DEBUG_SCRIPT { FILE *f = fopen(DEBUG_SCRIPT_LOG, "at"); if (f) { uint16 offs = (script_ptr - templ_data) & 0xFFFF; fprintf(f, "%04X: %02X\n", offs, opcode); fclose(f); } } #endif #ifdef DEBUG_QUEST if (script_ptr - templ_data == 0x4F) { /*manipulate rand_value to get a quest item we need*/ script_byte_vars.rand_value = DEBUG_QUEST; } #endif if (opcode == 0 || opcode >= 107) break; status = script_handlers[opcode](); if (g_vm->_shouldRestart) return status; if (g_vm->_prioritycommand_1) return status; if (status != ScriptContinue || g_vm->_shouldQuit) break; } #ifdef DEBUG_SCRIPT runscr_reentr -= 1; #endif return status; } /* Get script routine */ byte *getScriptSubroutine(uint16 index) { return seekToEntry(templ_data, index, &script_end_ptr); } /* Run script command */ uint16 runCommand(void) { uint16 res; uint16 cmd; again:; res = 0; if (the_command == 0) return 0; #ifdef DEBUG_SCRIPT { FILE *f = fopen(DEBUG_SCRIPT_LOG, "at"); if (f) { fprintf(f, "\nrunCommand 0x%04X rc: %d rs: %d\n", the_command, runcmd_reentr, runscr_reentr); fclose(f); } } runcmd_reentr += 1; #endif cmd = the_command & 0x3FF; switch (the_command & 0xF000) { case 0: /*TODO what kind of call is this?*/ res = RunScript(templ_data + the_command); break; case 0x9000: drawMessage(seekToString(desci_data, cmd), CGA_SCREENBUFFER); break; case 0xA000: case 0xB000: debug("Command: $%X 0x%X", the_command, cmd); res = command_handlers[cmd](); break; case 0xF000: /*restore sp from keep_sp then run script*/ /*currently only supposed to work correctly from the SCR_4D_PriorityCommand handler*/ debug("Restore: $%X 0x%X", the_command, cmd); /*TODO("SCR_RESTORE\n");*/ /*fall through*/ default: res = RunScript(getScriptSubroutine(cmd - 1)); } #ifdef DEBUG_SCRIPT runcmd_reentr -= 1; #endif #ifdef DEBUG_SCRIPT { FILE *f = fopen(DEBUG_SCRIPT_LOG, "at"); if (f) { fprintf(f, "\n"); fclose(f); } } #endif if (g_vm->_shouldRestart) return runCommandKeepSp(); if (g_vm->_prioritycommand_1 && !(g_vm->_prioritycommand_2)) return res; if (g_vm->_prioritycommand_1 && g_vm->_prioritycommand_2) return runCommandKeepSp(); /*TODO: this is pretty hacky, original code manipulates the stack to discard old script invocation*/ if (res == ScriptRerun) goto again; return res; } uint16 runCommandKeepSp(void) { /*keep_sp = sp;*/ g_vm->_prioritycommand_1 = false; g_vm->_prioritycommand_2 = false; if (g_vm->_shouldRestart) return RUNCOMMAND_RESTART; return runCommand(); } } // End of namespace Chamber