private bool FnGetTo(uint targetPlaceId, uint mode, uint c) { _compact.Core.upFlag = (ushort)mode; // save mode for action script _compact.Core.mode += 4; // next level up Compact cpt = _skyCompact.FetchCpt(_compact.Core.place); if (cpt == null) { // TODO: warning("can't find _compact's getToTable. Place compact is NULL"); return(false); } var raw = _skyCompact.FetchCptRaw(cpt.Core.getToTableId); if (raw == null) { //TODO: warning("Place compact's getToTable is NULL"); return(false); } var getToTable = new UShortAccess(raw, 0); while (getToTable.Value != targetPlaceId) { getToTable.Offset += 4; } // get new script SkyCompact.GetSub(_compact, _compact.Core.mode).Field = getToTable[1]; SkyCompact.GetSub(_compact, (ushort)(_compact.Core.mode + 2)).Field = 0; return(false); // drop out of script }
private void VerticalMask() { if (_sprWidth == 0) { return; } var startGridOfs = (int)((_sprY + _sprHeight - 1) * GridX + _sprX); var startScreenPtr = (int)((_sprY + _sprHeight - 1) * GridH * GameScreenWidth + _sprX * GridW); for (uint layerCnt = Logic.LAYER_1_ID; layerCnt <= Logic.LAYER_3_ID; layerCnt++) { var gridOfs = startGridOfs; var screenPtr = startScreenPtr; for (uint widCnt = 0; widCnt < _sprWidth; widCnt++) { // x_loop var nLayerCnt = layerCnt; while (Logic.ScriptVariables[(int)(nLayerCnt + 3)] != 0) { var scrGrid = new UShortAccess(SkyEngine.ItemList[Logic.ScriptVariables[(int)(layerCnt + 3)]], 0); if (scrGrid[gridOfs] != 0) { VertMaskSub(scrGrid, gridOfs, screenPtr, layerCnt); break; } nLayerCnt++; } // next_x: screenPtr += GridW; gridOfs++; } } }
private bool FnResetId(uint id, uint resetBlock, uint c) { // used when a mega is to be restarted // eg - when a smaller mega turn to larger // - a mega changes rooms... var cpt = _skyCompact.FetchCpt((ushort)id); var rst = new UShortAccess(_skyCompact.FetchCptRaw((ushort)resetBlock), 0); if (cpt == null) { // TODO: warning("fnResetId(): Compact %d (id) == NULL", id); return(true); } if (rst == null) { // TODO: warning("fnResetId(): Compact %d (resetBlock) == NULL", resetBlock); return(true); } ushort off; while ((off = rst[0]) != 0xffff) { rst.Offset += 2; _skyCompact.GetCompactElem(cpt, off).Field = rst[0]; rst.Offset += 2; } return(true); }
private void VertMaskSub(UShortAccess grid, int gridOfs, int screenPtr, uint layerId) { for (var cntx = 0; cntx < _sprHeight; cntx++) { // start_x | block_loop if (grid[gridOfs] != 0) { if ((grid[gridOfs] & 0x8000) == 0) { var gridVal = grid[gridOfs] - 1; gridVal *= GridW * GridH; var dataSrc = new ByteAccess(SkyEngine.ItemList[Logic.ScriptVariables[(int)layerId]], gridVal); var dataTrg = screenPtr; for (var grdCntY = 0; grdCntY < GridH; grdCntY++) { for (var grdCntX = 0; grdCntX < GridW; grdCntX++) { if (dataSrc[grdCntX] != 0) { Current[dataTrg + grdCntX] = dataSrc[grdCntX]; } } dataSrc.Offset += GridW; dataTrg += GameScreenWidth; } } // dummy_end: screenPtr -= GridH * GameScreenWidth; gridOfs -= GridX; } else { return; } } // next_x }
private void DoSprites(byte layer) { ushort drawListNum = Logic.DRAW_LIST_NO; while (Logic.ScriptVariables[drawListNum] != 0) { // std sp loop var idNum = Logic.ScriptVariables[drawListNum]; drawListNum++; var drawList = new UShortAccess(_skyCompact.FetchCptRaw((ushort)idNum), 0); while (drawList[0] != 0) { // new_draw_list: while ((drawList[0] != 0) && (drawList[0] != 0xFFFF)) { // back_loop: // not_new_list var spriteData = _skyCompact.FetchCpt(drawList[0]); drawList.Offset += 2; if (((spriteData.Core.status & (1 << layer)) != 0) && (spriteData.Core.screen == Logic.ScriptVariables[Logic.SCREEN])) { var toBeDrawn = SkyEngine.ItemList[spriteData.Core.frame >> 6]; if (toBeDrawn == null) { // TODO: debug(9, "Spritedata %d not loaded", spriteData.frame >> 6); spriteData.Core.status = 0; } else { DrawSprite(toBeDrawn, spriteData); if (layer == Back) { VerticalMask(); } if ((spriteData.Core.status & 8) != 0) { VectorToGame(0x81); } else { VectorToGame(1); } } } } while (drawList[0] == 0xFFFF) { drawList = new UShortAccess(_skyCompact.FetchCptRaw(drawList[1]), 0); } } } }
public void Engine() { do { var raw = _skyCompact.FetchCptRaw((ushort)_scriptVariables[LOGIC_LIST_NO]); var logicList = new UShortAccess(raw, 0); ushort id; while ((id = logicList[0]) != 0) { logicList.Offset += 2; // 0 means end of list if (id == 0xffff) { // Change logic data address raw = _skyCompact.FetchCptRaw(logicList[0]); logicList = new UShortAccess(raw, 0); continue; } _scriptVariables[CUR_ID] = id; _compact = _skyCompact.FetchCpt(id); // check the id actually wishes to be processed if ((_compact.Core.status & (1 << 6)) == 0) { continue; } // ok, here we process the logic bit system if ((_compact.Core.status & (1 << 7)) != 0) { _skyGrid.RemoveObjectFromWalk(_compact); } Debug.Instance.Logic(_compact.Core.logic); _logicTable[_compact.Core.logic](); if ((_compact.Core.status & (1 << 7)) != 0) { _skyGrid.ObjectToWalk(_compact); } // a sync sent to the compact is available for one cycle // only. that cycle has just ended so remove the sync. // presumably the mega has just reacted to it. _compact.Core.sync = 0; } // usually this loop is run only once, it'll only be run a second time if the game // script just asked the user to enter a copy protection code. // this is done to prevent the copy protection screen from flashing up. // (otherwise it would be visible for 1/50 second) } while (CheckProtection()); }
public void FnTextModule(uint textInfoId, uint textNo) { FnSetFont(1); var msgData = new UShortAccess(_skyCompact.FetchCptRaw((ushort)textInfoId), 0); var textId = LowTextManager(textNo, msgData[1], msgData[2], 209, false); Logic.ScriptVariables[Logic.RESULT] = textId.CompactNum; var textCompact = _skyCompact.FetchCpt(textId.CompactNum); textCompact.Core.xcood = msgData[3]; textCompact.Core.ycood = msgData[4]; FnSetFont(0); }
private bool FnSetToStand(uint a, uint b, uint c) { _compact.Core.mood = 1; // high level stood still _compact.Core.grafixProgId = _skyCompact.GetCompactElem(_compact, (ushort)(C_STAND_UP + _compact.Core.megaSet + _compact.Core.dir * 4)).Field; _compact.Core.grafixProgPos = 0; UShortAccess standList = _skyCompact.GetGrafixPtr(_compact); _compact.Core.offset = standList.Value; // get frames offset _compact.Core.logic = L_SIMPLE_MOD; _compact.Core.grafixProgPos++; SimpleAnim(); return(false); // drop out of script }
private void ArTurn() { var turnData = new UShortAccess(_skyCompact.FetchCptRaw(_compact.Core.turnProgId), _compact.Core.turnProgPos * 2); _compact.Core.frame = turnData[0]; turnData.Offset += 2; _compact.Core.turnProgPos++; if (turnData[0] == 0) { // turn done? // Back to ar mode _compact.Core.arAnimIndex = 0; _compact.Core.logic = L_AR_ANIM; } }
private static ushort CheckBlock(byte[] block, int blockPos) { var b = new UShortAccess(block, blockPos); ushort retVal = 0xFFFF; for (byte cnt = 0; cnt < 4; cnt++) { var fieldVal = b[RouteDirections[cnt]]; if (fieldVal != 0 && (fieldVal < retVal)) { retVal = fieldVal; } } return(retVal); }
private bool FnTestList(uint id, uint x, uint y) { _scriptVariables[RESULT] = 0; // assume fail var list = new UShortAccess(_skyCompact.FetchCptRaw((ushort)id), 0); while (list.Value != 0) { if ((x >= list[0]) && (x < list[1]) && (y >= list[2]) && (y < list[3])) { _scriptVariables[RESULT] = list[4]; } list.Offset += 5 * 2; } return(true); }
public void Script(uint command, UShortAccess scriptData) { Write("SCRIPT: {0}", Opcodes[command]); if (command == 0 || command == 6) { Write(" {0}", ScriptVars[scriptData[0] / 4]); } else { for (var i = 0; i < OpcodeParameter[command]; i++) { Write(" {0}", scriptData[i]); } } Write(" "); // Print an empty line as separator }
public void LoadSection(byte section) { FnStopFx(); _mixer.StopAll(); _soundData = _skyDisk.LoadFile(section * 4 + SoundFileBase); ushort asmOfs; if (SystemVars.Instance.GameVersion.Version.Minor == 109) { if (section == 0) { asmOfs = 0x78; } else { asmOfs = 0x7C; } } else { asmOfs = 0x7E; } if ((_soundData[asmOfs] != 0x3C) || (_soundData[asmOfs + 0x27] != 0x8D) || (_soundData[asmOfs + 0x28] != 0x1E) || (_soundData[asmOfs + 0x2F] != 0x8D) || (_soundData[asmOfs + 0x30] != 0x36)) { throw new NotSupportedException("Unknown sounddriver version"); } _soundsTotal = _soundData[asmOfs + 1]; var sRateTabOfs = _soundData.ToUInt16(asmOfs + 0x29); _sfxBaseOfs = _soundData.ToUInt16(asmOfs + 0x31); _sampleRates = new ByteAccess(_soundData, sRateTabOfs); _sfxInfo = new UShortAccess(_soundData, _sfxBaseOfs); // if we just restored a savegame, the sfxqueue holds the sound we need to restart if (!SystemVars.Instance.SystemFlags.HasFlag(SystemFlags.GameRestored)) { for (var cnt = 0; cnt < MaxQueuedFx; cnt++) { SfxQueue[cnt].Count = 0; } } }
private void Turn() { var turnData = new UShortAccess(_skyCompact.FetchCptRaw(_compact.Core.turnProgId), _compact.Core.turnProgPos * 2); if (turnData[0] != 0) { _compact.Core.frame = turnData[0]; _compact.Core.turnProgPos++; return; } // turn_to_script: _compact.Core.arAnimIndex = 0; _compact.Core.logic = L_SCRIPT; LogicScript(); }
private bool FnMoveItems(uint listNo, uint screenNo, uint c) { // Move a list of id's to another screen var p = new UShortAccess(_skyCompact.FetchCptRaw((ushort)CptIds.MoveList), 0); p = new UShortAccess(_skyCompact.FetchCptRaw(p[(int)listNo]), 0); for (int i = 0; i < 2; i++) { if (p.Value == 0) { return(true); } Compact cpt = _skyCompact.FetchCpt(p.Value); p.Offset += 2; cpt.Core.screen = (ushort)(screenNo & 0xffff); } return(true); }
private static UShortAccess CheckInitMove(UShortAccess data, short initStaX) { var index = 0; if (initStaX < 0) { index -= 2; data[index + 1] = Logic.RIGHTY; data[index] = (ushort)((-initStaX + 7) & 0xFFF8); } else if (initStaX > 0) { index -= 2; data[index] = (ushort)((initStaX + 7) & 0xFFF8); data[index] = (ushort)((initStaX + 7) & 0xFFF8); } return(new UShortAccess(data.Data, data.Offset + index * 2)); }
private UShortAccess MakeRouteData(byte destX, byte destY) { Array.Clear(_routeBuf, 0, _routeBuf.Length); var routeBuf = new UShortAccess(_routeBuf, 0); var routeGrid = new UShortAccess(_routeGrid, 0); var routePos = (destY + 1) * RouteGridWidth + destX + 1; var dataTrg = (Logic.ROUTE_SPACE >> 1) - 2; var lastVal = (ushort)(routeGrid[routePos] - 1); while (lastVal != 0) { // lastVal == 0 means route is done. dataTrg -= 2; short walkDirection = 0; for (byte cnt = 0; cnt < 4; cnt++) { if (lastVal == routeGrid[routePos + RouteDirections[cnt]]) { routeBuf[dataTrg + 1] = LogicCommands[cnt]; walkDirection = RouteDirections[cnt]; break; } } if (walkDirection == 0) { throw new InvalidOperationException( string.Format("makeRouteData:: can't find way through walkGrid (pos {0})", lastVal)); } while (lastVal != 0 && (lastVal == routeGrid[routePos + walkDirection])) { routeBuf[dataTrg] += WalkJump; lastVal--; routePos += walkDirection; } } return(new UShortAccess(routeBuf.Data, routeBuf.Offset + dataTrg * 2)); }
private bool FnEyeball(uint id, uint b, uint c) { // set 'result' to frame no. pointing to foster, according to table used // eg. FN_eyeball (id_eye_90_table); var eyeTable = new UShortAccess(_skyCompact.FetchCptRaw((ushort)id), 0); Compact cpt = _skyCompact.FetchCpt(ID_BLUE_FOSTER); int x = cpt.Core.xcood; // 168 < x < 416 x -= 168; x >>= 3; int y = cpt.Core.ycood; // 256 < y < 296 y -= 256; y <<= 2; _scriptVariables[RESULT] = (uint)(eyeTable[x + y] + S91); return(true); }
private void InitWalkGrid(byte screen, byte width) { byte stretch = 0; var screenGrid = _grid.GiveGrid(screen); var screenGridPos = Logic.GRID_SIZE; var wGridPos = new UShortAccess(_routeGrid, ((RouteGridSize >> 1) - RouteGridWidth - 2) * 2); Array.Clear(_routeGrid, 0, _routeGrid.Length); byte bitsLeft = 0; uint gridData = 0; for (byte gridCntY = 0; gridCntY < RouteGridHeight - 2; gridCntY++) { for (byte gridCntX = 0; gridCntX < RouteGridWidth - 2; gridCntX++) { if (bitsLeft == 0) { screenGridPos -= 4; gridData = screenGrid.ToUInt32(screenGridPos); bitsLeft = 32; } if ((gridData & 1) != 0) { wGridPos[0] = 0xFFFF; // block is not accessible stretch = width; } else if (stretch != 0) { wGridPos[0] = 0xFFFF; stretch--; } wGridPos.Offset -= 2; bitsLeft--; gridData >>= 1; } wGridPos.Offset -= 4; stretch = 0; } }
private bool FnTurnTo(uint dir, uint b, uint c) { // turn compact to direction dir ushort curDir = _compact.Core.dir; // get current direction _compact.Core.dir = (ushort)(dir & 0xffff); // set new direction UShortAccess tt = _skyCompact.GetTurnTable(_compact, curDir); if (tt[(int)dir] == 0) { return(true); // keep going } _compact.Core.turnProgId = tt[(int)dir]; // put turn program in _compact.Core.turnProgPos = 0; _compact.Core.logic = L_TURNING; Turn(); return(false); // drop out of script }
private void MainAnim() { // Extension of arAnim() _compact.Core.waitingFor = 0; // clear possible zero-zero skip var sequence = _skyCompact.GetGrafixPtr(_compact); if (sequence[0] == 0) { // ok, move to new anim segment sequence.Offset += 4; _compact.Core.grafixProgPos += 2; if (sequence[0] == 0) { // end of route? // ok, sequence has finished // will start afresh if new sequence continues in last direction _compact.Core.arAnimIndex = 0; _compact.Core.downFlag = 0; // pass back ok to script _compact.Core.logic = L_SCRIPT; LogicScript(); return; } _compact.Core.arAnimIndex = 0; // reset position } ushort dir; while ((dir = _compact.Core.dir) != sequence[1]) { // ok, setup turning _compact.Core.dir = sequence[1]; var tt = _skyCompact.GetTurnTable(_compact, dir); if (tt[_compact.Core.dir] != 0) { _compact.Core.turnProgId = tt[_compact.Core.dir]; _compact.Core.turnProgPos = 0; _compact.Core.logic = L_AR_TURNING; ArTurn(); return; } } var animId = _skyCompact.GetCompactElem(_compact, (ushort)(C_ANIM_UP + _compact.Core.megaSet + dir * 4)).Field; var animList = new UShortAccess(_skyCompact.FetchCptRaw(animId), 0); ushort arAnimIndex = _compact.Core.arAnimIndex; if (animList[arAnimIndex / 2] == 0) { arAnimIndex = 0; _compact.Core.arAnimIndex = 0; // reset } _compact.Core.arAnimIndex += S_LENGTH; sequence[0] -= animList[(S_COUNT + arAnimIndex) / 2]; // reduce the distance to travel _compact.Core.frame = animList[(S_FRAME + arAnimIndex) / 2]; // new graphic frame _compact.Core.xcood += animList[(S_AR_X + arAnimIndex) / 2]; // update x coordinate _compact.Core.ycood += animList[(S_AR_Y + arAnimIndex) / 2]; // update y coordinate }
private bool CalcWalkGrid(byte startX, byte startY, byte destX, byte destY) { short directionX, directionY; byte roiX, roiY; // Rectangle Of Interest in the walk grid if (startY > destY) { directionY = -RouteGridWidth; roiY = startY; } else { directionY = RouteGridWidth; roiY = (byte)(RouteGridHeight - 1 - startY); } if (startX > destX) { directionX = -1; roiX = (byte)(startX + 2); } else { directionX = 1; roiX = (byte)(RouteGridWidth - 1 - startX); } var walkDest = (destY + 1) * RouteGridWidth + destX + 1; var walkStart = (startY + 1) * RouteGridWidth + startX + 1; var routeGrid = new UShortAccess(_routeGrid, 0); routeGrid[walkStart] = 1; // if we are on the edge, move diagonally from start if (roiY < RouteGridHeight - 3) { walkStart -= directionY; } if (roiX < RouteGridWidth - 2) { walkStart -= directionX; } var gridChanged = true; var foundRoute = false; while (!foundRoute && gridChanged) { gridChanged = false; var yWalkCalc = walkStart; for (byte cnty = 0; cnty < roiY; cnty++) { var xWalkCalc = yWalkCalc; for (byte cntx = 0; cntx < roiX; cntx++) { if (routeGrid[xWalkCalc] == 0) { // block wasn't done, yet var blockRet = CheckBlock(_routeGrid, routeGrid.Offset + xWalkCalc * 2); if (blockRet < 0xFFFF) { routeGrid[xWalkCalc] = (ushort)(blockRet + 1); gridChanged = true; } } xWalkCalc += directionX; } yWalkCalc += directionY; } if (routeGrid[walkDest] != 0) { // okay, finished foundRoute = true; } else { // we couldn't find the route, let's extend the ROI if (roiY < RouteGridHeight - 4) { walkStart -= directionY; roiY++; } if (roiX < RouteGridWidth - 4) { walkStart -= directionX; roiX++; } } } return(foundRoute); }
private void SortSprites() { var sortList = new StSortList[30]; int currDrawList = Logic.DRAW_LIST_NO; while (Logic.ScriptVariables[currDrawList] != 0) { // big_sort_loop uint spriteCnt = 0; var loadDrawList = Logic.ScriptVariables[currDrawList]; currDrawList++; bool nextDrawList; do { // a_new_draw_list: var drawListData = new UShortAccess(_skyCompact.FetchCptRaw((ushort)loadDrawList), 0); nextDrawList = false; while (!nextDrawList && (drawListData[0] != 0)) { if (drawListData[0] == 0xFFFF) { loadDrawList = drawListData[1]; nextDrawList = true; } else { // process_this_id: var spriteComp = _skyCompact.FetchCpt(drawListData[0]); if (((spriteComp.Core.status & 4) != 0) && // is it sortable playfield?(!?!) (spriteComp.Core.screen == Logic.ScriptVariables[Logic.SCREEN])) { // on current screen var spriteData = SkyEngine.ItemList[spriteComp.Core.frame >> 6]; if (spriteData == null) { // TODO: debug(9, "Missing file %d", spriteComp.frame >> 6); spriteComp.Core.status = 0; } else { sortList[spriteCnt].YCood = (uint) (spriteComp.Core.ycood + spriteData.ToUInt16(18) + spriteData.ToUInt16(8)); sortList[spriteCnt].Compact = spriteComp; sortList[spriteCnt].Sprite = spriteData; spriteCnt++; } } drawListData.Offset += 2; } } } while (nextDrawList); // made_list: if (spriteCnt > 1) { // bubble sort for (var cnt1 = 0; cnt1 < spriteCnt - 1; cnt1++) { for (var cnt2 = cnt1 + 1; cnt2 < spriteCnt; cnt2++) { if (sortList[cnt1].YCood > sortList[cnt2].YCood) { StSortList tmp; tmp.YCood = sortList[cnt1].YCood; tmp.Sprite = sortList[cnt1].Sprite; tmp.Compact = sortList[cnt1].Compact; sortList[cnt1].YCood = sortList[cnt2].YCood; sortList[cnt1].Sprite = sortList[cnt2].Sprite; sortList[cnt1].Compact = sortList[cnt2].Compact; sortList[cnt2].YCood = tmp.YCood; sortList[cnt2].Sprite = tmp.Sprite; sortList[cnt2].Compact = tmp.Compact; } } } } for (var cnt = 0; cnt < spriteCnt; cnt++) { DrawSprite(sortList[cnt].Sprite, sortList[cnt].Compact); if ((sortList[cnt].Compact.Core.status & 8) != 0) { VectorToGame(0x81); } else { VectorToGame(1); } if ((sortList[cnt].Compact.Core.status & 0x200) == 0) { VerticalMask(); } } } }
public ushort DoAutoRoute(Compact cpt) { var cptScreen = (byte)cpt.Core.screen; var cptWidth = (byte)SkyCompact.GetMegaSet(cpt).gridWidth; InitWalkGrid(cptScreen, cptWidth); byte startX, startY, destX, destY; short initStaX, initStaY, initDestX, initDestY; ClipCoordX(cpt.Core.xcood, out startX, out initStaX); ClipCoordY(cpt.Core.ycood, out startY, out initStaY); ClipCoordX(cpt.Core.arTargetX, out destX, out initDestX); ClipCoordY(cpt.Core.arTargetY, out destY, out initDestY); var raw = _skyCompact.FetchCptRaw(cpt.Core.animScratchId); Array.Clear(raw, 0, 64); var routeDest = new UShortAccess(raw, 0); if ((startX == destX) && (startY == destY)) { return(2); } var routeGrid = new UShortAccess(_routeGrid, 0); if (routeGrid[(destY + 1) * RouteGridWidth + destX + 1] != 0) { //if ((cpt == &Sky::SkyCompact::foster) && (cptScreen == 12) && (destX == 2) && (destY == 14)) { if (_skyCompact.CptIsId(cpt, (ushort)CptIds.Foster) && (cptScreen == 12) && (destX == 2) && (destY == 14)) { /* workaround for Scriptbug #1043047 * In screen 12 (the pipe factory) Joey can block Foster's target * coordinates (2/14). This is normally not too tragic, but in the * scene when foster gets thrown out by Lamb (first time you enter * the pipe factory), the game would enter an infinite loop. */ routeGrid[(destY + 1) * RouteGridWidth + destX + 1] = 0; // hide this part joey from the grid } else { return(1); // AR destination is an unaccessible block } } if (!CalcWalkGrid(startX, startY, destX, destY)) { return(1); // can't find route to block } var routeData = MakeRouteData(destX, destY); // the route is done. // if there was an initial x movement (due to clipping) tag it onto the start routeData = CheckInitMove(routeData, initStaX); byte cnt = 0; do { routeDest[cnt] = routeData[cnt]; routeDest[cnt + 1] = routeData[cnt + 1]; cnt += 2; } while (routeData[cnt - 2] != 0); return(0); }
private void ArAnim() { // Follow a route // Mega should be in getToMode // only check collisions on character boundaries if (((_compact.Core.xcood & 7) != 0) || ((_compact.Core.ycood & 7) != 0)) { MainAnim(); return; } // On character boundary. Have we been told to wait? // if not - are WE colliding? if (_compact.Core.waitingFor == 0xffff) { // 1st cycle of re-route does not require collision checks MainAnim(); return; } if (_compact.Core.waitingFor != 0) { // ok, we've been told we've hit someone // we will wait until we are no longer colliding // with them. here we check to see if we are (still) colliding. // if we are then run the stop script. if not clear the flag // and continue. // remember - this could be the first ar cycle for some time, // we might have been told to wait months ago. if we are // waiting for one person then another hits us then // c_waiting_for will be replaced by the new mega - this is // fine because the later collision will almost certainly // take longer to clear than the earlier one. if (Collide(_skyCompact.FetchCpt(_compact.Core.waitingFor))) { StopAndWait(); return; } // we are not in fact hitting this person so clr & continue // it must have registered some time ago _compact.Core.waitingFor = 0; // clear id flag } // ok, our turn to check for collisions var logicList = new UShortAccess(_skyCompact.FetchCptRaw((ushort)_scriptVariables[LOGIC_LIST_NO]), 0); ushort id; while ((id = logicList[0]) != 0) { // get an id logicList.Offset += 2; if (id == 0xffff) { // address change? logicList = new UShortAccess(_skyCompact.FetchCptRaw(logicList[0]), 0); // get new logic list continue; } if (id == (ushort)(_scriptVariables[CUR_ID] & 0xffff)) // is it us? { continue; } _scriptVariables[HIT_ID] = id; // save target id for any possible c_mini_bump var cpt = _skyCompact.FetchCpt(id); if ((cpt.Core.status & (1 << ST_COLLISION_BIT)) == 0) // can it collide? { continue; } if (cpt.Core.screen != _compact.Core.screen) // is it on our screen? { continue; } if (Collide(cpt)) { // check for a hit // ok, we've hit a mega // is it moving... or something else? if (cpt.Core.logic != L_AR_ANIM) { // check for following route // it is doing something else // we restart our get-to script // first tell it to wait for us - in case it starts moving // ( *it may have already hit us and stopped to wait ) _compact.Core.waitingFor = 0xffff; // effect 1 cycle collision skip // tell it it is waiting for us cpt.Core.waitingFor = (ushort)(_scriptVariables[CUR_ID] & 0xffff); // restart current script SkyCompact.GetSub(_compact, _compact.Core.mode + 2).Field = 0; _compact.Core.logic = L_SCRIPT; LogicScript(); return; } Script(_compact.Core.miniBump, 0); return; } } // ok, there was no collisions // now check for interaction request // *note: the interaction is always set up as an action script if (_compact.Core.request != 0) { _compact.Core.mode = C_ACTION_MODE; // put into action mode _compact.Core.actionSub = _compact.Core.request; _compact.Core.actionSub_off = 0; _compact.Core.request = 0; // trash request _compact.Core.logic = L_SCRIPT; LogicScript(); return; } // any flag? - or any change? // if change then re-run the current script, which must be // a position independent get-to ---- if (_compact.Core.atWatch == 0) { // any flag set? MainAnim(); return; } // ok, there is an at watch - see if it's changed if (_compact.Core.atWas == _scriptVariables[_compact.Core.atWatch / 4]) { // still the same? MainAnim(); return; } // changed so restart the current script // *not suitable for base initiated ARing SkyCompact.GetSub(_compact, _compact.Core.mode + 2).Field = 0; _compact.Core.logic = L_SCRIPT; LogicScript(); }
public ushort Script(ushort scriptNo, ushort offset) { do { bool restartScript = false; // process a script // low level interface to interpreter ushort moduleNo = (ushort)(scriptNo >> 12); var data = _moduleList[moduleNo]; // get module address if (data == null) { // We need to load the script module _moduleList[moduleNo] = _skyDisk.LoadScriptFile((ushort)(moduleNo + F_MODULE_0)); data = _moduleList[moduleNo]; // module has been loaded } var scriptData = new UShortAccess(data, 0); Debug.Instance.Write("Doing Script: {0}:{1}:{2:X}", moduleNo, scriptNo & 0xFFF, offset != 0 ? offset - scriptData[scriptNo & 0xFFF] : 0); // WORKAROUND for bug #3149412: "Invalid Mode when giving shades to travel agent" // Using the dark glasses on Trevor (travel agent) multiple times in succession would // wreck the trevor compact's mode, as the script in question doesn't account for using // this item at this point in the game (you will only have it here if you play the game // in an unusual way) and thus would loop indefinitely / never drop out. // To prevent this, we trigger the generic response by pretending we're using an item // which the script /does/ handle. if (scriptNo == TREVOR_SPEECH && _scriptVariables[OBJECT_HELD] == IDO_SHADES) { _scriptVariables[OBJECT_HELD] = IDO_GLASS; } // Check whether we have an offset or what if (offset != 0) { scriptData = new UShortAccess(data, offset * 2); } else { scriptData.Offset += scriptData[scriptNo & 0x0FFF] * 2; } uint b = 0, c = 0; while (!restartScript) { var command = scriptData.Value; scriptData.Offset += 2; // get a command Debug.Instance.Script(command, scriptData); uint a; ushort s; switch (command) { case 0: // push_variable Push(_scriptVariables[scriptData.Value / 4]); scriptData.Offset += 2; break; case 1: // less_than a = Pop(); b = Pop(); if (b < a) { Push(1); } else { Push(0); } break; case 2: // push_number Push(scriptData.Value); scriptData.Offset += 2; break; case 3: // not_equal a = Pop(); b = Pop(); if (a != b) { Push(1); } else { Push(0); } break; case 4: // if_and a = Pop(); b = Pop(); if (a != 0 && b != 0) { Push(1); } else { Push(0); } break; case 5: // skip_zero s = scriptData.Value; scriptData.Offset += 2; a = Pop(); if (a == 0) { scriptData.Offset += s; } break; case 6: // pop_var b = _scriptVariables[scriptData.Value / 4] = Pop(); scriptData.Offset += 2; break; case 7: // minus a = Pop(); b = Pop(); Push(b - a); break; case 8: // plus a = Pop(); b = Pop(); Push(b + a); break; case 9: // skip_always s = scriptData.Value; scriptData.Offset += 2; scriptData.Offset += s; break; case 10: // if_or a = Pop(); b = Pop(); if (a != 0 || b != 0) { Push(1); } else { Push(0); } break; case 11: // call_mcode { a = scriptData.Value; scriptData.Offset += 2; System.Diagnostics.Debug.Assert(a <= 3); switch (a) { case 3: c = Pop(); b = Pop(); a = Pop(); break; case 2: b = Pop(); a = Pop(); break; case 1: a = Pop(); break; } ushort mcode = (ushort)(scriptData.Value / 4); scriptData.Offset += 2; // get mcode number Debug.Instance.Mcode(mcode, a, b, c); var saveCpt = _compact; bool ret = _mcodeTable[mcode](a, b, c); _compact = saveCpt; if (!ret) { return((ushort)(scriptData.Offset / 2)); } } break; case 12: // more_than a = Pop(); b = Pop(); if (b > a) { Push(1); } else { Push(0); } break; case 14: // switch c = s = scriptData.Value; scriptData.Offset += 2; // get number of cases a = Pop(); // and value to switch on do { if (a == scriptData.Value) { scriptData.Offset += scriptData[1]; scriptData.Offset += 2; break; } scriptData.Offset += 4; } while (--s != 0); if (s == 0) { scriptData.Offset += scriptData.Value; // use the default } break; case 15: // push_offset { var elem = _skyCompact.GetCompactElem(_compact, scriptData.Value); Push(elem.Field); scriptData.Offset += 2; } break; case 16: // pop_offset { // pop a value into a compact var elem = _skyCompact.GetCompactElem(_compact, scriptData.Value); scriptData.Offset += 2; elem.Field = (ushort)Pop(); } break; case 17: // is_equal a = Pop(); b = Pop(); if (a == b) { Push(1); } else { Push(0); } break; case 18: { // skip_nz short t = (short)scriptData.Value; scriptData.Offset += 2; a = Pop(); if (a != 0) { scriptData.Offset += t; } break; } case 13: case 19: // script_exit return(0); case 20: // restart_script offset = 0; restartScript = true; break; default: throw new InvalidOperationException(string.Format("Unknown script command: {0}", command)); } } } while (true); }
private void StdSpeak(Compact target, uint textNum, uint animNum) { animNum += (uint)(target.Core.megaSet / NEXT_MEGA_SET); animNum &= 0xFF; var talkTable = new UShortAccess(_skyCompact.FetchCptRaw((ushort)CptIds.TalkTableList), 0); target.Core.grafixProgId = talkTable[(int)animNum]; target.Core.grafixProgPos = 0; var animPtr = _skyCompact.GetGrafixPtr(target); if (animPtr != null) { target.Core.offset = animPtr[0]; animPtr.Offset += 2; target.Core.getToFlag = animPtr[0]; animPtr.Offset += 2; target.Core.grafixProgPos += 2; } else { target.Core.grafixProgId = 0; } bool speechFileFound = false; if (SkyEngine.IsCDVersion) { speechFileFound = _skySound.StartSpeech((ushort)textNum); } // Set the focus region to that area // Calculate the point where the character is int x = target.Core.xcood - TOP_LEFT_X; int y = target.Core.ycood - TOP_LEFT_Y; // TODO: Make the box size change based on the object that has the focus _skyScreen.SetFocusRectangle(new Rect(x, y, 192, 128)); if (SystemVars.Instance.SystemFlags.HasFlag(SystemFlags.AllowText) || !speechFileFound) { // form the text sprite, if player wants subtitles or // if we couldn't find the speech file var textInfo = _skyText.LowTextManager(textNum, FIXED_TEXT_WIDTH, 0, (byte)target.Core.spColor, true); var textCompact = _skyCompact.FetchCpt(textInfo.CompactNum); target.Core.spTextId = textInfo.CompactNum; //So we know what text to kill var textGfxHeader = ServiceLocator.Platform.ToStructure <DataFileHeader>(textInfo.TextData, 0); textCompact.Core.screen = target.Core.screen; //put it on our screen if (_scriptVariables[SCREEN] == target.Core.screen) { // Only use coordinates if we are on the current screen //talking on-screen //create the x coordinate for the speech text //we need the talkers sprite information var targetGfx = SkyEngine.ItemList[target.Core.frame >> 6]; var header = ServiceLocator.Platform.ToStructure <DataFileHeader>(targetGfx, 0); ushort xPos = (ushort)(target.Core.xcood + header.s_offset_x); ushort width = (ushort)(header.s_width >> 1); xPos += (ushort)(width - FIXED_TEXT_WIDTH / 2); //middle of talker if (xPos < TOP_LEFT_X) { xPos = TOP_LEFT_X; } width = (ushort)(xPos + FIXED_TEXT_WIDTH); if (TOP_LEFT_X + FULL_SCREEN_WIDTH <= width) { xPos = TOP_LEFT_X + FULL_SCREEN_WIDTH; xPos -= FIXED_TEXT_WIDTH; } textCompact.Core.xcood = xPos; ushort yPos = (ushort)(target.Core.ycood + (header.s_offset_y - 6 - textGfxHeader.s_height)); if (yPos < TOP_LEFT_Y) { yPos = TOP_LEFT_Y; } textCompact.Core.ycood = yPos; } else { //talking off-screen target.Core.spTextId = 0; //don't kill any text 'cos none was made textCompact.Core.status = 0; //don't display text } // In CD version, we're doing the timing by checking when the VOC has stopped playing. // Setting spTime to 10 thus means that we're doing a pause of 10 gamecycles between // each sentence. if (speechFileFound) { target.Core.spTime = 10; } else { target.Core.spTime = (ushort)(_skyText.NumLetters + 5); } } else { target.Core.spTime = 10; target.Core.spTextId = 0; } target.Core.logic = L_TALK; }
private void PointerEngine(ushort xPos, ushort yPos) { UShortAccess currentList; var currentListNum = Logic.ScriptVariables[Logic.MOUSE_LIST_NO]; do { currentList = new UShortAccess(_skyCompact.FetchCptRaw((ushort)currentListNum), 0); while ((currentList[0] != 0) && (currentList[0] != 0xFFFF)) { var itemNum = currentList[0]; var itemData = _skyCompact.FetchCpt(itemNum); currentList.Offset += 2; if ((itemData.Core.screen == Logic.ScriptVariables[Logic.SCREEN]) && ((itemData.Core.status & 16) != 0)) { if (itemData.Core.xcood + itemData.Core.mouseRelX > xPos) { continue; } if (itemData.Core.xcood + itemData.Core.mouseRelX + itemData.Core.mouseSizeX < xPos) { continue; } if (itemData.Core.ycood + itemData.Core.mouseRelY > yPos) { continue; } if (itemData.Core.ycood + itemData.Core.mouseRelY + itemData.Core.mouseSizeY < yPos) { continue; } // we've hit the item if (Logic.ScriptVariables[Logic.SPECIAL_ITEM] == itemNum) { return; } Logic.ScriptVariables[Logic.SPECIAL_ITEM] = itemNum; if (Logic.ScriptVariables[Logic.GET_OFF] != 0) { Logic.MouseScript(Logic.ScriptVariables[Logic.GET_OFF], itemData); } Logic.ScriptVariables[Logic.GET_OFF] = itemData.Core.mouseOff; if (itemData.Core.mouseOn != 0) { Logic.MouseScript(itemData.Core.mouseOn, itemData); } return; } } if (currentList[0] == 0xFFFF) { currentListNum = currentList[1]; } } while (currentList[0] != 0); if (Logic.ScriptVariables[Logic.SPECIAL_ITEM] != 0) { Logic.ScriptVariables[Logic.SPECIAL_ITEM] = 0; if (Logic.ScriptVariables[Logic.GET_OFF] != 0) { Logic.Script((ushort)Logic.ScriptVariables[Logic.GET_OFF], (ushort)(Logic.ScriptVariables[Logic.GET_OFF] >> 16)); } Logic.ScriptVariables[Logic.GET_OFF] = 0; } }