protected void FadeOut(int effect) { _mainVirtScreen.SetDirtyRange(0, 0); if (Game.Version < 7) { Camera.LastPosition.X = Camera.CurrentPosition.X; } if (Game.Version == 3 && _game.Platform == Platform.FMTowns) { Gdi.Fill(TextSurface, new Rect(0, MainVirtScreen.TopLine * _textSurfaceMultiplier, _textSurface.Pitch, (MainVirtScreen.TopLine + MainVirtScreen.Height) * _textSurfaceMultiplier), 0); } if ((Game.Version == 7 || _screenEffectFlag) && effect != 0) { // Fill screen 0 with black var pixNav = new PixelNavigator(_mainVirtScreen.Surfaces[0]); pixNav.OffsetX(_mainVirtScreen.XStart); Gdi.Fill(pixNav, 0, _mainVirtScreen.Width, _mainVirtScreen.Height); // Fade to black with the specified effect, if any. switch (effect) { case 1: case 2: case 3: case 4: case 5: case 6: DoTransitionEffect(effect - 1); break; case 128: UnkScreenEffect6(); break; case 129: // Just blit screen 0 to the display (i.e. display will be black) _mainVirtScreen.SetDirtyRange(0, _mainVirtScreen.Height); UpdateDirtyScreen(_mainVirtScreen); if (_townsScreen != null) { _townsScreen.Update(); } break; default: throw new NotImplementedException(string.Format("fadeOut: case {0}", effect)); } } // Update the palette at the end (once we faded to black) to avoid // some nasty effects when the palette is changed UpdatePalette(); _screenEffectFlag = false; }
void OldRoomEffect() { _opCode = ReadByte(); if ((_opCode & 0x1F) == 3) { var a = GetVarOrDirectWord(OpCodeParameter.Param1); if (Game.Platform == Platform.FMTowns && Game.Version == 3) { if (a == 4) { Gdi.Fill(TextSurface, new Rect(0, 0, TextSurface.Width * TextSurfaceMultiplier, TextSurface.Height * TextSurfaceMultiplier), 0); if (_townsScreen != null) { _townsScreen.ClearLayer(1); } return; } } if (a != 0) { _switchRoomEffect = (byte)(a & 0xFF); _switchRoomEffect2 = (byte)(a >> 8); } else { FadeIn(_newEffect); } } }
protected void RedefineBuiltinCursorFromChar(int index, int chr) { // Cursor image in both Loom versions are based on images from charset. // This function is *only* supported for Loom! if (_game.GameId != Scumm.IO.GameId.Loom) { throw new NotSupportedException("RedefineBuiltinCursorFromChar is *only* supported for Loom!"); } if (index < 0 || index >= 4) { throw new ArgumentException("index"); } // const int oldID = _charset->getCurID(); var ptr = _cursorImages[index]; if (_game.Version == 3) { _charset.SetCurID(0); } else if (_game.Version >= 4) { _charset.SetCurID(1); } var s = new Surface(_charset.GetCharWidth(chr), _charset.GetFontHeight(), PixelFormat.Indexed8, false); var p = new PixelNavigator(s); Gdi.Fill(new PixelNavigator(s), 123, s.Width, s.Height); _charset.DrawChar(chr, s, 0, 0); Array.Clear(ptr, 0, ptr.Length); for (int h = 0; h < s.Height; h++) { for (int w = 0; w < s.Width; w++) { p.GoTo(w, h); if (p.Read() != 123) { ptr[h] |= (ushort)(1 << (15 - w)); } } } // _charset->setCurID(oldID); }
protected void InitScreens(int b, int h) { if (_townsScreen != null) { if (_townsClearLayerFlag == 0 && MainVirtScreen != null && ((h - b) != MainVirtScreen.Height)) { _townsScreen.ClearLayer(0); } if (Game.GameId != GameId.Monkey1) { Gdi.Fill(TextSurface, new Rect(0, 0, _textSurface.Width * _textSurfaceMultiplier, _textSurface.Height * _textSurfaceMultiplier), 0); _townsScreen.ClearLayer(1); } } var format = Game.Features.HasFlag(GameFeatures.Is16BitColor) ? PixelFormat.Rgb16 : PixelFormat.Indexed8; _mainVirtScreen = new VirtScreen(b, ScreenWidth, h - b, format, 2, true); _textVirtScreen = new VirtScreen(0, ScreenWidth, b, format, 1); _verbVirtScreen = new VirtScreen(h, ScreenWidth, ScreenHeight - h, format, 1); // Since the size of screen 3 is fixed, there is no need to reallocate // it if its size changed. // Not sure what it is good for, though. I think it may have been used // in pre-V7 for the games messages (like 'Pause', Yes/No dialogs, // version display, etc.). I don't know about V7, maybe the same is the // case there. If so, we could probably just remove it completely. if (_game.Version >= 7) { _unkVirtScreen = new VirtScreen((ScreenHeight / 2) - 10, ScreenWidth, 13, format, 1); } else { _unkVirtScreen = new VirtScreen(80, ScreenWidth, 13, format, 1); } _screenB = b; _screenH = h; Gdi.Init(); }
protected void RestoreBackground(Rect rect, byte backColor = 0) { VirtScreen vs; if (rect.Top < 0) { rect.Top = 0; } if (rect.Left >= rect.Right || rect.Top >= rect.Bottom) { return; } if ((vs = FindVirtScreen(rect.Top)) == null) { return; } if (rect.Left > vs.Width) { return; } // Convert 'rect' to local (virtual screen) coordinates rect.Top -= vs.TopLine; rect.Bottom -= vs.TopLine; rect.Clip(vs.Width, vs.Height); int height = rect.Height; int width = rect.Width; if (_game.Platform == Platform.FMTowns && Game.GameId == GameId.Monkey1 && vs == VerbVirtScreen && rect.Bottom <= 154) { rect.Right = 319; } MarkRectAsDirty(vs, rect.Left, rect.Right, rect.Top, rect.Bottom, Gdi.UsageBitRestored); var screenBuf = new PixelNavigator(vs.Surfaces[0]); screenBuf.GoTo(vs.XStart + rect.Left, rect.Top); if (height == 0) { return; } if (vs.HasTwoBuffers && _currentRoom != 0 && IsLightOn()) { var back = new PixelNavigator(vs.Surfaces[1]); back.GoTo(vs.XStart + rect.Left, rect.Top); Gdi.Blit(screenBuf, back, width, height); if (vs == MainVirtScreen && _charset.HasMask) { if (_game.Platform == Platform.FMTowns) { var mask = new PixelNavigator(_textSurface); mask.GoTo(rect.Left * _textSurfaceMultiplier, (rect.Top + vs.TopLine) * _textSurfaceMultiplier); Gdi.Fill(mask, 0, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier); } else { var mask = new PixelNavigator(_textSurface); mask.GoTo(rect.Left, rect.Top - ScreenTop); Gdi.Fill(mask, CharsetMaskTransparency, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier); } } } else { if (Game.Platform == Platform.FMTowns) { backColor |= (byte)(backColor << 4); var mask = new PixelNavigator(_textSurface); mask.GoTo(rect.Left * _textSurfaceMultiplier, (rect.Top + vs.TopLine) * _textSurfaceMultiplier); Gdi.Fill(mask, backColor, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier); } if (Game.Features.HasFlag(GameFeatures.Is16BitColor)) { Gdi.Fill(screenBuf, _16BitPalette[backColor], width, height); } else { Gdi.Fill(screenBuf, backColor, width, height); } } }
void DrawFlashlight() { int i, j, x, y; var vs = MainVirtScreen; // Remove the flash light first if it was previously drawn if (_flashlight.IsDrawn) { MarkRectAsDirty(MainVirtScreen, _flashlight.X, _flashlight.X + _flashlight.W, _flashlight.Y, _flashlight.Y + _flashlight.H, Gdi.UsageBitDirty); if (_flashlight.PixelNavigator.HasValue) { Gdi.Fill(_flashlight.PixelNavigator.Value, 0, _flashlight.W, _flashlight.H); } _flashlight.IsDrawn = false; } if (_flashlight.XStrips == 0 || _flashlight.YStrips == 0) { return; } // Calculate the area of the flashlight if (Game.GameId == GameId.Zak || Game.GameId == GameId.Maniac) { x = _mousePos.X + vs.XStart; y = _mousePos.Y - vs.TopLine; } else { var a = Actors[Variables[VariableEgo.Value]]; x = a.Position.X; y = a.Position.Y; } _flashlight.W = _flashlight.XStrips * 8; _flashlight.H = _flashlight.YStrips * 8; _flashlight.X = x - _flashlight.W / 2 - _screenStartStrip * 8; _flashlight.Y = y - _flashlight.H / 2; if (Game.GameId == GameId.Loom) { _flashlight.Y -= 12; } // Clip the flashlight at the borders if (_flashlight.X < 0) { _flashlight.X = 0; } else if (_flashlight.X + _flashlight.W > Gdi.NumStrips * 8) { _flashlight.X = Gdi.NumStrips * 8 - _flashlight.W; } if (_flashlight.Y < 0) { _flashlight.Y = 0; } else if (_flashlight.Y + _flashlight.H > vs.Height) { _flashlight.Y = vs.Height - _flashlight.H; } // Redraw any actors "under" the flashlight for (i = _flashlight.X / 8; i < (_flashlight.X + _flashlight.W) / 8; i++) { Debug.Assert(0 <= i && i < Gdi.NumStrips); Gdi.SetGfxUsageBit(_screenStartStrip + i, Gdi.UsageBitDirty); vs.TDirty[i] = 0; vs.BDirty[i] = vs.Height; } var pn = new PixelNavigator(vs.Surfaces[0]); pn.GoTo(_flashlight.X + vs.XStart, _flashlight.Y); pn = new PixelNavigator(pn); _flashlight.PixelNavigator = pn; var bgbak = new PixelNavigator(vs.Surfaces[1]); bgbak.GoTo(_flashlight.X + vs.XStart, _flashlight.Y); Gdi.Blit(pn, bgbak, _flashlight.W, _flashlight.H); // Round the corners. To do so, we simply hard-code a set of nicely // rounded corners. var corner_data = new [] { 8, 6, 4, 3, 2, 2, 1, 1 }; int minrow = 0; int maxcol = (_flashlight.W - 1); int maxrow = (_flashlight.H - 1); for (i = 0; i < 8; i++, minrow++, maxrow--) { int d = corner_data[i]; for (j = 0; j < d; j++) { if (vs.BytesPerPixel == 2) { pn.GoTo(j, minrow); pn.WriteUInt16(0); pn.GoTo(maxcol - j, minrow); pn.WriteUInt16(0); pn.GoTo(j, maxrow); pn.WriteUInt16(0); pn.GoTo(maxcol - j, maxrow); pn.WriteUInt16(0); } else { pn.GoTo(j, minrow); pn.Write(0); pn.GoTo(maxcol - j, minrow); pn.Write(0); pn.GoTo(j, maxrow); pn.Write(0); pn.GoTo(maxcol - j, maxrow); pn.Write(0); } } } _flashlight.IsDrawn = true; }
void RoomOps() { bool paramsBeforeOpcode = (Game.Version == 3); int a = 0; int b = 0; if (paramsBeforeOpcode) { a = GetVarOrDirectWord(OpCodeParameter.Param1); b = GetVarOrDirectWord(OpCodeParameter.Param2); } _opCode = ReadByte(); switch (_opCode & 0x1F) { case 1: // SO_ROOM_SCROLL { if (!paramsBeforeOpcode) { a = GetVarOrDirectWord(OpCodeParameter.Param1); b = GetVarOrDirectWord(OpCodeParameter.Param2); } if (a < (ScreenWidth / 2)) { a = (ScreenWidth / 2); } if (b < (ScreenWidth / 2)) { b = (ScreenWidth / 2); } if (a > roomData.Header.Width - (ScreenWidth / 2)) { a = roomData.Header.Width - (ScreenWidth / 2); } if (b > roomData.Header.Width - (ScreenWidth / 2)) { b = roomData.Header.Width - (ScreenWidth / 2); } Variables[VariableCameraMinX.Value] = a; Variables[VariableCameraMaxX.Value] = b; } break; case 2: // SO_ROOM_COLOR { if (Game.Version < 5) { if (!paramsBeforeOpcode) { a = GetVarOrDirectWord(OpCodeParameter.Param1); b = GetVarOrDirectWord(OpCodeParameter.Param2); } ScummHelper.AssertRange(0, a, 256, "RoomOps: 2: room color slot"); Gdi.RoomPalette[b] = (byte)a; _fullRedraw = true; } else { throw new NotSupportedException("room-color is no longer a valid command"); } } break; case 3: // SO_ROOM_SCREEN { if (!paramsBeforeOpcode) { a = GetVarOrDirectWord(OpCodeParameter.Param1); b = GetVarOrDirectWord(OpCodeParameter.Param2); } InitScreens(a, b); } break; case 4: // SO_ROOM_PALETTE { if (Game.Version < 5) { if (!paramsBeforeOpcode) { a = GetVarOrDirectWord(OpCodeParameter.Param1); b = GetVarOrDirectWord(OpCodeParameter.Param2); } ScummHelper.AssertRange(0, a, 256, "RoomOps: 4: room color slot"); _shadowPalette[b] = (byte)a; SetDirtyColors(b, b); } else { a = GetVarOrDirectWord(OpCodeParameter.Param1); b = GetVarOrDirectWord(OpCodeParameter.Param2); var c = GetVarOrDirectWord(OpCodeParameter.Param3); _opCode = ReadByte(); var d = GetVarOrDirectByte(OpCodeParameter.Param1); SetPalColor(d, a, b, c); /* index, r, g, b */ } } break; case 5: // SO_ROOM_SHAKE_ON SetShake(true); break; case 6: // SO_ROOM_SHAKE_OFF SetShake(false); break; case 7: // SO_ROOM_SCALE { a = GetVarOrDirectByte(OpCodeParameter.Param1); b = GetVarOrDirectByte(OpCodeParameter.Param2); _opCode = ReadByte(); var c = GetVarOrDirectByte(OpCodeParameter.Param1); var d = GetVarOrDirectByte(OpCodeParameter.Param2); _opCode = ReadByte(); var e = GetVarOrDirectByte(OpCodeParameter.Param2); SetScaleSlot(e - 1, 0, b, a, 0, d, c); } break; case 8: // SO_ROOM_INTENSITY { a = GetVarOrDirectByte(OpCodeParameter.Param1); b = GetVarOrDirectByte(OpCodeParameter.Param2); var c = GetVarOrDirectByte(OpCodeParameter.Param3); DarkenPalette(a, a, a, b, c); } break; case 9: // SO_ROOM_SAVEGAME { _saveLoadFlag = GetVarOrDirectByte(OpCodeParameter.Param1); _saveLoadSlot = GetVarOrDirectByte(OpCodeParameter.Param2); _saveLoadSlot = 99; /* use this slot */ _saveTemporaryState = true; } break; case 10: // SO_ROOM_FADE { a = GetVarOrDirectWord(OpCodeParameter.Param1); if (a != 0) { if (Game.Platform == Platform.FMTowns) { switch (a) { case 8: TownsDrawStripToScreen(MainVirtScreen, 0, MainVirtScreen.TopLine, 0, 0, MainVirtScreen.Width, MainVirtScreen.TopLine + MainVirtScreen.Height); _townsScreen.Update(); return; case 9: _townsActiveLayerFlags = 2; _townsScreen.ToggleLayers(_townsActiveLayerFlags); return; case 10: _townsActiveLayerFlags = 3; _townsScreen.ToggleLayers(_townsActiveLayerFlags); return; case 11: _townsScreen.ClearLayer(1); return; case 12: _townsActiveLayerFlags = 0; _townsScreen.ToggleLayers(_townsActiveLayerFlags); return; case 13: _townsActiveLayerFlags = 1; _townsScreen.ToggleLayers(_townsActiveLayerFlags); return; case 16: // enable clearing of layer 2 buffer in drawBitmap() TownsPaletteFlags |= 2; return; case 17: // disable clearing of layer 2 buffer in drawBitmap() TownsPaletteFlags &= ~2; return; case 18: // clear kMainVirtScreen layer 2 buffer Gdi.Fill(TextSurface, new Rect(0, MainVirtScreen.TopLine * TextSurfaceMultiplier, TextSurface.Pitch, (MainVirtScreen.TopLine + MainVirtScreen.Height) * TextSurfaceMultiplier), 0); TownsPaletteFlags |= 1; return; case 19: // enable palette operations (palManipulate(), cyclePalette() etc.) TownsPaletteFlags |= 1; return; case 20: // disable palette operations TownsPaletteFlags &= ~1; return; case 21: // disable clearing of layer 0 in initScreens() _townsClearLayerFlag = 1; return; case 22: // enable clearing of layer 0 in initScreens() _townsClearLayerFlag = 0; return; case 30: TownsOverrideShadowColor = 3; return; } } _switchRoomEffect = (byte)(a & 0xFF); _switchRoomEffect2 = (byte)(a >> 8); } else { FadeIn(_newEffect); } } break; case 11: // SO_RGB_ROOM_INTENSITY { a = GetVarOrDirectWord(OpCodeParameter.Param1); b = GetVarOrDirectWord(OpCodeParameter.Param2); var c = GetVarOrDirectWord(OpCodeParameter.Param3); _opCode = ReadByte(); var d = GetVarOrDirectByte(OpCodeParameter.Param1); var e = GetVarOrDirectByte(OpCodeParameter.Param2); DarkenPalette(a, b, c, d, e); } break; case 12: // SO_ROOM_SHADOW { a = GetVarOrDirectWord(OpCodeParameter.Param1); b = GetVarOrDirectWord(OpCodeParameter.Param2); var c = GetVarOrDirectWord(OpCodeParameter.Param3); _opCode = ReadByte(); var d = GetVarOrDirectByte(OpCodeParameter.Param1); var e = GetVarOrDirectByte(OpCodeParameter.Param2); SetShadowPalette(a, b, c, d, e, 0, 256); } break; case 13: // SO_SAVE_STRING { // This subopcode is used in Indy 4 to save the IQ points // data. No other LucasArts game uses it. We use this fact // to substitute a filename based on the targetname // ("TARGET.iq"). // // This way, the iq data of each Indy 4 variant stays // separate. Moreover, the filename now clearly reflects to // which target it belongs (as it should). // // In addition, the Monkey Island fan patch (which adds // speech support and more things to MI 1 and 2) uses // this opcode to generate a "monkey.cfg" file containing. // some user controllable settings. // Once more we use a custom filename ("TARGET.cfg"). var index = GetVarOrDirectByte(OpCodeParameter.Param1); var filename = System.Text.Encoding.UTF8.GetString(ReadCharacters()); filename = GetIqFilename(filename); using (var file = ServiceLocator.FileStorage.OpenFileWrite(filename)) { var str = _strings[index]; file.Write(str, 0, str.Length); Variables[VariableSoundResult.Value] = 0; } break; } case 14: // SO_LOAD_STRING { // This subopcode is used in Indy 4 to load the IQ points data. // See SO_SAVE_STRING for details var index = GetVarOrDirectByte(OpCodeParameter.Param1); var filename = System.Text.Encoding.UTF8.GetString(ReadCharacters()); filename = GetIqFilename(filename); if (ServiceLocator.FileStorage.FileExists(filename)) { _strings[index] = ServiceLocator.FileStorage.ReadAllBytes(filename); } } break; case 15: // SO_ROOM_TRANSFORM { a = GetVarOrDirectByte(OpCodeParameter.Param1); _opCode = ReadByte(); b = GetVarOrDirectByte(OpCodeParameter.Param1); var c = GetVarOrDirectByte(OpCodeParameter.Param2); _opCode = ReadByte(); var d = GetVarOrDirectByte(OpCodeParameter.Param1); PalManipulateInit(a, b, c, d); } break; case 16: // SO_CYCLE_SPEED { a = GetVarOrDirectByte(OpCodeParameter.Param1); b = GetVarOrDirectByte(OpCodeParameter.Param2); ScummHelper.AssertRange(1, a, 16, "o5_roomOps: 16: color cycle"); _colorCycle[a - 1].Delay = (ushort)((b != 0) ? 0x4000 / (b * 0x4C) : 0); } break; default: throw new NotImplementedException(); } }