diff --git a/Braver.Core/SaveData.cs b/Braver.Core/SaveData.cs index 1007f3b..8b3f814 100644 --- a/Braver.Core/SaveData.cs +++ b/Braver.Core/SaveData.cs @@ -56,6 +56,7 @@ namespace Braver { Party3 = 0x40, Available = 0x100, + Locked = 0x200, ANY_PARTY_SLOT = Party1 | Party2 | Party3, } diff --git a/F7/Braver.csproj b/F7/Braver.csproj index 36cb855..29301b4 100644 --- a/F7/Braver.csproj +++ b/F7/Braver.csproj @@ -10,6 +10,7 @@ app.manifest Icon.ico + True diff --git a/F7/FGame.cs b/F7/FGame.cs index 547da66..9dae5fe 100644 --- a/F7/FGame.cs +++ b/F7/FGame.cs @@ -31,6 +31,7 @@ namespace Braver { public Audio Audio { get; } public Screen Screen => _screens.Peek(); + public string FFMpegPath { get; private set; } //TODO - better place to put/calculate this? private Dictionary _prefs; @@ -64,6 +65,7 @@ namespace Braver { L = _data[category] = new(); L.Add(new FileDataSource(dir)); } + FFMpegPath = Path.Combine(bdata, "ffmpeg.exe"); Audio = new Audio(this, data); diff --git a/F7/Field/Background.cs b/F7/Field/Background.cs index cbf3c56..5fad7f3 100644 --- a/F7/Field/Background.cs +++ b/F7/Field/Background.cs @@ -18,20 +18,12 @@ namespace Braver.Field { public int MinX { get; private set; } public int MinY { get; private set; } - private enum BlendType { - Blend, - Additive, - Subtractive, - QuarterAdd, - None = 0xff, - } - private class TexLayer { public Texture2D Tex; public VertexPositionTexture[] Verts; public IEnumerable Sprites; public List Data; - public BlendType Blend; + public Ficedula.FF7.Field.BlendType Blend; public int Parameter; public int Mask; public int OffsetX, OffsetY; @@ -110,7 +102,7 @@ namespace Braver.Field { Tex = new Texture2D(graphics, texWidth, texHeight, false, SurfaceFormat.Color), OffsetX = -minX, OffsetY = -minY, - Blend = (BlendType)group.First().TypeTrans, + Blend = (Ficedula.FF7.Field.BlendType)group.First().TypeTrans, Sprites = group.ToArray(), Data = Enumerable.Range(0, texHeight) .Select(_ => new uint[texWidth]) @@ -184,11 +176,11 @@ namespace Braver.Field { continue; switch (layer.Blend) { - case BlendType.None: - case BlendType.Blend: + case Ficedula.FF7.Field.BlendType.None: + case Ficedula.FF7.Field.BlendType.Blend: _graphics.BlendState = BlendState.AlphaBlend; break; - case BlendType.Additive: + case Ficedula.FF7.Field.BlendType.Additive: _graphics.BlendState = BlendState.Additive; break; default: //TODO NO diff --git a/F7/Field/FieldScreen.cs b/F7/Field/FieldScreen.cs index 38de848..ca87702 100644 --- a/F7/Field/FieldScreen.cs +++ b/F7/Field/FieldScreen.cs @@ -1,17 +1,29 @@ using Ficedula.FF7.Field; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; using System; using System.Collections.Generic; -using System.Configuration; using System.Linq; -using System.Net.Http.Headers; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Serialization; namespace Braver.Field { + [Flags] + public enum BattleFlags { + None = 0, + TimedBattle = 0x2, + Preemptive = 0x4, + NoEscape = 0x8, + NoVictoryMusic = 0x20, + BattleArena = 0x40, + NoVictoryScreens = 0x80, + NoGameOver = 0x100, + } + + public class BattleOptions { + public string OverrideMusic { get; set; } + public string PostBattleMusic { get; set; } //will play in field + public bool BattlesEnabled { get; set; } = false; //TODO - reasonable default? + public BattleFlags Flags { get; set; } = BattleFlags.None; + } + public class FieldInfo { public float BGZFrom { get; set; } public float BGZTo { get; set; } @@ -38,6 +50,7 @@ namespace Braver.Field { MenuEnabled = 0x4, CameraTracksPlayer = 0x8, CameraIsAsyncScrolling = 0x10, + MusicLocked = 0x20, DEFAULT = PlayerControls | MenuEnabled | CameraTracksPlayer, } @@ -65,14 +78,16 @@ namespace Braver.Field { public HashSet DisabledWalkmeshTriangles { get; } = new HashSet(); public Background Background { get; private set; } + public Movie Movie { get; private set; } public List Entities { get; private set; } public Entity Player { get; private set; } public List FieldModels { get; private set; } public DialogEvent FieldDialog { get; private set; } public FieldOptions Options { get; set; } = FieldOptions.DEFAULT; public Dialog Dialog { get; private set; } + public Overlay Overlay { get; private set; } - public string OverrideBattleMusic { get; set; } + public BattleOptions BattleOptions { get; } = new(); private HashSet _activeTriggers = new(); @@ -89,6 +104,8 @@ namespace Braver.Field { g.Net.Listen(this); g.Net.Listen(this); + Overlay = new Overlay(g, graphics); + var mapList = g.Singleton(() => new MapList(g.Open("field", "maplist"))); string file = mapList.Items[_destination.DestinationFieldID]; @@ -97,6 +114,7 @@ namespace Braver.Field { using (var s = g.Open("field", file)) { var field = new FieldFile(s); Background = new Background(g, graphics, field.GetBackground()); + Movie = new Movie(g, graphics); FieldDialog = field.GetDialogEvent(); Entities = FieldDialog.Entities @@ -297,7 +315,10 @@ namespace Braver.Field { } BringPlayerIntoView(); - FadeIn(null); + + if (!Overlay.HasTriggered) + Overlay.Fade(30, GraphicsUtil.BlendSubtractive, Color.White, Color.Black, null); + g.Net.Send(new Net.ScreenReadyMessage()); } Entity.DEBUG_OUT = false; @@ -311,8 +332,12 @@ namespace Braver.Field { protected override void DoRender() { Graphics.DepthStencilState = DepthStencilState.Default; Graphics.BlendState = BlendState.AlphaBlend; - if (_renderBG) - Background.Render(_view2D, _bgZFrom, _bgZTo); + if (_renderBG) { + if (Movie.Active) + Movie.Render(); + else + Background.Render(_view2D, _bgZFrom, _bgZTo); + } if (_renderDebug) _debug.Render(_view3D); @@ -322,6 +347,8 @@ namespace Braver.Field { if ((entity.Model != null) && entity.Model.Visible) entity.Model.Render(_view3D); + Overlay.Render(); + Dialog.Render(); } @@ -340,6 +367,7 @@ namespace Braver.Field { protected override void DoStep(GameTime elapsed) { if (Game.Net is Net.Server) { if ((frame % 2) == 0) { + Overlay.Step(); foreach (var entity in Entities) { entity.Run(1000); entity.Model?.FrameStep(); @@ -353,6 +381,7 @@ namespace Braver.Field { } Dialog.Step(); + Movie.Step(); Background.Step(); } else { if ((frame % 2) == 0) { @@ -786,6 +815,8 @@ namespace Braver.Field { if (newHeight != null) { //We're staying in the same tri, so just update height eMove.Model.Translation = newPosition.WithZ(newHeight.Value); + if (eMove.Name == "ba") + System.Diagnostics.Debug.WriteLine($"Moving {eMove.Name} to {eMove.Model.Translation}"); return true; } else { if (!DoesLeaveTri(eMove.Model.Translation.XY(), newPosition.XY(), currentTri, out float dist, out short? newTri)) { @@ -819,6 +850,8 @@ namespace Braver.Field { } testLocation = testFrom; + if (eMove.Name == "ba") + System.Diagnostics.Debug.WriteLine($"Moving {eMove.Name} from wmtri {eMove.WalkmeshTri} to {newTri.Value}"); movingToTri = _walkmesh[newTri.Value]; newHeight = HeightInTriangle( movingToTri.V0.ToX(), movingToTri.V1.ToX(), movingToTri.V2.ToX(), @@ -828,8 +861,9 @@ namespace Braver.Field { eMove.WalkmeshTri = newTri.Value; eMove.Model.Translation = newPosition.WithZ(newHeight.Value); - return true; - + if (eMove.Name == "ba") + System.Diagnostics.Debug.WriteLine($"Moving {eMove.Name} to {eMove.Model.Translation}"); + return true; } } } diff --git a/F7/Field/Movie.cs b/F7/Field/Movie.cs new file mode 100644 index 0000000..4beac97 --- /dev/null +++ b/F7/Field/Movie.cs @@ -0,0 +1,217 @@ +using Microsoft.Xna.Framework.Audio; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Braver.Field { + public class Movie { + + private int _frame; + private SpriteBatch _spriteBatch; + private GraphicsDevice _graphics; + + public int Frame => _frame; + public bool Active => (_process != null) && (_frame >= 0); + + private string _ffPath; + + private Process _process; + private Action _onComplete; + private SoundEffect _soundEffect; + private SoundEffectInstance _effectInstance; + private Texture2D _texture; + private float _gameFramesPerVideoFrame, _frameIncrement; + private byte[] _framebuffer; + + private static Regex _reSize = new Regex(@"(\d+)x(\d+)"); + + private string[] _files; + + public Movie(FGame g, GraphicsDevice graphics) { + _graphics = graphics; + _spriteBatch = new SpriteBatch(graphics); + _ffPath = g.FFMpegPath; + + _files = g.OpenString("movies", "movielist.txt") + .Split('\n') + .Select(s => s.Trim('\r')) + .Select(s => Path.Combine(Path.GetDirectoryName(_ffPath), "movies", s + ".mp4")) //TODO!!! + .ToArray(); + } + + private unsafe void GetAudioData(string filename, string customCommand) { + + var psi = new ProcessStartInfo { + FileName = _ffPath, + ArgumentList = { + "-i", filename, + "-map", "0:a", + "-acodec", "pcm_s16le", + "-f", "s16le", + "-" + }, + RedirectStandardError = true, + RedirectStandardInput = true, + RedirectStandardOutput = true, + CreateNoWindow = true, + }; + + if (!string.IsNullOrWhiteSpace(customCommand)) { + psi.ArgumentList.Clear(); + psi.Arguments = string.Format(customCommand, filename); + } + + _process = Process.Start(psi); + + bool stereo = false; + int freq = 0; + + do { + string s = _process.StandardError.ReadLine(); + if (s == null) break; + if (s.Contains("Audio: pcm_s16le")) { + foreach (string part in s.Split(',')) { + if (part.EndsWith("Hz")) + freq = int.Parse(part.Substring(0, part.Length - 2).Trim()); + else if (part.Trim().Equals("stereo", StringComparison.InvariantCultureIgnoreCase)) + stereo = true; + } + } + } while (freq == 0); + + _process.StandardError.Close(); + + if (freq > 0) { + var ms = new System.IO.MemoryStream(); + _process.StandardOutput.BaseStream.CopyTo(ms); + byte[] data = ms.ToArray(); + + _soundEffect = new SoundEffect(data, freq, stereo ? AudioChannels.Stereo : AudioChannels.Mono); + _effectInstance = _soundEffect.CreateInstance(); + } + + } + + + public void Prepare(int movie) { + + string filename = _files[movie]; + + GetAudioData(filename, ""); + + string ffFormat; + int bytesPerPixel; + ffFormat = "rgba"; + bytesPerPixel = 4; + + var psi = new ProcessStartInfo { + FileName = _ffPath, + ArgumentList = { + "-hwaccel", "auto", + "-i", filename, + "-f", "image2pipe", + "-pix_fmt", ffFormat, + "-vcodec", "rawvideo", + "-blocksize", "65536", + "-" + }, + RedirectStandardError = true, + RedirectStandardInput = true, + RedirectStandardOutput = true, + CreateNoWindow = true, + }; + + _process = Process.Start(psi); + + int stride; + int width = 0, height = 0; + float fps = 0; + + do { + string s = _process.StandardError.ReadLine(); + if (s == null) return; + if (s.Contains("Video: rawvideo")) { + foreach (string part in s.Split(',')) { + if (part.EndsWith("fps")) + fps = float.Parse(part.Substring(0, part.Length - 3).Trim()); + var m = _reSize.Match(part.Trim()); + if (m.Success) { + width = int.Parse(m.Groups[1].Value); + height = int.Parse(m.Groups[2].Value); + } + } + } + } while (width == 0); + + _process.StandardError.Close(); + if (fps == 0) fps = 30; + + _gameFramesPerVideoFrame = 60 / fps; + _frameIncrement = 0; + + stride = width * bytesPerPixel; + /* //TODO: Necessary or not? + if ((stride % 32) != 0) + stride += 32 - (stride % 32); + */ + + _texture = new Texture2D(_graphics, stride / bytesPerPixel, height, false, SurfaceFormat.Color); + _framebuffer = new byte[_texture.Width * _texture.Height * 4]; + _texture.SetData(_framebuffer); + + _frame = -1; + } + + public void Play(Action onComplete) { + _frame = 0; + _effectInstance?.Play(); + _onComplete = onComplete; + } + + public void Render() { + if (_frame >= 0) { + _spriteBatch.Begin(depthStencilState: DepthStencilState.None); + + _spriteBatch.Draw(_texture, new Rectangle(0, 0, _graphics.Viewport.Width, _graphics.Viewport.Height), Color.White); + + _spriteBatch.End(); + } + } + + public unsafe void Step() { + if (_process == null) return; + + _frameIncrement++; + + if (_frameIncrement >= _gameFramesPerVideoFrame) { + int read = 0, size = _texture.Width * _texture.Height * 4; + fixed (byte* baseptr = &_framebuffer[0]) { + while (read < size) { + byte* ptr = baseptr + read; + int count = _process.StandardOutput.BaseStream.Read(new Span(ptr, size - read)); + + if (count <= 0) { + _process.Kill(); + _process.Dispose(); + _process = null; + _onComplete?.Invoke(); + break; + } + + read += count; + } + } + _texture.SetData(_framebuffer); + _frame++; + _frameIncrement -= _gameFramesPerVideoFrame; + } else { + // + } + } + } +} diff --git a/F7/Field/Overlay.cs b/F7/Field/Overlay.cs new file mode 100644 index 0000000..b4990cf --- /dev/null +++ b/F7/Field/Overlay.cs @@ -0,0 +1,56 @@ +using Braver.UI; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Braver.Field { + public class Overlay { + + private Color _color = Color.Black; + private SpriteBatch _spriteBatch; + private BlendState _blend; + private Texture2D _tex; + private Rectangle _rect; + + private Action _onComplete; + private int _progress, _duration; + private Color _cFrom, _cTo; + + public bool HasTriggered { get; private set; } + + public Overlay(FGame g, GraphicsDevice graphics) { + _spriteBatch = new SpriteBatch(graphics); + g.Singleton(() => new CompositeImages(graphics, g)).Find("white", out _tex, out _rect, out _); + } + + public void Fade(int frames, BlendState blend, Color cFrom, Color cTo, Action onComplete) { + _color = _cFrom = cFrom; + _cTo = cTo; + _onComplete = onComplete; + _progress = 0; + _duration = frames; + _blend = blend; + HasTriggered = true; + } + + public void Render() { + if ((_color.PackedValue & 0xffffff) != 0) { + _spriteBatch.Begin(blendState: _blend, depthStencilState: DepthStencilState.None); + _spriteBatch.Draw(_tex, new Rectangle(0, 0, 9000, 9000), _rect, _color); + _spriteBatch.End(); + } + } + + public void Step() { + if (_progress == _duration) { + _color = _cTo; + _onComplete?.Invoke(); + } else { + _progress++; + _color = Color.Lerp(_cFrom, _cTo, 1f * _progress / _duration); + } + } + } +} diff --git a/F7/Field/VM.cs b/F7/Field/VM.cs index e39fe50..fdb4fab 100644 --- a/F7/Field/VM.cs +++ b/F7/Field/VM.cs @@ -1,15 +1,7 @@ -using Microsoft.Xna.Framework; -using NAudio.Gui; -using SharpDX.Direct2D1; -using SharpDX.Direct3D9; -using SixLabors.ImageSharp; -using System; +using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; -using System.Net; -using System.Text; -using System.Threading.Tasks; -using static System.Windows.Forms.VisualStyles.VisualStyleElement; namespace Braver.Field { public enum OpCode { @@ -383,6 +375,7 @@ namespace Braver.Field { Register(typeof(PartyInventory)); Register(typeof(FieldModels)); Register(typeof(Maths)); + Register(typeof(SystemControl)); } public static OpResult Execute(OpCode op, Fiber f, Entity e, FieldScreen s) { @@ -783,6 +776,17 @@ namespace Braver.Field { return OpResult.Continue; } + public static OpResult CANIM2(Fiber f, Entity e, FieldScreen s) { + byte anim = f.ReadU8(), first = f.ReadU8(), last = f.ReadU8(), speed = f.ReadU8(); + f.Pause(); + var state = e.Model.AnimationState; + Action onComplete = () => { + f.Resume(); + e.Model.AnimationState = state; + }; + e.Model.PlayAnimation(anim, false, 1f / speed, onComplete, first, last); //TODO is this speed even vaguely correct? + return OpResult.Continue; + } public static OpResult ANIME1(Fiber f, Entity e, FieldScreen s) { byte anim = f.ReadU8(), speed = f.ReadU8(); f.Pause(); @@ -937,6 +941,11 @@ namespace Braver.Field { return OpResult.Continue; } + public static OpResult MMBLK(Fiber f, Entity e, FieldScreen s) { + byte charID = f.ReadU8(); + s.Game.SaveData.Characters[charID].Flags |= CharFlags.Locked; + return OpResult.Continue; + } } internal static class WindowMenu { @@ -1051,14 +1060,76 @@ if (y + h + MIN_WINDOW_DISTANCE > GAME_HEIGHT) { y = GAME_HEIGHT - h - MIN_WINDO public static OpResult MUSIC(Fiber f, Entity e, FieldScreen s) { byte track = f.ReadU8(); - s.Game.Audio.PlayMusic(_trackNames[s.FieldDialog.AkaoMusicIDs[track]]); + if (!s.Options.HasFlag(FieldOptions.MusicLocked)) + s.Game.Audio.PlayMusic(_trackNames[s.FieldDialog.AkaoMusicIDs[track]]); return OpResult.Continue; } public static OpResult BMUSC(Fiber f, Entity e, FieldScreen s) { byte track = f.ReadU8(); - s.OverrideBattleMusic = _trackNames[s.FieldDialog.AkaoMusicIDs[track]]; + s.BattleOptions.OverrideMusic = _trackNames[s.FieldDialog.AkaoMusicIDs[track]]; return OpResult.Continue; } + public static OpResult FMUSC(Fiber f, Entity e, FieldScreen s) { + byte track = f.ReadU8(); + s.BattleOptions.PostBattleMusic = _trackNames[s.FieldDialog.AkaoMusicIDs[track]]; + return OpResult.Continue; + } + public static OpResult MULCK(Fiber f, Entity e, FieldScreen s) { + byte locked = f.ReadU8(); + if (locked != 0) + s.Options |= FieldOptions.MusicLocked; + else + s.Options &= ~FieldOptions.MusicLocked; + return OpResult.Continue; + } + + private static OpResult DoAKAO(Fiber f, Entity e, FieldScreen s, byte op, int parm1, int parm2, int parm3, int parm4, int parm5) { + switch (op) { + case 0xc8: + case 0xc9: + case 0xca: + //Music pan - Not implemented in original FF7 (but maybe we could?) + break; + + case 0xf0: + if (!s.Options.HasFlag(FieldOptions.MusicLocked)) + s.Game.Audio.StopMusic(); + break; + + default: //TODO - all ops! + throw new NotImplementedException(); + } + + return OpResult.Continue; + } + + public static OpResult AKAO(Fiber f, Entity e, FieldScreen s) { + byte bank12 = f.ReadU8(), bank34 = f.ReadU8(), bank5 = f.ReadU8(), + op = f.ReadU8(), p1 = f.ReadU8(); + ushort p2 = f.ReadU16(), p3 = f.ReadU16(), p4 = f.ReadU16(), p5 = f.ReadU16(); + + int parm1 = s.Game.Memory.Read(bank12 >> 4, p1), + parm2 = s.Game.Memory.Read(bank12 & 0xf, p2), + parm3 = s.Game.Memory.Read(bank34 >> 4, p3), + parm4 = s.Game.Memory.Read(bank34 & 0xf, p4), + parm5 = s.Game.Memory.Read(bank5 >> 4, p5); + + return DoAKAO(f, e, s, op, parm1, parm2, parm3, parm4, parm5); + } + public static OpResult AKAO2(Fiber f, Entity e, FieldScreen s) { + byte bank12 = f.ReadU8(), bank34 = f.ReadU8(), bank5 = f.ReadU8(), + op = f.ReadU8(); + ushort p1 = f.ReadU16(), p2 = f.ReadU16(), p3 = f.ReadU16(), p4 = f.ReadU16(), p5 = f.ReadU16(); + + int parm1 = s.Game.Memory.Read(bank12 >> 4, p1), + parm2 = s.Game.Memory.Read(bank12 & 0xf, p2), + parm3 = s.Game.Memory.Read(bank34 >> 4, p3), + parm4 = s.Game.Memory.Read(bank34 & 0xf, p4), + parm5 = s.Game.Memory.Read(bank5 >> 4, p5); + + return DoAKAO(f, e, s, op, parm1, parm2, parm3, parm4, parm5); + } + public static OpResult SOUND(Fiber f, Entity e, FieldScreen s) { byte banks = f.ReadU8(); @@ -1070,6 +1141,23 @@ if (y + h + MIN_WINDOW_DISTANCE > GAME_HEIGHT) { y = GAME_HEIGHT - h - MIN_WINDO return OpResult.Continue; } + public static OpResult PMVIE(Fiber f, Entity e, FieldScreen s) { + byte which = f.ReadU8(); + s.Movie.Prepare(which); + return OpResult.Continue; + } + public static OpResult MOVIE(Fiber f, Entity e, FieldScreen s) { + f.Pause(); + s.Movie.Play(f.Resume); + return OpResult.Continue; + } + public static OpResult MVIEF(Fiber f, Entity e, FieldScreen s) { + byte bank = f.ReadU8(), addr = f.ReadU8(); + s.Game.Memory.Write(bank, addr, (ushort)Math.Max(0, s.Movie.Frame)); + return OpResult.Continue; + } + + public static OpResult SCRLW(Fiber f, Entity e, FieldScreen s) { if (s.Options.HasFlag(FieldOptions.CameraIsAsyncScrolling)) return OpResult.Restart; @@ -1087,6 +1175,41 @@ if (y + h + MIN_WINDOW_DISTANCE > GAME_HEIGHT) { y = GAME_HEIGHT - h - MIN_WINDO return OpResult.Continue; } + private class ScrollState { + public Vector2 Start; + public int Frame; + } + public static OpResult SCR2DC(Fiber f, Entity e, FieldScreen s) { + byte banks = f.ReadU8(), bankS = f.ReadU8(); + short sx = f.ReadS16(), sy = f.ReadS16(), ss = f.ReadS16(); + int x = s.Game.Memory.Read(banks & 0xf, sx), + y = s.Game.Memory.Read(banks >> 4, sy), + speed = s.Game.Memory.Read(bankS, ss); + + ScrollState state; + if (f.ResumeState == null) { + var scroll = s.GetBGScroll(); + state = new ScrollState { + Start = new Vector2(scroll.x, scroll.y), + }; + f.ResumeState = state; + } else + state = (ScrollState)f.ResumeState; + + Vector2 end = new Vector2(x, y); + var progress = Easings.QuadraticInOut(1f * state.Frame / speed); //TODO - is interpreting speed as framecount vaguely correct? + + if (progress >= 1f) { + s.BGScroll(x, y); + return OpResult.Continue; + } else { + var pos = state.Start + (end - state.Start) * progress; + s.BGScroll(pos.X, pos.Y); + state.Frame++; + return OpResult.Restart; + } + } + public static OpResult SCRLC(Fiber f, Entity e, FieldScreen s) { byte bank = f.ReadU8(); byte sspeed = f.ReadU8(); @@ -1157,6 +1280,42 @@ if (y + h + MIN_WINDOW_DISTANCE > GAME_HEIGHT) { y = GAME_HEIGHT - h - MIN_WINDO return OpResult.Continue; } + + private static OpResult DoFade(Fiber f, Entity e, FieldScreen s, byte bankRG, byte bankB, + byte r, byte g, byte b, byte frames, byte fadeType, byte adjust) { + + int cR = (byte)s.Game.Memory.Read(bankRG >> 4, r), + cG = (byte)s.Game.Memory.Read(bankRG & 0xf, g), + cB = (byte)s.Game.Memory.Read(bankB, b); + + Color cStandard = new Color(cR, cG, cB, 0xff), + cInverse = new Color(0xff - cR, 0xff - cG, 0xff - cB, 0xff), + cInverse4 = new Color(4 * (0xff - cR), 4 * (0xff - cG), 4 * (0xff - cB), 0xff); + + switch (fadeType) { + case 0: //NFADE type 0 + case 4: //FADE type 4 - both basically the same...? + s.Overlay.Fade(0, BlendState.Additive, Color.Black, Color.Black, null); + return OpResult.Continue; + default: //TODO - other types! + throw new NotImplementedException(); + } + } + + public static OpResult NFADE(Fiber f, Entity e, FieldScreen s) { + byte bankRG = f.ReadU8(), bankB = f.ReadU8(), fadeType = f.ReadU8(), + r = f.ReadU8(), g = f.ReadU8(), b = f.ReadU8(), + speed = f.ReadU8(), adjust = f.ReadU8(); + + return DoFade(f, e, s, bankRG, bankB, r, g, b, speed, fadeType, adjust); + } + public static OpResult FADE(Fiber f, Entity e, FieldScreen s) { + byte bankRG = f.ReadU8(), bankB = f.ReadU8(), r = f.ReadU8(), g = f.ReadU8(), b = f.ReadU8(), + speed = f.ReadU8(), fadeType = f.ReadU8(), adjust = f.ReadU8(); + + return DoFade(f, e, s, bankRG, bankB, r, g, b, (byte)(240 / speed), fadeType, adjust); + } + } internal static class Maths { @@ -1214,4 +1373,16 @@ if (y + h + MIN_WINDOW_DISTANCE > GAME_HEIGHT) { y = GAME_HEIGHT - h - MIN_WINDO return OpResult.Continue; } } + + public static class SystemControl { + public static OpResult BTLON(Fiber f, Entity e, FieldScreen s) { + s.BattleOptions.BattlesEnabled = f.ReadU8() == 0; + return OpResult.Continue; + } + + public static OpResult BTLMD(Fiber f, Entity e, FieldScreen s) { + s.BattleOptions.Flags = (BattleFlags)f.ReadU16(); + return OpResult.Continue; + } + } } diff --git a/F7/GraphicsUtil.cs b/F7/GraphicsUtil.cs index 9d06a53..7d60199 100644 --- a/F7/GraphicsUtil.cs +++ b/F7/GraphicsUtil.cs @@ -33,6 +33,23 @@ namespace Braver { public static class GraphicsUtil { + public static readonly BlendState BlendSubtractive = new BlendState { + AlphaBlendFunction = BlendFunction.ReverseSubtract, + AlphaDestinationBlend = Blend.One, + AlphaSourceBlend = Blend.SourceAlpha, + ColorBlendFunction = BlendFunction.ReverseSubtract, + ColorDestinationBlend = Blend.One, + ColorSourceBlend = Blend.SourceAlpha, + }; + public static readonly BlendState BlendDarken = new BlendState { + AlphaBlendFunction = BlendFunction.ReverseSubtract, + AlphaDestinationBlend = Blend.One, + AlphaSourceBlend = Blend.One, + ColorBlendFunction = BlendFunction.ReverseSubtract, + ColorDestinationBlend = Blend.One, + ColorSourceBlend = Blend.InverseSourceColor, + }; + public static int MakePowerOfTwo(int i) { int n = 1; while (n < i) diff --git a/F7/TODO.txt b/F7/TODO.txt index 4a6babe..5550111 100644 --- a/F7/TODO.txt +++ b/F7/TODO.txt @@ -6,17 +6,17 @@ Background load razor engine - do other templates? Automatically load all of them after any specified "priority" templates like MainMenu? -Moving through walkmap triangles on field is still broken, esp. when running - IFKEYON etc not fully working - prob. due to IsJustPressed one-frame check conflicting with field scripts not running every frame? Shops! -ASK opcode - Compressed/single file saves Music looping -Item menu: Icons, actually do arrange \ No newline at end of file +Item menu: Icons, actually do arrange + +Field Fade network msg, movie msg + +NEED TO IMPLEMENT field walkmesh tri sliding for first map to complete! \ No newline at end of file diff --git a/F7/UI/Splash.cs b/F7/UI/Splash.cs index e797203..7124eae 100644 --- a/F7/UI/Splash.cs +++ b/F7/UI/Splash.cs @@ -59,11 +59,17 @@ namespace Braver.UI { Game.NewGame(); Game.ChangeScreen(this, new Field.FieldScreen( new Ficedula.FF7.Field.FieldDestination { - X = 7, Y = -107, Triangle = 21, + X = 3600, Y = 27320, Triangle = 28, Orientation = 132, - DestinationFieldID = 180, + DestinationFieldID = 116, } - + /* + new Ficedula.FF7.Field.FieldDestination { + X = 7, Y = -107, Triangle = 21, + Orientation = 132, + DestinationFieldID = 180, + } + */ /* new Ficedula.FF7.Field.FieldDestination { X = -225, Y = -830, Triangle = 152, diff --git a/data/movies/movielist.txt b/data/movies/movielist.txt new file mode 100644 index 0000000..c37a3eb --- /dev/null +++ b/data/movies/movielist.txt @@ -0,0 +1,54 @@ +fship2 + +d_ropego +d_ropein +u_ropein +u_ropego +gold2 +gold3 +gold4 +gold6 +gold5 +boogup +boogdown +junair_u +junair_d +junelein +junelego +junin_in +junin_go +moriya +mkup +northmk +mk8 +ontrain +mainplr +smk +southmk +plrexp +fallpl +monitor +bike +mtnvl +mtnvl2 +brgnvl +nvlmk +nivlsfs +jenova_e +junon +hiwind0 +mtcrl +gold1 +biskdead +boogdemo +boogstar +setogake +rcktfail +jairofly +jairofal +gold7 +gold7_2 +earithdd +funeral +car_1209 +opening \ No newline at end of file