int Router::faceXY(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, int16 target_x, int16 target_y) { uint8 target_dir = 0; // If this is the start of the turn, get the mega's current feet // coords + the required direction ObjectLogic obLogic(ob_logic); if (obLogic.getLooping() == 0) { ObjectMega obMega(ob_mega); target_dir = whatTarget(obMega.getFeetX(), obMega.getFeetY(), target_x, target_y); } return doFace(ob_logic, ob_graph, ob_mega, ob_walkdata, target_dir); }
int Router::walkToTalkToMega(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, uint32 megaId, uint32 separation) { ObjectMega obMega(ob_mega); int16 target_x = 0; int16 target_y = 0; uint8 target_dir = 0; // If this is the start of the walk, calculate the route. ObjectLogic obLogic(ob_logic); if (obLogic.getLooping() == 0) { assert(_vm->_resman->fetchType(megaId) == GAME_OBJECT); // Call the base script. This is the graphic/mouse service // call, and will set _engineMega to the ObjectMega of mega we // want to route to. _vm->_logic->runResScript(megaId, 3); ObjectMega targetMega(_vm->_logic->getEngineMega()); // Stand exactly beside the mega, ie. at same y-coord target_y = targetMega.getFeetY(); int scale = obMega.calcScale(); int mega_separation = (separation * scale) / 256; debug(4, "Target is at (%d, %d), separation %d", targetMega.getFeetX(), targetMega.getFeetY(), mega_separation); if (targetMega.getFeetX() < obMega.getFeetX()) { // Target is left of us, so aim to stand to their // right. Face down_left target_x = targetMega.getFeetX() + mega_separation; target_dir = 5; } else { // Ok, must be right of us so aim to stand to their // left. Face down_right. target_x = targetMega.getFeetX() - mega_separation; target_dir = 3; } } return doWalk(ob_logic, ob_graph, ob_mega, ob_walkdata, target_x, target_y, target_dir); }
int Router::megaTableAnimate(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *animTable, bool reverse) { int32 animRes = 0; // If this is the start of the anim, read the anim table to get the // appropriate anim resource ObjectLogic obLogic(ob_logic); if (obLogic.getLooping() == 0) { ObjectMega obMega(ob_mega); // Appropriate anim resource is in 'table[direction]' animRes = READ_LE_UINT32(animTable + 4 * obMega.getCurDir()); } return doAnimate(ob_logic, ob_graph, animRes, reverse); }
void Router::standAt(byte *ob_graph, byte *ob_mega, int32 x, int32 y, int32 dir) { assert(dir >= 0 && dir <= 7); ObjectGraphic obGraph(ob_graph); ObjectMega obMega(ob_mega); // Set up the stand frame & set the mega's new direction obMega.setFeetX(x); obMega.setFeetY(y); obMega.setCurDir(dir); // Mega-set animation file obGraph.setAnimResource(obMega.getMegasetRes()); // Dir + first stand frame (always frame 96) obGraph.setAnimPc(dir + 96); }
int Router::doFace(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, uint8 target_dir) { int16 target_x = 0; int16 target_y = 0; // If this is the start of the turn, get the mega's current feet // coords + the required direction ObjectLogic obLogic(ob_logic); if (obLogic.getLooping() == 0) { assert(target_dir <= 7); ObjectMega obMega(ob_mega); target_x = obMega.getFeetX(); target_y = obMega.getFeetY(); } return doWalk(ob_logic, ob_graph, ob_mega, ob_walkdata, target_x, target_y, target_dir); }
int Router::faceMega(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, uint32 megaId) { uint8 target_dir = 0; // If this is the start of the walk, decide where to walk to. ObjectLogic obLogic(ob_logic); if (obLogic.getLooping() == 0) { assert(_vm->_resman->fetchType(megaId) == GAME_OBJECT); // Call the base script. This is the graphic/mouse service // call, and will set _engineMega to the ObjectMega of mega we // want to turn to face. _vm->_logic->runResScript(megaId, 3); ObjectMega obMega(ob_mega); ObjectMega targetMega(_vm->_logic->getEngineMega()); target_dir = whatTarget(obMega.getFeetX(), obMega.getFeetY(), targetMega.getFeetX(), targetMega.getFeetY()); } return doFace(ob_logic, ob_graph, ob_mega, ob_walkdata, target_dir); }
uint32 Sword2Engine::restoreFromBuffer(byte *buffer, uint32 size) { Common::MemoryReadStream readS(buffer, size); // Calc checksum & check that aginst the value stored in the header if (readS.readUint32LE() != calcChecksum(buffer + 4, size - 4)) { free(buffer); return SR_ERR_INCOMPATIBLE; } readS.seek(SAVE_DESCRIPTION_LEN, SEEK_CUR); // Check savegame against length of current global variables resource // This would most probably be trapped by the checksum test anyway, // but it doesn't do any harm to check this as well. // Historical note: During development, earlier savegames would often // be shorter than the current expected length. if (readS.readUint32LE() != _resman->fetchLen(1)) { free(buffer); return SR_ERR_INCOMPATIBLE; } byte *globalVars = _resman->openResource(1); byte *objectHub = _resman->openResource(CUR_PLAYER_ID) + ResHeader::size(); uint32 screenId = readS.readUint32LE(); uint32 runListId = readS.readUint32LE(); uint32 feetX = readS.readUint32LE(); uint32 feetY = readS.readUint32LE(); uint32 musicId = readS.readUint32LE(); // Trash all resources from memory except player object & global vars _resman->killAll(false); _logic->resetKillList(); readS.read(objectHub, ObjectHub::size()); readS.read(_logic->_saveLogic, ObjectLogic::size()); readS.read(_logic->_saveGraphic, ObjectGraphic::size()); readS.read(_logic->_saveMega, ObjectMega::size()); // Fill out the player object structures from the savegame structures. // Also run the appropriate scripts to set up George's anim tables and // walkdata, and Nico's anim tables. // Script no. 8 - 'george_savedata_return' calls fnGetPlayerSaveData _logic->runResScript(CUR_PLAYER_ID, 8); // Script no. 14 - 'set_up_nico_anim_tables' _logic->runResScript(CUR_PLAYER_ID, 14); // Which megaset was the player at the time of saving? ObjectMega obMega(_logic->_saveMega); uint32 scriptNo = 0; switch (obMega.getMegasetRes()) { case 36: // GeoMega: scriptNo = 9; // script no.9 - 'player_is_george' break; case 2003: // GeoMegaB: scriptNo = 13; // script no.13 - 'player_is_georgeB' break; case 1366: // NicMegaA: scriptNo = 11; // script no.11 - 'player_is_nicoA' break; case 1437: // NicMegaB: scriptNo = 12; // script no.12 - 'player_is_nicoB' break; case 1575: // NicMegaC: scriptNo = 10; // script no.10 - 'player_is_nicoC' break; } _logic->runResScript(CUR_PLAYER_ID, scriptNo); // Copy variables from savegame buffer to memory readS.read(globalVars, _resman->fetchLen(1)); _resman->closeResource(CUR_PLAYER_ID); _resman->closeResource(1); free(buffer); int32 pars[2]; pars[0] = screenId; pars[1] = 1; _logic->fnInitBackground(pars); ScreenInfo *screenInfo = _screen->getScreenInfo(); // So palette not restored immediately after control panel - we want to // fade up instead! screenInfo->new_palette = 99; // These need setting after the defaults get set in fnInitBackground. // Remember that these can change through the game, so need saving & // restoring too. screenInfo->feet_x = feetX; screenInfo->feet_y = feetY; // Start the new run list _logic->expressChangeSession(runListId); // Force in the new scroll position, so unsightly scroll-catch-up does // not occur when screen first draws after returning from restore panel // Set the screen record of player position - ready for setScrolling() screenInfo->player_feet_x = obMega.getFeetX(); screenInfo->player_feet_y = obMega.getFeetY(); // if this screen is wide, recompute the scroll offsets now if (screenInfo->scroll_flag) _screen->setScrolling(); // Any music required will be started after we've returned from // restoreControl() - see systemMenuMouse() in mouse.cpp! // Restart any looping music. Originally this was - and still is - done // in systemMenuMouse(), but with ScummVM we have other ways of // restoring savegames so it's easier to put it here as well. if (musicId) { pars[0] = musicId; pars[1] = FX_LOOP; _logic->fnPlayMusic(pars); } else _logic->fnStopMusic(NULL); return SR_OK; }
void Logic::locateTalker(int32 *params) { // params: 0 pointer to ob_graphic // 1 pointer to ob_speech // 2 pointer to ob_logic // 3 pointer to ob_mega // 4 encoded text number // 5 wav res id // 6 anim res id // 7 pointer to anim table // 8 animation mode 0 lip synced, // 1 just straight animation if (!_animId) { // There is no animation. Assume it's voice-over text, and put // it at the bottom of the screen. _textX = 320; _textY = 400; return; } byte *file = _vm->_resman->openResource(_animId); // '0' means 1st frame CdtEntry cdt_entry; FrameHeader frame_head; cdt_entry.read(_vm->fetchCdtEntry(file, 0)); frame_head.read(_vm->fetchFrameHeader(file, 0)); // Note: This part of the code is quite similar to registerFrame(). if (cdt_entry.frameType & FRAME_OFFSET) { // The frame has offsets, i.e. it's a scalable mega frame ObjectMega obMega(decodePtr(params[S_OB_MEGA])); uint16 scale = obMega.calcScale(); // Calc suitable centre point above the head, based on scaled // height // just use 'feet_x' as centre _textX = obMega.getFeetX(); // Add scaled y-offset to feet_y coord to get top of sprite _textY = obMega.getFeetY() + (cdt_entry.y * scale) / 256; } else { // It's a non-scaling anim - calc suitable centre point above // the head, based on scaled width // x-coord + half of width _textX = cdt_entry.x + frame_head.width / 2; _textY = cdt_entry.y; } _vm->_resman->closeResource(_animId); // Leave space above their head _textY -= GAP_ABOVE_HEAD; // Adjust the text coords for RDSPR_DISPLAYALIGN ScreenInfo *screenInfo = _vm->_screen->getScreenInfo(); _textX -= screenInfo->scroll_offset_x; _textY -= screenInfo->scroll_offset_y; }
void Screen::registerFrame(byte *ob_mouse, byte *ob_graph, byte *ob_mega, BuildUnit *build_unit) { ObjectGraphic obGraph(ob_graph); ObjectMega obMega(ob_mega); assert(obGraph.getAnimResource()); byte *file = _vm->_resman->openResource(obGraph.getAnimResource()); AnimHeader anim_head; CdtEntry cdt_entry; FrameHeader frame_head; anim_head.read(_vm->fetchAnimHeader(file)); cdt_entry.read(_vm->fetchCdtEntry(file, obGraph.getAnimPc())); frame_head.read(_vm->fetchFrameHeader(file, obGraph.getAnimPc())); // update player graphic details for on-screen debug info if (_vm->_logic->readVar(ID) == CUR_PLAYER_ID) { _vm->_debugger->_graphType = obGraph.getType(); _vm->_debugger->_graphAnimRes = obGraph.getAnimResource(); // counting 1st frame as 'frame 1' _vm->_debugger->_graphAnimPc = obGraph.getAnimPc() + 1; _vm->_debugger->_graphNoFrames = anim_head.noAnimFrames; } // fill in the BuildUnit structure for this frame build_unit->anim_resource = obGraph.getAnimResource(); build_unit->anim_pc = obGraph.getAnimPc(); build_unit->layer_number = 0; // Affected by shading mask? if (obGraph.getType() & SHADED_SPRITE) build_unit->shadingFlag = true; else build_unit->shadingFlag = false; // Check if this frame has offsets ie. this is a scalable mega frame int scale = 0; if (cdt_entry.frameType & FRAME_OFFSET) { scale = obMega.calcScale(); // calc final render coordinates (top-left of sprite), based // on feet coords & scaled offsets // add scaled offsets to feet coords build_unit->x = obMega.getFeetX() + (cdt_entry.x * scale) / 256; build_unit->y = obMega.getFeetY() + (cdt_entry.y * scale) / 256; // Work out new width and height. Always divide by 256 after // everything else, to maintain accurary build_unit->scaled_width = ((scale * frame_head.width) / 256); build_unit->scaled_height = ((scale * frame_head.height) / 256); } else { // It's a non-scaling anim. Get render coords for sprite, from cdt build_unit->x = cdt_entry.x; build_unit->y = cdt_entry.y; // Get width and height build_unit->scaled_width = frame_head.width; build_unit->scaled_height = frame_head.height; } // either 0 or required scale, depending on whether 'scale' computed build_unit->scale = scale; // calc the bottom y-coord for sorting purposes build_unit->sort_y = build_unit->y + build_unit->scaled_height - 1; if (ob_mouse) { // passed a mouse structure, so add to the _mouseList _vm->_mouse->registerMouse(ob_mouse, build_unit); } _vm->_resman->closeResource(obGraph.getAnimResource()); }
int Router::doWalk(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, int16 target_x, int16 target_y, uint8 target_dir) { ObjectLogic obLogic(ob_logic); ObjectGraphic obGraph(ob_graph); ObjectMega obMega(ob_mega); // If this is the start of the walk, calculate the route. if (obLogic.getLooping() == 0) { // If we're already there, don't even bother allocating // memory and calling the router, just quit back & continue // the script! This avoids an embarassing mega stand frame // appearing for one cycle when we're already in position for // an anim eg. repeatedly clicking on same object to repeat // an anim - no mega frame will appear in between runs of the // anim. if (obMega.getFeetX() == target_x && obMega.getFeetY() == target_y && obMega.getCurDir() == target_dir) { _vm->_logic->writeVar(RESULT, 0); return IR_CONT; } assert(target_dir <= 8); obMega.setWalkPc(0); // Set up mem for _walkData in route_slots[] & set mega's // 'route_slot_id' accordingly allocateRouteMem(); int32 route = routeFinder(ob_mega, ob_walkdata, target_x, target_y, target_dir); // 0 = can't make route to target // 1 = created route // 2 = zero route but may need to turn if (route != 1 && route != 2) { freeRouteMem(); _vm->_logic->writeVar(RESULT, 1); return IR_CONT; } // Walk is about to start obMega.setIsWalking(1); obLogic.setLooping(1); obGraph.setAnimResource(obMega.getMegasetRes()); } else if (_vm->_logic->readVar(EXIT_FADING) && _vm->_screen->getFadeStatus() == RDFADE_BLACK) { // Double clicked an exit, and the screen has faded down to // black. Ok, that's it. Back to script and change screen. // We have to clear te EXIT_CLICK_ID variable in case there's a // walk instruction on the new screen, or it'd be cut short. freeRouteMem(); obLogic.setLooping(0); obMega.setIsWalking(0); _vm->_logic->writeVar(EXIT_CLICK_ID, 0); _vm->_logic->writeVar(RESULT, 0); return IR_CONT; } // Get pointer to walkanim & current frame position WalkData *walkAnim = getRouteMem(); int32 walk_pc = obMega.getWalkPc(); // If stopping the walk early, overwrite the next step with a // slow-out, then finish if (_vm->_logic->checkEventWaiting() && walkAnim[walk_pc].step == 0 && walkAnim[walk_pc + 1].step == 1) { // At the beginning of a step earlySlowOut(ob_mega, ob_walkdata); } // Get new frame of walk obGraph.setAnimPc(walkAnim[walk_pc].frame); obMega.setCurDir(walkAnim[walk_pc].dir); obMega.setFeetX(walkAnim[walk_pc].x); obMega.setFeetY(walkAnim[walk_pc].y); // Is the NEXT frame is the end-marker (512) of the walk sequence? if (walkAnim[walk_pc + 1].frame != 512) { // No, it wasn't. Increment the walk-anim frame number and // come back next cycle. obMega.setWalkPc(obMega.getWalkPc() + 1); return IR_REPEAT; } // We have reached the end-marker, which means we can return to the // script just as the final (stand) frame of the walk is set. freeRouteMem(); obLogic.setLooping(0); obMega.setIsWalking(0); // If George's walk has been interrupted to run a new action script for // instance or Nico's walk has been interrupted by player clicking on // her to talk // There used to be code here for checking if two megas were colliding, // but it had been commented out, and it was only run if a function // that always returned zero returned non-zero. if (_vm->_logic->checkEventWaiting()) { _vm->_logic->startEvent(); _vm->_logic->writeVar(RESULT, 1); return IR_TERMINATE; } _vm->_logic->writeVar(RESULT, 0); // CONTINUE the script so that RESULT can be checked! Also, if an anim // command follows the fnWalk command, the 1st frame of the anim (which // is always a stand frame itself) can replace the final stand frame of // the walk, to hide the slight difference between the shrinking on the // mega frames and the pre-shrunk anim start-frame. return IR_CONT; }