mirror of
https://github.com/ficed/Braver.git
synced 2024-11-23 05:10:08 +00:00
Rework battle effects to use Razor scripts for control
Implement Fire1 as a first effect test, woo
This commit is contained in:
parent
50e725191f
commit
efc8eb0ac3
@ -126,6 +126,7 @@ namespace Braver {
|
||||
SaveMap = new SaveMap(Memory);
|
||||
}
|
||||
|
||||
public int GameTimeFrames => GameTimeSeconds * 30 + SaveMap.GameTimeFrames;
|
||||
public int GameTimeSeconds {
|
||||
get => SaveMap.GameTimeSeconds + 60 * SaveMap.GameTimeMinutes + 60 * 60 * SaveMap.GameTimeHours;
|
||||
set {
|
||||
|
@ -31,6 +31,7 @@ namespace Braver.Battle {
|
||||
public CombatStats BaseStats { get; }
|
||||
|
||||
public string Name { get; }
|
||||
public int ID { get; set; }
|
||||
|
||||
public int HP { get; set; }
|
||||
public int MaxHP { get; }
|
||||
@ -109,6 +110,7 @@ namespace Braver.Battle {
|
||||
private CombatStats _stats;
|
||||
|
||||
public string Name => _char.Name;
|
||||
public int ID { get; set; }
|
||||
public Character Character => _char;
|
||||
|
||||
public List<CharacterAction> Actions { get; } = new();
|
||||
@ -263,6 +265,7 @@ namespace Braver.Battle {
|
||||
private CombatStats _stats;
|
||||
|
||||
public string Name { get; private set; }
|
||||
public int ID { get; set; }
|
||||
|
||||
public EnemyInstance Enemy => _enemy;
|
||||
public AI AI { get; set; }
|
||||
|
@ -27,12 +27,6 @@ namespace Braver.Battle {
|
||||
Counter,
|
||||
}
|
||||
|
||||
public interface IInProgress {
|
||||
string Description { get; }
|
||||
bool Step(int frames); //return true if done
|
||||
bool IsIndefinite { get; }
|
||||
}
|
||||
|
||||
public class QueuedAction {
|
||||
public ICombatant Source { get; private set; }
|
||||
public Ability Ability { get; private set; }
|
||||
@ -104,6 +98,7 @@ namespace Braver.Battle {
|
||||
|
||||
|
||||
foreach(var comb in ActiveCombatants) {
|
||||
comb.ID = _combatants.IndexOf(comb);
|
||||
comb.VTimer = new Timer(_speedValue * 2, 8192, 0);
|
||||
comb.CTimer = new Timer(136, 8192, 0);
|
||||
|
||||
|
@ -37,18 +37,27 @@ namespace Braver {
|
||||
void SetMusicVolume(byte? volumeFrom, byte volumeTo, float duration);
|
||||
void PlayMusic(string name, bool pushContext = false);
|
||||
void StopMusic(bool popContext = false);
|
||||
void Precache(Sfx which, bool pin);
|
||||
void Precache(int which, bool pin);
|
||||
void ChannelProperty(int channel, float? pan, float? volume);
|
||||
void GetChannelProperty(int channel, out float? pan, out float? volume);
|
||||
void StopChannel(int channel);
|
||||
void StopLoopingSfx(bool includeChannels);
|
||||
void PlaySfx(int which, float volume, float pan, int? channel = null);
|
||||
void PlaySfx(Sfx which, float volume, float pan, int? channel = null);
|
||||
|
||||
IAudioItem LoadStream(string path, string file);
|
||||
IAudioItem TryLoadStream(string path, string file);
|
||||
void DecodeStream(Stream s, out byte[] rawData, out int channels, out int frequency);
|
||||
}
|
||||
|
||||
public static class AudioExtensions {
|
||||
public static void PlaySfx(this IAudio audio, Sfx which, float volume, float pan, int? channel = null) {
|
||||
audio.PlaySfx((int)which, volume, pan, channel);
|
||||
}
|
||||
|
||||
public static void Precache(this IAudio audio, Sfx which, bool pin) {
|
||||
audio.Precache((int)which, pin);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -75,6 +75,6 @@ namespace Braver.Plugins.UI {
|
||||
void BattleCharacterReady(ICombatant character);
|
||||
void BattleTargetHighlighted(IEnumerable<ICombatant> targets);
|
||||
void BattleActionStarted(string action);
|
||||
void BattleActionResult(IInProgress result);
|
||||
//void BattleActionResult(IInProgress result);
|
||||
}
|
||||
}
|
||||
|
@ -295,12 +295,14 @@ namespace Braver {
|
||||
};
|
||||
}
|
||||
|
||||
public void Precache(Sfx which, bool pin) {
|
||||
var effect = GetEffect((int)which);
|
||||
_sfx[(int)which] = new WeakReference<LoadedEffect>(effect);
|
||||
public void Precache(int which, bool pin) {
|
||||
var effect = GetEffect(which);
|
||||
_sfx[which] = new WeakReference<LoadedEffect>(effect);
|
||||
|
||||
if (pin)
|
||||
_pinned.Add(effect);
|
||||
else
|
||||
_recent0.Add(effect);
|
||||
}
|
||||
|
||||
public void StopChannel(int channel) {
|
||||
@ -339,7 +341,6 @@ namespace Braver {
|
||||
}
|
||||
|
||||
|
||||
public void PlaySfx(Sfx which, float volume, float pan, int? channel = null) => PlaySfx((int)which, volume, pan, channel);
|
||||
public void PlaySfx(int which, float volume, float pan, int? channel = null) {
|
||||
LoadedEffect effect;
|
||||
|
||||
|
@ -13,101 +13,64 @@ using System.Linq;
|
||||
|
||||
namespace Braver.Battle {
|
||||
|
||||
public class ActionInProgress {
|
||||
|
||||
private Dictionary<int?, List<IInProgress>> _items = new();
|
||||
private string _name;
|
||||
private Dictionary<IInProgress, int> _frames = new();
|
||||
private PluginInstances<IBattleUI> _plugins;
|
||||
|
||||
public bool IsComplete => !_items.Keys.Any(phase => phase != null);
|
||||
|
||||
public ActionInProgress(string name, PluginInstances<IBattleUI> plugins) {
|
||||
_name = name;
|
||||
_plugins = plugins;
|
||||
System.Diagnostics.Trace.WriteLine($"Starting new action {name}");
|
||||
_plugins.Call(ui => ui.BattleActionStarted(name));
|
||||
}
|
||||
|
||||
public void Add(int? phase, IInProgress inProgress) {
|
||||
if (!_items.TryGetValue(phase, out var list))
|
||||
_items[phase] = list = new List<IInProgress>();
|
||||
list.Add(inProgress);
|
||||
}
|
||||
|
||||
public void Step() {
|
||||
if (IsComplete) return;
|
||||
|
||||
int phase = _items
|
||||
.Keys
|
||||
.Where(phase => phase != null)
|
||||
.Select(phase => phase.Value)
|
||||
.Min();
|
||||
|
||||
var lists = _items
|
||||
.Where(kv => (kv.Key == null) || (kv.Key == phase))
|
||||
.Select(kv => kv.Value);
|
||||
|
||||
foreach(var list in lists) {
|
||||
for (int i = list.Count - 1; i >= 0; i--) {
|
||||
if (!_frames.TryGetValue(list[i], out int frame)) {
|
||||
frame = 0;
|
||||
_plugins.Call(ui => ui.BattleActionResult(list[i]));
|
||||
}
|
||||
if (list[i].Step(frame) && !list[i].IsIndefinite)
|
||||
list.RemoveAt(i);
|
||||
else
|
||||
_frames[list[i]] = frame + 1;
|
||||
}
|
||||
}
|
||||
|
||||
var toRemove = _items
|
||||
.Where(kv => !kv.Value.Any())
|
||||
.Select(kv => kv.Key)
|
||||
.ToArray();
|
||||
foreach(var key in toRemove)
|
||||
_items.Remove(key);
|
||||
}
|
||||
|
||||
public override string ToString() => _name;
|
||||
public interface IInProgress {
|
||||
string Description { get; }
|
||||
void FrameStep();
|
||||
void Cancel();
|
||||
bool IsComplete { get; }
|
||||
}
|
||||
|
||||
public class BattleTitle : IInProgress {
|
||||
public abstract class TimedInProgress : IInProgress {
|
||||
protected int _frames;
|
||||
protected int _frame;
|
||||
protected string _description;
|
||||
|
||||
public string Description => _description;
|
||||
public bool IsComplete => _frame > _frames;
|
||||
|
||||
protected float Progress => 1f * _frame / _frames;
|
||||
|
||||
protected abstract void DoStep();
|
||||
|
||||
public void FrameStep() {
|
||||
if (_frame <= _frames)
|
||||
DoStep();
|
||||
_frame++;
|
||||
}
|
||||
public virtual void Cancel() {
|
||||
_frame = _frames;
|
||||
}
|
||||
}
|
||||
|
||||
public class BattleTitle : TimedInProgress {
|
||||
private string _title;
|
||||
private int? _frames;
|
||||
private UI.UIBatch _ui;
|
||||
private float _alpha;
|
||||
private bool _announce;
|
||||
|
||||
public bool IsIndefinite => _frames == null;
|
||||
|
||||
public string Description => _announce ? _title : null;
|
||||
|
||||
public BattleTitle(string title, int? frames, UI.UIBatch ui, float alpha, bool announce) {
|
||||
public BattleTitle(string title, int frames, UI.UIBatch ui, float alpha, bool announce) {
|
||||
_title = title;
|
||||
_frames = frames;
|
||||
_ui = ui;
|
||||
_alpha = alpha;
|
||||
_announce = announce;
|
||||
_announce = announce;
|
||||
_description = _announce ? _title : null;
|
||||
}
|
||||
|
||||
public bool Step(int frame) {
|
||||
protected override void DoStep() {
|
||||
_ui.DrawBox(new Rectangle(0, 0, 1280, 55), 0.97f, _alpha);
|
||||
_ui.DrawText("main", _title, 640, 15, 0.98f, Color.White, UI.Alignment.Center);
|
||||
|
||||
return frame >= _frames.GetValueOrDefault();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class BattleResultText : IInProgress {
|
||||
private int _frames;
|
||||
public class BattleResultText : TimedInProgress {
|
||||
private UI.UIBatch _ui;
|
||||
private Color _color;
|
||||
private Func<Vector2> _start;
|
||||
private Vector2 _movement;
|
||||
private string _text;
|
||||
public bool IsIndefinite => false;
|
||||
public string Description { get; private set; }
|
||||
|
||||
public BattleResultText(UI.UIBatch ui, string text, Color color, Func<Vector2> start, Vector2 movement, int frames, string description) {
|
||||
_ui = ui;
|
||||
@ -116,38 +79,34 @@ namespace Braver.Battle {
|
||||
_start = start;
|
||||
_movement = movement;
|
||||
_frames = frames;
|
||||
Description = description;
|
||||
_description = description;
|
||||
}
|
||||
|
||||
public bool Step(int frame) {
|
||||
var pos = _start() + _movement * frame;
|
||||
_ui.DrawText("batm", _text, (int)pos.X, (int)pos.Y, 0.96f, _color, UI.Alignment.Center);
|
||||
return frame++ >= _frames;
|
||||
protected override void DoStep() {
|
||||
var pos = _start() + _movement * _frame;
|
||||
_ui.DrawText("batm", _text, (int)pos.X, (int)pos.Y, 0.96f, _color, UI.Alignment.Center);
|
||||
}
|
||||
}
|
||||
|
||||
public class EnemyDeath : IInProgress {
|
||||
private int _frames;
|
||||
public class EnemyDeath : TimedInProgress {
|
||||
private Model _model;
|
||||
|
||||
public bool IsIndefinite => false;
|
||||
|
||||
public string Description { get; private set; }
|
||||
|
||||
public EnemyDeath(int frames, ICombatant combatant, Model model) {
|
||||
_frames = frames;
|
||||
_model = model;
|
||||
Description = combatant.Name + " died";
|
||||
_description = combatant.Name + " died";
|
||||
}
|
||||
|
||||
public bool Step(int frame) {
|
||||
if (frame < _frames) {
|
||||
_model.DeathFade = 0.33f - (0.33f * frame / _frames);
|
||||
} else {
|
||||
_model.DeathFade = null;
|
||||
_model.Visible = false;
|
||||
}
|
||||
return frame >= _frames;
|
||||
protected override void DoStep() {
|
||||
_model.DeathFade = 0.33f - (0.33f * _frame / _frames);
|
||||
}
|
||||
|
||||
public override void Cancel() {
|
||||
base.Cancel();
|
||||
_model.DeathFade = null;
|
||||
_model.Visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,10 +16,13 @@ using System.Threading.Tasks;
|
||||
namespace Braver.Battle {
|
||||
public class AnimScriptExecutor {
|
||||
|
||||
public enum WaitingForKind {
|
||||
Animation,
|
||||
Action,
|
||||
}
|
||||
|
||||
private ICombatant _source;
|
||||
private ICombatant[] _targets;
|
||||
private RealBattleScreen _screen;
|
||||
private Engine _engine;
|
||||
private AnimationScriptDecoder _script;
|
||||
|
||||
private Func<bool> _shouldContinue = null;
|
||||
@ -27,16 +30,27 @@ namespace Braver.Battle {
|
||||
|
||||
private List<Action> _renderers = new();
|
||||
|
||||
public WaitingForKind? WaitingFor { get; private set; }
|
||||
public bool IsComplete => !_paused && _complete;
|
||||
|
||||
public AnimScriptExecutor(ICombatant source, ICombatant[] targets, RealBattleScreen screen, Engine engine, AnimationScriptDecoder script) {
|
||||
public AnimScriptExecutor(ICombatant source, RealBattleScreen screen, AnimationScriptDecoder script) {
|
||||
_source = source;
|
||||
_targets = targets;
|
||||
_screen = screen;
|
||||
_engine = engine;
|
||||
_script = script;
|
||||
}
|
||||
|
||||
public void Resume() {
|
||||
switch (WaitingFor) {
|
||||
case WaitingForKind.Action:
|
||||
_paused = false;
|
||||
_shouldContinue = null;
|
||||
WaitingFor = null;
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public void Step() {
|
||||
if (_paused) {
|
||||
if (_shouldContinue()) {
|
||||
@ -49,11 +63,12 @@ namespace Braver.Battle {
|
||||
if (op == null)
|
||||
_complete = true;
|
||||
else {
|
||||
var model = _screen.Models[_source];
|
||||
var model = _screen.Renderer.Models[_source];
|
||||
if ((byte)op.Value.Op < 0x8E) {
|
||||
model.PlayAnimation((byte)op.Value.Op, false, 1f, onlyIfDifferent: false);
|
||||
_paused = true;
|
||||
_shouldContinue = () => model.AnimationState.CompletionCount > 0;
|
||||
WaitingFor = WaitingForKind.Animation;
|
||||
} else {
|
||||
|
||||
switch(op.Value.Op) {
|
||||
@ -66,7 +81,7 @@ namespace Braver.Battle {
|
||||
Action effRender = null;
|
||||
var pos = model.Translation + model.Translation2;
|
||||
effRender = () => {
|
||||
if (effect.Render(pos, _screen.View3D, frame++))
|
||||
if (effect.Render(pos, _screen.Renderer.View3D, frame++))
|
||||
_renderers.Remove(effRender);
|
||||
};
|
||||
_renderers.Add(effRender);
|
||||
@ -82,10 +97,9 @@ namespace Braver.Battle {
|
||||
break;
|
||||
|
||||
case AnimScriptOp.WaitForEffectLoad:
|
||||
//TODO!!!
|
||||
_paused = true;
|
||||
int timeout = 120;
|
||||
_shouldContinue = () => --timeout == 0;
|
||||
_shouldContinue = () => false;
|
||||
WaitingFor = WaitingForKind.Action;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -8,6 +8,7 @@ using Braver.UI;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using NAudio.CoreAudioApi.Interfaces;
|
||||
using NAudio.MediaFoundation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -23,6 +24,7 @@ namespace Braver.Battle {
|
||||
private RealBattleScreen _screen;
|
||||
private AnimScriptExecutor _exec;
|
||||
private SpriteRenderer _sprites;
|
||||
private BattleEffectManager _effect;
|
||||
private FGame _game;
|
||||
private GraphicsDevice _graphics;
|
||||
|
||||
@ -55,6 +57,11 @@ namespace Braver.Battle {
|
||||
}
|
||||
|
||||
_sprites.FrameStep();
|
||||
if (_effect != null) {
|
||||
_effect.Step();
|
||||
if (_effect.IsComplete)
|
||||
_effect = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void ProcessInput(InputState input) {
|
||||
@ -75,25 +82,40 @@ namespace Braver.Battle {
|
||||
if (input.IsJustDown(InputKey.Cancel)) {
|
||||
_exec = new AnimScriptExecutor(
|
||||
_engine.ActiveCombatants.ElementAt(_cMenu),
|
||||
_engine.ActiveCombatants.OfType<EnemyCombatant>().ToArray(),
|
||||
_screen, _engine,
|
||||
_screen,
|
||||
new Ficedula.FF7.Battle.AnimationScriptDecoder(new byte[] { (byte)_anim, 0 })
|
||||
);
|
||||
}
|
||||
if (input.IsJustDown(InputKey.OK)) {
|
||||
var source = _engine.ActiveCombatants.ElementAt(_cMenu);
|
||||
var model = _screen.Models[source];
|
||||
var model = _screen.Renderer.Models[source];
|
||||
_exec = new AnimScriptExecutor(
|
||||
source,
|
||||
_engine.ActiveCombatants.OfType<EnemyCombatant>().ToArray(),
|
||||
_screen, _engine,
|
||||
_screen,
|
||||
new Ficedula.FF7.Battle.AnimationScriptDecoder(model.AnimationScript.Scripts[_script])
|
||||
);
|
||||
}
|
||||
|
||||
if (input.IsJustDown(InputKey.Start)) {
|
||||
/*
|
||||
var sprite = new LoadedSprite(_game, _graphics, "fi_a01.s", new[] { "fire00.tex", "fire01.tex" });
|
||||
_sprites.Add(sprite, () => _screen.GetModelScreenPos(_engine.ActiveCombatants.ElementAt(_cMenu)));
|
||||
*/
|
||||
var source = _engine.ActiveCombatants.ElementAt(_cMenu);
|
||||
var action = new QueuedAction(
|
||||
source,
|
||||
(source as CharacterCombatant).Actions.Select(a => a.Ability).First(a => a.HasValue).Value,
|
||||
_engine.ActiveCombatants.Where(c => !c.IsPlayer).ToArray(),
|
||||
ActionPriority.Normal,
|
||||
"Fire1"
|
||||
);
|
||||
_engine.QueueAction(action);
|
||||
if (_engine.ExecuteNextAction(out var nextAction, out var results)) {
|
||||
_effect = new BattleEffectManager(
|
||||
_game, _screen,
|
||||
nextAction, results, "magic/fire1"
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@ -102,6 +124,7 @@ namespace Braver.Battle {
|
||||
_ui.Render();
|
||||
_exec?.Render();
|
||||
_sprites.Render();
|
||||
_effect?.Render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +0,0 @@
|
||||
// This program and the accompanying materials are made available under the terms of the
|
||||
// Eclipse Public License v2.0 which accompanies this distribution, and is available at
|
||||
// https://www.eclipse.org/legal/epl-v20.html
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Braver.Battle {
|
||||
|
||||
public enum EffectCommand {
|
||||
|
||||
}
|
||||
|
||||
internal class BattleEffect {
|
||||
}
|
||||
}
|
||||
/*
|
||||
* PRELOAD SPRITE <alias> <spritefile> <tex> <tex> <tex>...
|
||||
* PRELOAD SUMMON <alias> <summoncode>
|
||||
* PRELOAD SFX <numberOrName>
|
||||
*
|
||||
* SPRITE <combatantID> <xyzOffset>
|
||||
* SFX <numberOrName>
|
||||
* CAMERA ...
|
||||
*
|
||||
*/
|
344
Braver/Battle/BattleEffectManager.cs
Normal file
344
Braver/Battle/BattleEffectManager.cs
Normal file
@ -0,0 +1,344 @@
|
||||
// This program and the accompanying materials are made available under the terms of the
|
||||
// Eclipse Public License v2.0 which accompanies this distribution, and is available at
|
||||
// https://www.eclipse.org/legal/epl-v20.html
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0
|
||||
|
||||
using Braver.UI;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Braver.Battle {
|
||||
|
||||
public enum EffectCommand {
|
||||
PreloadSprite,
|
||||
PreloadSummon,
|
||||
PreloadSfx,
|
||||
|
||||
Sprite,
|
||||
Sfx,
|
||||
Camera,
|
||||
Anim,
|
||||
Resume,
|
||||
Wait,
|
||||
Pause,
|
||||
Result,
|
||||
|
||||
DisplayText,
|
||||
|
||||
DeathFade,
|
||||
ApplyResults,
|
||||
}
|
||||
|
||||
public interface IWaitableEffect {
|
||||
bool IsComplete { get; }
|
||||
}
|
||||
|
||||
public struct ImmediatelyCompleteEffect : IWaitableEffect {
|
||||
public bool IsComplete => true;
|
||||
}
|
||||
public struct CallbackWaitEffect : IWaitableEffect {
|
||||
public Func<bool> CheckComplete { get; set; }
|
||||
public bool IsComplete => CheckComplete();
|
||||
}
|
||||
|
||||
public class EffectTemplate : IBraverTemplateModel {
|
||||
private AbilityResult[] _results;
|
||||
private FGame _game;
|
||||
private QueuedAction _action;
|
||||
|
||||
public IEnumerable<AbilityResult> Results => _results;
|
||||
public QueuedAction Action => _action;
|
||||
|
||||
FGame IBraverTemplateModel.Game => _game;
|
||||
string IBraverTemplateModel.SourceCategory => "battle";
|
||||
|
||||
string IBraverTemplateModel.SourceExtension => ".effect";
|
||||
|
||||
public EffectTemplate(AbilityResult[] results, FGame game, QueuedAction action) {
|
||||
_results = results;
|
||||
_game = game;
|
||||
_action = action;
|
||||
}
|
||||
|
||||
public string Expand(byte[] text) {
|
||||
var chars = _game.SaveData.Characters.Select(c => c?.Name).ToArray();
|
||||
var party = _game.SaveData.Party.Select(c => c?.Name).ToArray();
|
||||
return Ficedula.FF7.Text.Expand(Ficedula.FF7.Text.Convert(text, 0), chars, party);
|
||||
}
|
||||
}
|
||||
|
||||
public class BattleEffectManager {
|
||||
|
||||
private static Dictionary<EffectCommand, Func<BattleEffectManager, IEnumerable<string>, IWaitableEffect>> _executor = new();
|
||||
private Dictionary<string, List<IWaitableEffect>> _waitable = new(StringComparer.InvariantCultureIgnoreCase);
|
||||
private List<IInProgress> _ongoing = new();
|
||||
|
||||
private static IWaitableEffect _immediatelyComplete = new ImmediatelyCompleteEffect();
|
||||
|
||||
|
||||
static BattleEffectManager() {
|
||||
_executor[EffectCommand.Sfx] = (effect, parms) => effect.CmdSfx(parms);
|
||||
_executor[EffectCommand.PreloadSfx] = (effect, parms) => effect.CmdPreloadSfx(parms);
|
||||
_executor[EffectCommand.Wait] = (effect, parms) => effect.CmdWait(parms);
|
||||
_executor[EffectCommand.Pause] = (effect, parms) => effect.CmdPause(parms);
|
||||
_executor[EffectCommand.PreloadSprite] = (effect, parms) => effect.CmdPreloadSprite(parms);
|
||||
_executor[EffectCommand.Sprite] = (effect, parms) => effect.CmdSprite(parms);
|
||||
_executor[EffectCommand.Anim] = (effect, parms) => effect.CmdAnim(parms);
|
||||
_executor[EffectCommand.Resume] = (effect, parms) => effect.CmdResume(parms);
|
||||
_executor[EffectCommand.Result] = (effect, parms) => effect.CmdResult(parms);
|
||||
_executor[EffectCommand.DeathFade] = (effect, parms) => effect.CmdDeathFade(parms);
|
||||
_executor[EffectCommand.ApplyResults] = (effect, parms) => effect.CmdApplyResults(parms);
|
||||
_executor[EffectCommand.DisplayText] = (effect, parms) => effect.CmdDisplayText(parms);
|
||||
}
|
||||
|
||||
private IWaitableEffect WaitableFromInProgress(IEnumerable<IInProgress> effects) {
|
||||
_ongoing.AddRange(effects);
|
||||
return new CallbackWaitEffect {
|
||||
CheckComplete = () => effects.All(p => p.IsComplete)
|
||||
};
|
||||
}
|
||||
private IWaitableEffect WaitableFromInProgress(params IInProgress[] effects) {
|
||||
return WaitableFromInProgress(effects.AsEnumerable());
|
||||
}
|
||||
|
||||
private IWaitableEffect CmdResult(IEnumerable<string> parms) {
|
||||
var result = _results.Single(r => r.Target.ID.ToString() == parms.ElementAt(0));
|
||||
|
||||
IInProgress effect = null;
|
||||
|
||||
if (result.Hit) {
|
||||
if (result.HPDamage != 0) {
|
||||
effect = new BattleResultText(
|
||||
_screen.MenuUI, Math.Abs(result.HPDamage).ToString(), result.HPDamage < 0 ? Color.White : Color.Green,
|
||||
() => _screen.GetModelScreenPos(result.Target).XY(), new Vector2(0, -1),
|
||||
60,
|
||||
$"{result.Target.Name} {Math.Abs(result.HPDamage)} {(result.HPDamage >= 0 ? "healing" : "damage")}"
|
||||
);
|
||||
}
|
||||
if (result.MPDamage != 0) {
|
||||
effect = new BattleResultText(
|
||||
_screen.MenuUI, $"{Math.Abs(result.MPDamage)} {Font.BATTLE_MP}", result.MPDamage < 0 ? Color.White : Color.Green,
|
||||
() => _screen.GetModelScreenPos(result.Target).XY(), new Vector2(0, -1),
|
||||
60,
|
||||
$"{result.Target.Name} {Math.Abs(result.HPDamage)} MP {(result.HPDamage >= 0 ? "healing" : "damage")}"
|
||||
);
|
||||
}
|
||||
//TODO anything else?!?!
|
||||
} else {
|
||||
effect = new BattleResultText(
|
||||
_screen.MenuUI, Font.BATTLE_MISS.ToString(), Color.White,
|
||||
() => _screen.GetModelScreenPos(result.Target).XY(), new Vector2(0, -1),
|
||||
60,
|
||||
$"{result.Target.Name} missed"
|
||||
);
|
||||
}
|
||||
|
||||
if (effect != null)
|
||||
return WaitableFromInProgress(effect);
|
||||
else
|
||||
return _immediatelyComplete;
|
||||
}
|
||||
|
||||
private IWaitableEffect CmdDeathFade(IEnumerable<string> parms) {
|
||||
var who = _models[parms.ElementAt(0)];
|
||||
return WaitableFromInProgress(new EnemyDeath(60, who, _screen.Renderer.Models[who]));
|
||||
}
|
||||
|
||||
private IWaitableEffect CmdDisplayText(IEnumerable<string> parms) {
|
||||
string text = string.Join(" ", parms);
|
||||
return WaitableFromInProgress(new BattleTitle(text, 60, _screen.MenuUI, 1f, true));
|
||||
}
|
||||
|
||||
private IWaitableEffect CmdApplyResults(IEnumerable<string> parms) {
|
||||
foreach (var result in _results) {
|
||||
result.Apply(_action);
|
||||
_screen.UpdateVisualState(result.Target);
|
||||
}
|
||||
return _immediatelyComplete;
|
||||
}
|
||||
|
||||
private IWaitableEffect CmdResume(IEnumerable<string> parms) {
|
||||
var who = _models[parms.ElementAt(0)];
|
||||
var exec = _anims[who];
|
||||
switch (exec.WaitingFor) {
|
||||
case AnimScriptExecutor.WaitingForKind.Action:
|
||||
exec.Resume();
|
||||
return _immediatelyComplete;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
private IWaitableEffect CmdAnim(IEnumerable<string> parms) {
|
||||
var who = _models[parms.ElementAt(0)];
|
||||
var model = _screen.Renderer.Models[who];
|
||||
int anim = int.Parse(parms.ElementAt(1));
|
||||
|
||||
var exec = _anims[who] = new AnimScriptExecutor(
|
||||
who, _screen,
|
||||
new Ficedula.FF7.Battle.AnimationScriptDecoder(model.AnimationScript.Scripts[anim])
|
||||
);
|
||||
|
||||
return new CallbackWaitEffect {
|
||||
CheckComplete = () => (_anims[who] != exec) || exec.IsComplete || (exec.WaitingFor == AnimScriptExecutor.WaitingForKind.Action)
|
||||
};
|
||||
}
|
||||
|
||||
private IWaitableEffect CmdSprite(IEnumerable<string> parms) {
|
||||
var sprite = _spritesByAlias[parms.First()];
|
||||
var target = _models[parms.ElementAt(1)];
|
||||
var model = _screen.Renderer.Models[target];
|
||||
var offset = GraphicsUtil.Parse3(parms.ElementAtOrDefault(2) ?? "0/0/0");
|
||||
var instance = _screen.Renderer.Sprites.Add(sprite, () => _screen.GetModelScreenPos(target, offset));
|
||||
return new CallbackWaitEffect {
|
||||
CheckComplete = () => !instance.IsActive
|
||||
};
|
||||
}
|
||||
|
||||
private IWaitableEffect CmdPreloadSprite(IEnumerable<string> parms) {
|
||||
//TODO actually background load!
|
||||
var sprite = _screen.Sprites.Get(parms.ElementAt(1), parms.Skip(2));
|
||||
_spritesByAlias[parms.ElementAt(0)] = sprite;
|
||||
return _immediatelyComplete;
|
||||
}
|
||||
|
||||
private IWaitableEffect CmdPause(IEnumerable<string> parms) {
|
||||
int completeFrames = _game.GameTimeFrames + int.Parse(parms.First());
|
||||
return new CallbackWaitEffect {
|
||||
CheckComplete = () => _game.GameTimeFrames >= completeFrames
|
||||
};
|
||||
}
|
||||
|
||||
private IWaitableEffect CmdSfx(IEnumerable<string> parms) {
|
||||
int id = int.Parse(parms.First());
|
||||
//TODO - 3d position
|
||||
_game.Audio.PlaySfx(id, 1f, 0f);
|
||||
return _immediatelyComplete;
|
||||
//TODO - wait for sfx completion?
|
||||
}
|
||||
private IWaitableEffect CmdPreloadSfx(IEnumerable<string> parms) {
|
||||
int id = int.Parse(parms.First());
|
||||
_game.Audio.Precache(id, false);
|
||||
return _immediatelyComplete;
|
||||
//TODO - wait for completion?
|
||||
}
|
||||
|
||||
private IWaitableEffect CmdWait(IEnumerable<string> parms) {
|
||||
if (parms.First().Equals("All", StringComparison.InvariantCultureIgnoreCase)) {
|
||||
var allWaiters = _waitable.Values.SelectMany(L => L);
|
||||
if (allWaiters.Any(w => !w.IsComplete))
|
||||
return null;
|
||||
_waitable.Clear();
|
||||
return _immediatelyComplete;
|
||||
} else {
|
||||
var waiters = parms
|
||||
.Select(id => _waitable.GetValueOrDefault(id))
|
||||
.Where(L => L != null)
|
||||
.SelectMany(L => L);
|
||||
if (waiters.Any(w => !w.IsComplete))
|
||||
return null;
|
||||
return _immediatelyComplete;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Dictionary<string, ICombatant> _models = new(StringComparer.InvariantCultureIgnoreCase);
|
||||
private Dictionary<ICombatant, AnimScriptExecutor> _anims = new();
|
||||
private List<string> _effect;
|
||||
private int _ip;
|
||||
private FGame _game;
|
||||
private Dictionary<string, LoadedSprite> _spritesByAlias = new(StringComparer.InvariantCultureIgnoreCase);
|
||||
private RealBattleScreen _screen;
|
||||
private QueuedAction _action;
|
||||
private AbilityResult[] _results;
|
||||
|
||||
public bool IsComplete => _ip >= _effect.Count;
|
||||
|
||||
public BattleEffectManager(FGame g, RealBattleScreen screen,
|
||||
QueuedAction action, AbilityResult[] results, string effect) {
|
||||
_game = g;
|
||||
_screen = screen;
|
||||
_action = action;
|
||||
_results = results;
|
||||
|
||||
var cache = g.Singleton(() => new RazorLayoutCache(g));
|
||||
var model = new EffectTemplate(results, g, action);
|
||||
_effect = cache.Compile("battle", effect + ".effect", false)
|
||||
.Run(template => template.Model = model)
|
||||
.Split('\r', '\n')
|
||||
.ToList();
|
||||
|
||||
_models["source"] = action.Source;
|
||||
foreach (var result in results)
|
||||
_models[result.Target.ID.ToString()] = result.Target;
|
||||
}
|
||||
|
||||
public void Render() {
|
||||
foreach (var anim in _anims.Values.Where(a => a != null))
|
||||
anim.Render();
|
||||
|
||||
}
|
||||
|
||||
public void Step() {
|
||||
foreach (var anim in _anims.Values.Where(a => a != null))
|
||||
anim.Step();
|
||||
foreach (var effect in _ongoing)
|
||||
effect.FrameStep();
|
||||
|
||||
while(_ip < _effect.Count) {
|
||||
string cmd = _effect[_ip].Trim();
|
||||
if (string.IsNullOrWhiteSpace(cmd) || cmd.StartsWith('#')) {
|
||||
_ip++;
|
||||
continue;
|
||||
}
|
||||
var parts = cmd.Split(null)
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s))
|
||||
.ToArray();
|
||||
int index = 0;
|
||||
string waitID;
|
||||
if (parts[index].StartsWith('!'))
|
||||
waitID = parts[index++];
|
||||
else
|
||||
waitID = null;
|
||||
|
||||
var effectCmd = Enum.Parse<EffectCommand>(parts[index++]);
|
||||
var result = _executor[effectCmd](this, parts.Skip(index));
|
||||
if (result != null) {
|
||||
_ip++;
|
||||
if (waitID != null) {
|
||||
if (!_waitable.TryGetValue(waitID, out var list))
|
||||
_waitable[waitID] = list = new List<IWaitableEffect>();
|
||||
list.Add(result);
|
||||
}
|
||||
} else
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsComplete) {
|
||||
foreach (var effect in _ongoing.Where(p => !p.IsComplete))
|
||||
effect.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Prefix any command with !waitID to make it waitable later
|
||||
*
|
||||
* PRELOADSPRITE <alias> <spritefile> <tex> <tex> <tex>...
|
||||
* PRELOADSUMMON <alias> <summoncode>
|
||||
* PRELOADSFX <numberOrName>
|
||||
*
|
||||
* SPRITE <alias> <combatantID> <xyzOffset>
|
||||
* SFX <numberOrName> <positionAtCombatantID>
|
||||
* CAMERA ...
|
||||
* ANIM <combatantID> <animNumber>
|
||||
* RESUME <combatantID>
|
||||
* WAIT <id> <id> <id>...
|
||||
* WAIT ALL
|
||||
* PAUSE <duration>
|
||||
* RESULT <combatantID>
|
||||
*
|
||||
*/
|
@ -33,12 +33,14 @@ namespace Braver.Battle {
|
||||
public FGame Game { get; private set; }
|
||||
public GraphicsDevice Graphics { get; private set; }
|
||||
public Dictionary<T, Model> Models { get; } = new();
|
||||
public SpriteRenderer Sprites { get; }
|
||||
public PerspView3D View3D => _view;
|
||||
|
||||
public BattleRenderer(FGame game, GraphicsDevice graphics, Screen uiScreen) {
|
||||
Game = game;
|
||||
Graphics = graphics;
|
||||
_ui = uiScreen;
|
||||
Sprites = new SpriteRenderer(graphics);
|
||||
}
|
||||
|
||||
public void SetCamera(BattleCamera cam) {
|
||||
@ -125,6 +127,7 @@ namespace Braver.Battle {
|
||||
foreach (var model in Models.Values) {
|
||||
model.FrameStep();
|
||||
}
|
||||
Sprites.FrameStep();
|
||||
_ui.Step(elapsed);
|
||||
}
|
||||
|
||||
@ -151,6 +154,8 @@ namespace Braver.Battle {
|
||||
if (model.Visible)
|
||||
model.Render(_view);
|
||||
|
||||
Sprites.Render();
|
||||
|
||||
_ui.Render();
|
||||
}
|
||||
|
||||
|
@ -119,15 +119,11 @@ namespace Braver.Battle {
|
||||
|
||||
private bool _toggleMultiSingleTarget;
|
||||
|
||||
private ActionInProgress _actionInProgress;
|
||||
private Action _actionComplete;
|
||||
private BattleEffectManager _effect;
|
||||
|
||||
private SpriteRenderer _sprites;
|
||||
private Engine _engine;
|
||||
private BattleDebug _debug;
|
||||
private PluginInstances<IBattleUI> _plugins;
|
||||
|
||||
private BattleRenderer<ICombatant> _renderer;
|
||||
private UIHandler _uiHandler;
|
||||
|
||||
private bool _debugCamera = false;
|
||||
@ -143,8 +139,9 @@ namespace Braver.Battle {
|
||||
_plugins.Call(ui => ui.BattleTargetHighlighted(current));
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<ICombatant, Model> Models => _renderer.Models;
|
||||
public PerspView3D View3D => _renderer.View3D;
|
||||
public BattleRenderer<ICombatant> Renderer { get; private set; }
|
||||
public SpriteManager Sprites { get; private set; }
|
||||
public UI.UIBatch MenuUI => _menuUI;
|
||||
|
||||
public override string Description {
|
||||
get {
|
||||
@ -167,7 +164,7 @@ namespace Braver.Battle {
|
||||
if (position.Z < 0)
|
||||
model.Rotation = new Vector3(0, 180, 0);
|
||||
//Defaults to animation 0 so no PlayAnimation required
|
||||
_renderer.Models.Add(combatant, model);
|
||||
Renderer.Models.Add(combatant, model);
|
||||
Game.Net.Send(new AddBattleModelMessage {
|
||||
Code = code,
|
||||
Position = position,
|
||||
@ -176,7 +173,7 @@ namespace Braver.Battle {
|
||||
}
|
||||
|
||||
public void UpdateVisualState(ICombatant combatant) {
|
||||
var model = _renderer.Models[combatant];
|
||||
var model = Renderer.Models[combatant];
|
||||
switch(combatant) {
|
||||
case CharacterCombatant chr:
|
||||
if (chr.HP <= 0)
|
||||
@ -261,13 +258,11 @@ namespace Braver.Battle {
|
||||
var ui = new UI.Layout.LayoutScreen("battle", _uiHandler, isEmbedded: true);
|
||||
ui.Init(Game, Graphics);
|
||||
|
||||
_renderer = new BattleRenderer<ICombatant>(g, graphics, ui);
|
||||
_renderer.LoadBackground(_scene.LocationID);
|
||||
Renderer = new BattleRenderer<ICombatant>(g, graphics, ui);
|
||||
Renderer.LoadBackground(_scene.LocationID);
|
||||
var cam = _scene.Cameras[0];
|
||||
_renderer.SetCamera(cam);
|
||||
|
||||
_sprites = new SpriteRenderer(graphics);
|
||||
|
||||
Renderer.SetCamera(cam);
|
||||
|
||||
foreach (var enemy in _scene.Enemies) {
|
||||
AddModel(
|
||||
SceneDecoder.ModelIDToFileName(enemy.Enemy.ID),
|
||||
@ -286,6 +281,7 @@ namespace Braver.Battle {
|
||||
|
||||
_menuUI = new UI.UIBatch(Graphics, Game);
|
||||
_plugins = GetPlugins<IBattleUI>(_formationID.ToString());
|
||||
Sprites = new SpriteManager(g, graphics);
|
||||
|
||||
g.Net.Send(new Net.ScreenReadyMessage());
|
||||
g.Audio.PlayMusic("bat", true); //TODO!
|
||||
@ -391,10 +387,12 @@ namespace Braver.Battle {
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 GetModelScreenPos(ICombatant combatant) {
|
||||
var model = _renderer.Models[combatant];
|
||||
public Vector3 GetModelScreenPos(ICombatant combatant, Vector3? offset = null) {
|
||||
var model = Renderer.Models[combatant];
|
||||
var middle = (model.MaxBounds + model.MinBounds) * 0.5f;
|
||||
var screenPos = _renderer.View3D.ProjectTo2D(model.Translation + middle);
|
||||
if (offset != null)
|
||||
middle += offset.Value;
|
||||
var screenPos = Renderer.View3D.ProjectTo2D(model.Translation + middle);
|
||||
return screenPos;
|
||||
}
|
||||
|
||||
@ -437,8 +435,6 @@ namespace Braver.Battle {
|
||||
|
||||
_uiHandler.Update(_activeMenu?.Combatant);
|
||||
|
||||
_menuUI.Reset();
|
||||
|
||||
_activeMenu?.Step();
|
||||
|
||||
var action = _activeMenu?.SelectedAction;
|
||||
@ -465,13 +461,11 @@ namespace Braver.Battle {
|
||||
}
|
||||
}
|
||||
|
||||
if (_actionInProgress != null) {
|
||||
_actionInProgress.Step();
|
||||
if (_actionInProgress.IsComplete) {
|
||||
System.Diagnostics.Trace.WriteLine($"Action {_actionInProgress} now complete");
|
||||
_actionInProgress = null;
|
||||
_actionComplete?.Invoke();
|
||||
_actionComplete = null;
|
||||
if (_effect != null) {
|
||||
_effect.Step();
|
||||
if (_effect.IsComplete) {
|
||||
System.Diagnostics.Trace.WriteLine($"Action {_effect} now complete");
|
||||
_effect = null;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
@ -481,18 +475,18 @@ namespace Braver.Battle {
|
||||
* -Or, Check for battle end, and if not, execute any other queued action
|
||||
*/
|
||||
|
||||
var pendingDeadEnemies = _renderer.Models
|
||||
var pendingDeadEnemies = Renderer.Models
|
||||
.Where(kv => !kv.Key.IsPlayer && !kv.Key.IsAlive())
|
||||
.Where(kv => kv.Value.Visible);
|
||||
|
||||
if (_engine.AnyQueuedCounters)
|
||||
DoExecuteNextQueuedAction();
|
||||
else if (pendingDeadEnemies.Any()) {
|
||||
_actionInProgress = new ActionInProgress("FadeDeadEnemies", _plugins);
|
||||
foreach (var enemy in pendingDeadEnemies) {
|
||||
_actionInProgress.Add(1, new EnemyDeath(60, enemy.Key, enemy.Value));
|
||||
Game.Audio.PlaySfx(Sfx.EnemyDeath, 1f, 0f); //TODO 3d position?!
|
||||
}
|
||||
_effect = new BattleEffectManager(
|
||||
Game, this, null,
|
||||
pendingDeadEnemies.Select(e => new AbilityResult { Target = e.Key }).ToArray(),
|
||||
"FadeDeadEnemies"
|
||||
);
|
||||
} else if (CheckForBattleEnd()) {
|
||||
//
|
||||
} else
|
||||
@ -501,78 +495,24 @@ namespace Braver.Battle {
|
||||
}
|
||||
|
||||
protected override void DoStep(GameTime elapsed) {
|
||||
_menuUI.Reset();
|
||||
|
||||
if (_debug != null) {
|
||||
_debug.Step();
|
||||
} else {
|
||||
EngineTick(elapsed);
|
||||
}
|
||||
_renderer.Step(elapsed);
|
||||
Renderer.Step(elapsed);
|
||||
}
|
||||
|
||||
private void DoExecuteNextQueuedAction() {
|
||||
if (_engine.ExecuteNextAction(out var nextAction, out var results)) {
|
||||
_actionInProgress = new ActionInProgress($"Action {nextAction.Name} by {nextAction.Source.Name}", _plugins);
|
||||
_actionComplete += () => {
|
||||
foreach (var result in results) {
|
||||
result.Apply(nextAction);
|
||||
UpdateVisualState(result.Target);
|
||||
}
|
||||
};
|
||||
|
||||
int phase = 0;
|
||||
|
||||
if (nextAction.QueuedText.Any()) {
|
||||
var chars = Game.SaveData.Characters.Select(c => c?.Name).ToArray();
|
||||
var party = Game.SaveData.Party.Select(c => c?.Name).ToArray();
|
||||
|
||||
foreach (var text in nextAction.QueuedText) {
|
||||
_actionInProgress.Add(phase++, new BattleTitle(
|
||||
Ficedula.FF7.Text.Expand(Ficedula.FF7.Text.Convert(text, 0), chars, party),
|
||||
60, _menuUI, 1f, true
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
//TODO this isn't always needed
|
||||
_actionInProgress.Add(phase, new BattleTitle(
|
||||
nextAction.Name ?? "(Unknown)", 60, _menuUI, 0.75f, false
|
||||
));
|
||||
|
||||
//TODO all the animations!!
|
||||
foreach (var result in results) {
|
||||
if (result.Hit) {
|
||||
if (result.HPDamage != 0) {
|
||||
_actionInProgress.Add(phase, new BattleResultText(
|
||||
_menuUI, Math.Abs(result.HPDamage).ToString(), result.HPDamage < 0 ? Color.White : Color.Green,
|
||||
() => GetModelScreenPos(result.Target).XY(), new Vector2(0, -1),
|
||||
60,
|
||||
$"{result.Target.Name} {Math.Abs(result.HPDamage)} {(result.HPDamage >= 0 ? "healing" : "damage")}"
|
||||
));
|
||||
}
|
||||
if (result.MPDamage != 0) {
|
||||
_actionInProgress.Add(phase, new BattleResultText(
|
||||
_menuUI, $"{Math.Abs(result.MPDamage)} {Font.BATTLE_MP}", result.MPDamage < 0 ? Color.White : Color.Green,
|
||||
() => GetModelScreenPos(result.Target).XY(), new Vector2(0, -1),
|
||||
60,
|
||||
$"{result.Target.Name} {Math.Abs(result.HPDamage)} MP {(result.HPDamage >= 0 ? "healing" : "damage")}"
|
||||
));
|
||||
}
|
||||
//TODO anything else?!?!
|
||||
} else {
|
||||
_actionInProgress.Add(phase, new BattleResultText(
|
||||
_menuUI, Font.BATTLE_MISS.ToString(), Color.White,
|
||||
() => GetModelScreenPos(result.Target).XY(), new Vector2(0, -1),
|
||||
60,
|
||||
$"{result.Target.Name} missed"
|
||||
));
|
||||
}
|
||||
}
|
||||
_effect = new BattleEffectManager(Game, this, nextAction, results, nextAction.Name /*VERY TODO*/);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DoRender() {
|
||||
_renderer.Render();
|
||||
Renderer.Render();
|
||||
_menuUI.Render();
|
||||
_debug?.Render();
|
||||
}
|
||||
@ -600,13 +540,13 @@ namespace Braver.Battle {
|
||||
|
||||
if (_debugCamera) {
|
||||
if (input.IsDown(InputKey.Up))
|
||||
_renderer.View3D.CameraPosition += new Vector3(0, 0, -100);
|
||||
Renderer.View3D.CameraPosition += new Vector3(0, 0, -100);
|
||||
if (input.IsDown(InputKey.Down))
|
||||
_renderer.View3D.CameraPosition += new Vector3(0, 0, 100);
|
||||
Renderer.View3D.CameraPosition += new Vector3(0, 0, 100);
|
||||
if (input.IsDown(InputKey.Left))
|
||||
_renderer.View3D.CameraPosition += new Vector3(100, 0, 0);
|
||||
Renderer.View3D.CameraPosition += new Vector3(100, 0, 0);
|
||||
if (input.IsDown(InputKey.Right))
|
||||
_renderer.View3D.CameraPosition += new Vector3(-100, 0, 0);
|
||||
Renderer.View3D.CameraPosition += new Vector3(-100, 0, 0);
|
||||
} else if (input.IsJustDown(InputKey.Menu)) {
|
||||
NextMenu();
|
||||
} else if (Targets != null) {
|
||||
|
@ -98,6 +98,25 @@ namespace Braver.Battle {
|
||||
|
||||
}
|
||||
|
||||
public class SpriteManager {
|
||||
private Dictionary<string, LoadedSprite> _sprites = new(StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
private FGame _game;
|
||||
private GraphicsDevice _graphics;
|
||||
|
||||
public SpriteManager(FGame game, GraphicsDevice graphics) {
|
||||
_game = game;
|
||||
_graphics = graphics;
|
||||
}
|
||||
|
||||
public LoadedSprite Get(string spriteFile, IEnumerable<string> textures) {
|
||||
string key = spriteFile + "," + string.Join(",", textures);
|
||||
if (!_sprites.TryGetValue(key, out var sprite))
|
||||
_sprites[key] = sprite = new LoadedSprite(_game, _graphics, spriteFile, textures);
|
||||
return sprite;
|
||||
}
|
||||
}
|
||||
|
||||
public class SpriteRenderer {
|
||||
|
||||
private List<(LoadedSprite.Instance sprite, Func<Vector3> pos)> _sprites = new();
|
||||
@ -109,8 +128,10 @@ namespace Braver.Battle {
|
||||
_graphics = graphics;
|
||||
}
|
||||
|
||||
public void Add(LoadedSprite sprite, Func<Vector3> getPos) {
|
||||
_sprites.Add((new LoadedSprite.Instance(sprite), getPos));
|
||||
public LoadedSprite.Instance Add(LoadedSprite sprite, Func<Vector3> getPos) {
|
||||
var instance = new LoadedSprite.Instance(sprite);
|
||||
_sprites.Add((instance, getPos));
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void FrameStep() {
|
||||
|
81
Braver/BraverRazor.cs
Normal file
81
Braver/BraverRazor.cs
Normal file
@ -0,0 +1,81 @@
|
||||
// This program and the accompanying materials are made available under the terms of the
|
||||
// Eclipse Public License v2.0 which accompanies this distribution, and is available at
|
||||
// https://www.eclipse.org/legal/epl-v20.html
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0
|
||||
|
||||
using Braver.UI.Layout;
|
||||
using Ficedula;
|
||||
using RazorEngineCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Braver {
|
||||
|
||||
public interface IBraverTemplateModel {
|
||||
public FGame Game { get; }
|
||||
public string SourceCategory { get; }
|
||||
public string SourceExtension { get; }
|
||||
}
|
||||
public class BraverTemplate : RazorEngineTemplateBase {
|
||||
public FGame GameModel {
|
||||
get {
|
||||
switch (Model) {
|
||||
case FGame game:
|
||||
return game;
|
||||
case IBraverTemplateModel model:
|
||||
return model.Game;
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string Bool(bool b) {
|
||||
return b.ToString().ToLower();
|
||||
}
|
||||
|
||||
public string Include(string templateName, object model) {
|
||||
var btemplate = Model as IBraverTemplateModel;
|
||||
var cache = GameModel.Singleton(() => new RazorLayoutCache(GameModel));
|
||||
return cache.ApplyPartial(btemplate.SourceCategory, templateName + "." + btemplate.SourceExtension, false, model);
|
||||
}
|
||||
}
|
||||
|
||||
internal class RazorLayoutCache {
|
||||
|
||||
private Dictionary<string, IRazorEngineCompiledTemplate<BraverTemplate>> _templates = new Dictionary<string, IRazorEngineCompiledTemplate<BraverTemplate>>(StringComparer.InvariantCultureIgnoreCase);
|
||||
private IRazorEngine _razorEngine = new RazorEngine();
|
||||
private FGame _game;
|
||||
|
||||
public FGame Game => _game;
|
||||
|
||||
public RazorLayoutCache(FGame game) {
|
||||
_game = game;
|
||||
}
|
||||
|
||||
public IRazorEngineCompiledTemplate<BraverTemplate> Compile(string category, string razorFile, bool forceReload) {
|
||||
string key = category + "\\" + razorFile;
|
||||
if (forceReload || !_templates.TryGetValue(key, out var razor)) {
|
||||
string template = _game.OpenString(category, razorFile);
|
||||
_templates[key] = razor = _razorEngine.Compile<BraverTemplate>(template, builder => {
|
||||
builder.AddAssemblyReference(typeof(RazorLayoutCache));
|
||||
builder.AddAssemblyReference(typeof(SaveData));
|
||||
builder.AddAssemblyReference(typeof(Ficedula.FF7.Item));
|
||||
builder.AddAssemblyReference(typeof(Enumerable));
|
||||
});
|
||||
}
|
||||
return razor;
|
||||
}
|
||||
|
||||
public string ApplyPartial(string category, string razorFile, bool forceReload, object model) {
|
||||
return Compile(category, razorFile, forceReload).Run(template => {
|
||||
template.Model = model;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -67,8 +67,8 @@ namespace Braver.Field {
|
||||
private UI.Layout.Image iPointer;
|
||||
|
||||
public void Reset(FGame game) {
|
||||
var cache = game.Singleton(() => new UI.Layout.RazorLayoutCache(game));
|
||||
string xml = cache.ApplyPartial("dialog", false, this);
|
||||
var cache = game.Singleton(() => new RazorLayoutCache(game));
|
||||
string xml = cache.ApplyPartial("layout", "dialog.xml", false, this);
|
||||
Visual = Serialisation.Deserialise<UI.Layout.Component>(xml) as UI.Layout.Container;
|
||||
lText = Visual.Children.Single(c => c.ID == nameof(lText)) as UI.Layout.Label;
|
||||
lVariable = Visual.Children.SingleOrDefault(c => c.ID == nameof(lVariable)) as UI.Layout.Label;
|
||||
|
@ -148,6 +148,15 @@ namespace Braver {
|
||||
return new Vector2(v.X, v.Y);
|
||||
}
|
||||
|
||||
public static Vector3 Parse3(string s) {
|
||||
string[] parts = s.Split('/');
|
||||
return new Vector3(
|
||||
float.Parse(parts[0]),
|
||||
float.Parse(parts[1]),
|
||||
float.Parse(parts[2])
|
||||
);
|
||||
}
|
||||
|
||||
public static bool LineCircleIntersect(Vector2 line0, Vector2 line1, Vector2 center, float radius) {
|
||||
Vector2 ac = center - line0;
|
||||
Vector2 ab = line1 - line0;
|
||||
|
@ -377,7 +377,7 @@ namespace Braver.UI.Layout {
|
||||
public string Description { get; set; }
|
||||
}
|
||||
|
||||
public abstract class LayoutModel {
|
||||
public abstract class LayoutModel : IBraverTemplateModel {
|
||||
|
||||
protected Dictionary<string, Component> _components = new Dictionary<string, Component>(StringComparer.InvariantCultureIgnoreCase);
|
||||
protected FGame _game;
|
||||
@ -402,6 +402,9 @@ namespace Braver.UI.Layout {
|
||||
|
||||
public virtual bool IsRazorModel => false;
|
||||
|
||||
string IBraverTemplateModel.SourceCategory => "layout";
|
||||
string IBraverTemplateModel.SourceExtension => "xml";
|
||||
|
||||
private class FocusEntry {
|
||||
public string ContainerID { get; set; }
|
||||
public string FocusID { get; set; }
|
||||
@ -509,66 +512,14 @@ namespace Braver.UI.Layout {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class BraverTemplate : RazorEngineTemplateBase {
|
||||
//public new FGame Model { get; set; }
|
||||
|
||||
public FGame GameModel {
|
||||
get {
|
||||
switch (Model) {
|
||||
case FGame game:
|
||||
return game;
|
||||
case LayoutModel model:
|
||||
return model.Game;
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string Bool(bool b) {
|
||||
return b.ToString().ToLower();
|
||||
}
|
||||
|
||||
public string Include(string templateName, object model) {
|
||||
var cache = GameModel.Singleton(() => new RazorLayoutCache(GameModel));
|
||||
return cache.ApplyPartial(templateName, false, model);
|
||||
}
|
||||
}
|
||||
|
||||
internal class RazorLayoutCache {
|
||||
private Dictionary<string, IRazorEngineCompiledTemplate<BraverTemplate>> _templates = new Dictionary<string, IRazorEngineCompiledTemplate<BraverTemplate>>(StringComparer.InvariantCultureIgnoreCase);
|
||||
private IRazorEngine _razorEngine = new RazorEngine();
|
||||
private FGame _game;
|
||||
|
||||
public RazorLayoutCache(FGame game) {
|
||||
_game = game;
|
||||
}
|
||||
|
||||
public IRazorEngineCompiledTemplate<BraverTemplate> Compile(string layout, bool forceReload) {
|
||||
if (forceReload || !_templates.TryGetValue(layout, out var razor)) {
|
||||
string template = _game.OpenString("layout", layout + ".xml");
|
||||
_templates[layout] = razor = _razorEngine.Compile<BraverTemplate>(template, builder => {
|
||||
builder.AddAssemblyReference(typeof(RazorLayoutCache));
|
||||
builder.AddAssemblyReference(typeof(SaveData));
|
||||
builder.AddAssemblyReference(typeof(Ficedula.FF7.Item));
|
||||
builder.AddAssemblyReference(typeof(Enumerable));
|
||||
});
|
||||
}
|
||||
return razor;
|
||||
}
|
||||
|
||||
public Layout Apply(string layout, bool forceReload, object model = null) {
|
||||
string xml = Compile(layout, forceReload).Run(template => {
|
||||
template.Model = model ?? _game;
|
||||
internal static class LayoutRazor {
|
||||
public static Layout ApplyLayout(this RazorLayoutCache cache, string layout, bool forceReload, object model = null) {
|
||||
string xml = cache.Compile("layout", layout + ".xml", forceReload).Run(template => {
|
||||
template.Model = model ?? cache.Game;
|
||||
});
|
||||
return Serialisation.Deserialise<Layout>(xml);
|
||||
}
|
||||
public string ApplyPartial(string layout, bool forceReload, object model) {
|
||||
return Compile(layout, forceReload).Run(template => {
|
||||
template.Model = model;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class LayoutScreen : Screen, ILayoutScreen {
|
||||
@ -580,7 +531,7 @@ namespace Braver.UI.Layout {
|
||||
private static Task _backgroundLoad;
|
||||
public static void BeginBackgroundLoad(FGame g, string layout) {
|
||||
var cache = g.Singleton(() => new RazorLayoutCache(g));
|
||||
_backgroundLoad = Task.Run(() => cache.Compile(layout, true));
|
||||
_backgroundLoad = Task.Run(() => cache.Compile("layout", layout + ".xml", true));
|
||||
}
|
||||
|
||||
public override Color ClearColor => Color.Black;
|
||||
@ -613,7 +564,7 @@ namespace Braver.UI.Layout {
|
||||
_backgroundLoad.Wait();
|
||||
_backgroundLoad = null;
|
||||
}
|
||||
_layout = g.Singleton(() => new RazorLayoutCache(g)).Apply(_layoutFile, false, _model.IsRazorModel ? _model : null);
|
||||
_layout = g.Singleton(() => new RazorLayoutCache(g)).ApplyLayout(_layoutFile, false, _model.IsRazorModel ? _model : null);
|
||||
_model.Init(_layout);
|
||||
_ui = new UIBatch(graphics, g);
|
||||
if (!_isEmbedded) g.Net.Send(new Net.ScreenReadyMessage());
|
||||
@ -622,7 +573,7 @@ namespace Braver.UI.Layout {
|
||||
|
||||
public void Reload(bool forceReload = false) {
|
||||
_model.Created(Game, this);
|
||||
_layout = Game.Singleton(() => new RazorLayoutCache(Game)).Apply(_layoutFile, forceReload, _model.IsRazorModel ? _model : null);
|
||||
_layout = Game.Singleton(() => new RazorLayoutCache(Game)).ApplyLayout(_layoutFile, forceReload, _model.IsRazorModel ? _model : null);
|
||||
_model.Init(_layout);
|
||||
Plugins.Call(ui => ui.Reloaded());
|
||||
}
|
||||
@ -750,7 +701,7 @@ namespace Braver.UI.Layout {
|
||||
|
||||
IComponent ILayoutScreen.Load(string templateName, object? model) {
|
||||
var cache = Game.Singleton(() => new RazorLayoutCache(Game));
|
||||
string xml = cache.ApplyPartial(templateName, false, model ?? _model);
|
||||
string xml = cache.ApplyPartial("layout", templateName + ".xml", false, model ?? _model);
|
||||
return Serialisation.Deserialise<Component>(xml);
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ DATA FILE? kernel %ff7%\data\lang-en\kernel
|
||||
|
||||
DATA FILE root %ff7%
|
||||
|
||||
DATA FILE? battle %braver%\battle
|
||||
DATA FILE? field %braver%\field
|
||||
DATA FILE? layout %braver%\layout
|
||||
DATA FILE? movies %braver%\movies
|
||||
|
@ -9,6 +9,7 @@ using Ficedula.FF7.Exporters;
|
||||
|
||||
Console.WriteLine("F7Cmd");
|
||||
|
||||
/*
|
||||
using (var lgp = new Ficedula.FF7.LGPFile(@"C:\games\FF7\data\battle\magic.lgp")) {
|
||||
|
||||
foreach(string s in lgp.Filenames.Where(s => s.Contains("bio")))
|
||||
@ -25,24 +26,8 @@ using (var lgp = new Ficedula.FF7.LGPFile(@"C:\games\FF7\data\battle\magic.lgp")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tex = new Ficedula.FF7.TexFile(lgp.Open("fire00.tex"));
|
||||
foreach (int pal in Enumerable.Range(0, tex.Palettes.Count))
|
||||
using (var s = File.OpenWrite(@$"C:\temp\fire00#{pal}.png"))
|
||||
Ficedula.FF7.Exporters.TexFileUtil.ToBitmap(tex, pal).Encode(SkiaSharp.SKEncodedImageFormat.Png, 100)
|
||||
.SaveTo(s);
|
||||
|
||||
|
||||
/*
|
||||
foreach (string fn in new[] { "fire_1.s", "fire00.tex", "fire01.tex", "fire_2.s" }) {
|
||||
using (var s = lgp.Open(fn)) {
|
||||
using (var fs = File.OpenWrite(@"C:\temp\" + fn))
|
||||
s.CopyTo(fs);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
foreach(string file in Directory.GetFiles(@"C:\temp\wm_us", "*.a")) {
|
||||
|
66
Ficedula.FF7.Exporters/ExportTypes.cs
Normal file
66
Ficedula.FF7.Exporters/ExportTypes.cs
Normal file
@ -0,0 +1,66 @@
|
||||
// This program and the accompanying materials are made available under the terms of the
|
||||
// Eclipse Public License v2.0 which accompanies this distribution, and is available at
|
||||
// https://www.eclipse.org/legal/epl-v20.html
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ficedula.FF7.Exporters {
|
||||
|
||||
public abstract class DataSource {
|
||||
public abstract bool Exists(string name);
|
||||
public abstract Stream Open(string name);
|
||||
public abstract IEnumerable<string> AllFiles { get; }
|
||||
|
||||
public virtual Stream TryOpen(string name) {
|
||||
if (Exists(name))
|
||||
return Open(name);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
private class FileDataSource : DataSource {
|
||||
private string _root;
|
||||
|
||||
public override IEnumerable<string> AllFiles => Directory.GetFiles(_root)
|
||||
.Select(fn => fn.Substring(_root.Length).TrimStart(Path.DirectorySeparatorChar));
|
||||
|
||||
public FileDataSource(string root) {
|
||||
_root = root;
|
||||
}
|
||||
|
||||
public override bool Exists(string name) => File.Exists(Path.Combine(_root, name));
|
||||
public override Stream Open(string name) => File.OpenRead(Path.Combine(_root, name));
|
||||
}
|
||||
|
||||
private class LGPDataSource : DataSource, IDisposable {
|
||||
private LGPFile _lgp;
|
||||
|
||||
public override IEnumerable<string> AllFiles => _lgp.Filenames;
|
||||
|
||||
public LGPDataSource(string file) {
|
||||
_lgp = new LGPFile(File.OpenRead(file));
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
_lgp.Dispose();
|
||||
}
|
||||
|
||||
public override bool Exists(string name) => _lgp.Exists(name);
|
||||
public override Stream Open(string name) => _lgp.Open(name);
|
||||
}
|
||||
|
||||
public static DataSource Create(string source) {
|
||||
if (Directory.Exists(source))
|
||||
return new FileDataSource(source);
|
||||
else
|
||||
return new LGPDataSource(source);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -151,11 +151,13 @@ namespace Braver.Tolk {
|
||||
DavyKager.Tolk.Speak(action, false);
|
||||
}
|
||||
|
||||
/*
|
||||
public void BattleActionResult(IInProgress result) {
|
||||
string s = result.Description;
|
||||
if (!string.IsNullOrEmpty(s))
|
||||
DavyKager.Tolk.Speak(s, false);
|
||||
}
|
||||
*/
|
||||
|
||||
public void Showing(int window, int tag, IEnumerable<string> text) {
|
||||
//
|
||||
|
24
data/battle/magic/fire1.effect
Normal file
24
data/battle/magic/fire1.effect
Normal file
@ -0,0 +1,24 @@
|
||||
PreloadSprite fire fi_a01.s fire00.tex fire01.tex
|
||||
PreloadSfx 8
|
||||
|
||||
@foreach(var text in Model.Action.QueuedText) {
|
||||
@:!txt Text @Model.Expand(text)
|
||||
@:Wait !txt
|
||||
}
|
||||
|
||||
!cast Anim source 17
|
||||
Wait !cast
|
||||
#Camera
|
||||
|
||||
Sfx 8
|
||||
@foreach(var result in Model.Results) {
|
||||
@:!flames Sprite fire @result.Target.ID
|
||||
@:Result @result.Target.ID
|
||||
if (result.Hit) {
|
||||
}
|
||||
}
|
||||
|
||||
Wait !flames
|
||||
Resume source
|
||||
Wait !cast
|
||||
ApplyResults
|
Loading…
Reference in New Issue
Block a user