mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2024-08-19 00:19:19 +08:00
1252 lines
38 KiB
C#
1252 lines
38 KiB
C#
using CodeWalker.GameFiles;
|
|
using CodeWalker.Rendering;
|
|
using SharpDX;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Data;
|
|
using System.Drawing;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
|
|
namespace CodeWalker.World
|
|
{
|
|
public partial class CutsceneForm : Form
|
|
{
|
|
private WorldForm WorldForm;
|
|
private GameFileCache GameFileCache;
|
|
|
|
private Cutscene Cutscene = null;
|
|
|
|
private bool AnimateCamera = true;
|
|
private bool Playing = false;
|
|
|
|
|
|
class CutsceneDropdownItem
|
|
{
|
|
public RpfEntry RpfEntry { get; set; }
|
|
|
|
public override string ToString()
|
|
{
|
|
return RpfEntry?.Path ?? "";
|
|
}
|
|
}
|
|
|
|
public CutsceneForm(WorldForm worldForm)
|
|
{
|
|
WorldForm = worldForm;
|
|
GameFileCache = WorldForm.GameFileCache;
|
|
InitializeComponent();
|
|
}
|
|
|
|
|
|
|
|
public void UpdateAnimation(float elapsed)
|
|
{
|
|
if (Cutscene != null)
|
|
{
|
|
if (Playing)
|
|
{
|
|
var newt = Cutscene.PlaybackTime + elapsed;
|
|
Cutscene.Update(newt);
|
|
}
|
|
|
|
if (AnimateCamera && (Cutscene.CameraObject != null))
|
|
{
|
|
var pos = Cutscene.CameraObject.Position;
|
|
var rot = Cutscene.CameraObject.Rotation;
|
|
|
|
WorldForm.SetCameraTransform(pos, rot);
|
|
|
|
if (Cutscene.CameraClipUpdate)
|
|
{
|
|
WorldForm.SetCameraClipPlanes(Cutscene.CameraNearClip, Cutscene.CameraFarClip);
|
|
Cutscene.CameraClipUpdate = false;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
public void GetVisibleYmaps(Camera camera, Dictionary<MetaHash, YmapFile> ymaps)
|
|
{
|
|
//use a temporary ymap for entities?
|
|
|
|
var renderer = WorldForm?.Renderer;
|
|
if (renderer == null) return;
|
|
|
|
if (Cutscene == null) return;
|
|
Cutscene.Render(renderer);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void SelectCutscene(CutsceneDropdownItem dditem)
|
|
{
|
|
Cursor = Cursors.WaitCursor;
|
|
Task.Run(() =>
|
|
{
|
|
CutFile cutFile = null;
|
|
Cutscene cutscene = null;
|
|
|
|
if (GameFileCache.IsInited)
|
|
{
|
|
var entry = dditem?.RpfEntry as RpfFileEntry;
|
|
if (entry != null)
|
|
{
|
|
|
|
cutFile = new CutFile(entry);
|
|
GameFileCache.RpfMan.LoadFile(cutFile, entry);
|
|
|
|
cutscene = new Cutscene();
|
|
cutscene.Init(cutFile, GameFileCache, WorldForm);
|
|
|
|
}
|
|
}
|
|
|
|
CutsceneLoaded(cutscene);
|
|
|
|
});
|
|
}
|
|
private void CutsceneLoaded(Cutscene cs)
|
|
{
|
|
if (InvokeRequired)
|
|
{
|
|
try
|
|
{
|
|
Invoke(new Action(() => { CutsceneLoaded(cs); }));
|
|
}
|
|
catch
|
|
{ }
|
|
return;
|
|
}
|
|
|
|
Cutscene = cs;
|
|
|
|
LoadTreeView(cs);
|
|
|
|
TimeTrackBar.Maximum = (int)(cs.Duration * 10.0f);
|
|
TimeTrackBar.Value = 0;
|
|
UpdateTimeLabel();
|
|
|
|
Cursor = Cursors.Default;
|
|
}
|
|
|
|
|
|
private void LoadTreeView(Cutscene cs)
|
|
{
|
|
CutsceneTreeView.Nodes.Clear();
|
|
|
|
var cutFile = cs?.CutFile;
|
|
var cf = cutFile?.CutsceneFile2;
|
|
if (cf != null)
|
|
{
|
|
var csnode = CutsceneTreeView.Nodes.Add(cutFile.FileEntry?.Name);
|
|
csnode.Tag = cs;
|
|
|
|
if (cs.SceneObjects != null)
|
|
{
|
|
var objsnode = csnode.Nodes.Add("Objects");
|
|
objsnode.Name = "Objects";
|
|
|
|
foreach (var obj in cs.SceneObjects.Values)
|
|
{
|
|
var objnode = objsnode.Nodes.Add(obj.ToString());
|
|
objnode.Tag = obj;
|
|
}
|
|
}
|
|
if (cf.pCutsceneEventList != null)
|
|
{
|
|
var evtsnode = csnode.Nodes.Add("Events");
|
|
evtsnode.Name = "Events";
|
|
|
|
foreach (var evt in cf.pCutsceneEventList)
|
|
{
|
|
var evtnode = evtsnode.Nodes.Add(evt.ToString());
|
|
evtnode.Tag = evt;
|
|
}
|
|
}
|
|
if (cf.pCutsceneLoadEventList != null)
|
|
{
|
|
var ldesnode = csnode.Nodes.Add("Load Events");
|
|
ldesnode.Name = "Load Events";
|
|
|
|
foreach (var lev in cf.pCutsceneLoadEventList)
|
|
{
|
|
var ldenode = ldesnode.Nodes.Add(lev.ToString());
|
|
ldenode.Tag = lev;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
csnode.Expand();
|
|
CutsceneTreeView.SelectedNode = csnode;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void UpdateTimeTrackBar()
|
|
{
|
|
var tim = Cutscene?.PlaybackTime ?? 0.0f;
|
|
var itim = (int)(tim * 10.0f);
|
|
TimeTrackBar.Value = itim;
|
|
}
|
|
private void UpdateTimeLabel()
|
|
{
|
|
var tim = Cutscene?.PlaybackTime ?? 0.0f;
|
|
var dur = Cutscene?.Duration ?? 0.0f;
|
|
TimeLabel.Text = tim.ToString("0.00") + " / " + dur.ToString("0.00");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void CutsceneForm_Load(object sender, EventArgs e)
|
|
{
|
|
if (!GameFileCache.IsInited) return;//what to do here?
|
|
|
|
var rpfman = GameFileCache.RpfMan;
|
|
var rpflist = rpfman.AllRpfs; //loadedOnly ? gfc.ActiveMapRpfFiles.Values.ToList() :
|
|
|
|
var dditems = new List<CutsceneDropdownItem>();
|
|
foreach (var rpf in rpflist)
|
|
{
|
|
foreach (var entry in rpf.AllEntries)
|
|
{
|
|
if (entry.NameLower.EndsWith(".cut"))
|
|
{
|
|
var dditem = new CutsceneDropdownItem();
|
|
dditem.RpfEntry = entry;
|
|
dditems.Add(dditem);
|
|
}
|
|
}
|
|
}
|
|
|
|
CutsceneComboBox.Items.Clear();
|
|
CutsceneComboBox.Items.AddRange(dditems.ToArray());
|
|
|
|
|
|
}
|
|
|
|
private void CutsceneForm_FormClosed(object sender, FormClosedEventArgs e)
|
|
{
|
|
WorldForm?.OnCutsceneFormClosed();
|
|
}
|
|
|
|
private void CutsceneComboBox_SelectedIndexChanged(object sender, EventArgs e)
|
|
{
|
|
var item = CutsceneComboBox.SelectedItem as CutsceneDropdownItem;
|
|
SelectCutscene(item);
|
|
}
|
|
|
|
private void CutsceneTreeView_AfterSelect(object sender, TreeViewEventArgs e)
|
|
{
|
|
InfoPropertyGrid.SelectedObject = CutsceneTreeView.SelectedNode?.Tag;
|
|
}
|
|
|
|
private void AnimateCameraCheckBox_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
AnimateCamera = AnimateCameraCheckBox.Checked;
|
|
|
|
if (!AnimateCamera)
|
|
{
|
|
WorldForm?.ResetCameraClipPlanes();
|
|
}
|
|
else
|
|
{
|
|
if (Cutscene != null)
|
|
{
|
|
Cutscene.CameraClipUpdate = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PlayStopButton_Click(object sender, EventArgs e)
|
|
{
|
|
if (Playing)
|
|
{
|
|
Playing = false;
|
|
PlayStopButton.Text = "Play";
|
|
PlaybackTimer.Enabled = false;
|
|
}
|
|
else
|
|
{
|
|
Playing = true;
|
|
PlayStopButton.Text = "Stop";
|
|
PlaybackTimer.Enabled = true;
|
|
}
|
|
}
|
|
|
|
private void PlaybackTimer_Tick(object sender, EventArgs e)
|
|
{
|
|
if (Playing)
|
|
{
|
|
UpdateTimeTrackBar();
|
|
UpdateTimeLabel();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
[TypeConverter(typeof(ExpandableObjectConverter))] public class Cutscene
|
|
{
|
|
public CutFile CutFile { get; set; } = null;
|
|
private GameFileCache GameFileCache = null;
|
|
private WorldForm WorldForm = null;
|
|
|
|
public float[] CameraCutList { get; set; } = null;
|
|
public YcdFile[] Ycds { get; set; } = null;
|
|
|
|
|
|
public float Duration { get; set; } = 0.0f;
|
|
public float PlaybackTime { get; set; } = 0.0f;
|
|
|
|
|
|
public Dictionary<int, CutObject> Objects { get; set; } = null;
|
|
public Dictionary<int, CutsceneObject> SceneObjects { get; set; } = null;
|
|
public CutEvent[] LoadEvents { get; set; } = null;
|
|
public CutEvent[] PlayEvents { get; set; } = null;
|
|
public CutConcatData[] ConcatDatas { get; set; } = null;
|
|
|
|
public int NextLoadEvent { get; set; } = 0;
|
|
public int NextPlayEvent { get; set; } = 0;
|
|
public int NextCameraCut { get; set; } = 0;
|
|
public int NextConcatData { get; set; } = 0;
|
|
|
|
public Gxt2File Gxt2File { get; set; } = null;
|
|
|
|
public Vector3 Position { get; set; }
|
|
public Quaternion Rotation { get; set; }
|
|
|
|
|
|
public CutsceneObject CameraObject = null;
|
|
public float CameraNearClip { get; set; } = 0.5f;
|
|
public float CameraFarClip { get; set; } = 12000.0f;
|
|
public bool CameraClipUpdate = false;//signal to the form to update the camera clip planes
|
|
public Quaternion CameraRotationOffset = Quaternion.RotationAxis(Vector3.UnitX, -1.57079632679f) * Quaternion.RotationAxis(Vector3.UnitZ, 3.141592653f);
|
|
|
|
|
|
|
|
|
|
public void Init(CutFile cutFile, GameFileCache gfc, WorldForm wf)
|
|
{
|
|
CutFile = cutFile;
|
|
GameFileCache = gfc;
|
|
WorldForm = wf;
|
|
|
|
var csf = cutFile?.CutsceneFile2;
|
|
if (csf == null) return;
|
|
if (gfc == null) return;
|
|
|
|
|
|
Duration = csf.fTotalDuration;
|
|
CameraCutList = csf.cameraCutList;
|
|
Position = csf.vOffset;
|
|
Rotation = Quaternion.RotationAxis(Vector3.UnitZ, csf.fRotation);
|
|
Objects = csf.ObjectsDict;
|
|
LoadEvents = RecastArray<CutEvent>(csf.pCutsceneLoadEventList);
|
|
PlayEvents = RecastArray<CutEvent>(csf.pCutsceneEventList);
|
|
ConcatDatas = csf.concatDataList;
|
|
|
|
|
|
LoadYcds();
|
|
CreateSceneObjects();
|
|
RaiseEvents(0.0f);
|
|
}
|
|
|
|
private void LoadYcds()
|
|
{
|
|
int cutListCount = (CameraCutList?.Length ?? 0) + 1;
|
|
var shortName = CutFile.FileEntry?.GetShortNameLower() ?? "";
|
|
Ycds = new YcdFile[cutListCount];
|
|
if (!string.IsNullOrEmpty(shortName))
|
|
{
|
|
for (int i = 0; i < cutListCount; i++)
|
|
{
|
|
var ycdname = shortName + "-" + i.ToString();
|
|
var ycdhash = JenkHash.GenHash(ycdname);
|
|
var ycd = GameFileCache.GetYcd(ycdhash);
|
|
while ((ycd != null) && (!ycd.Loaded))
|
|
{
|
|
Thread.Sleep(1);//bite me
|
|
ycd = GameFileCache.GetYcd(ycdhash);
|
|
}
|
|
if (ycd != null)
|
|
{
|
|
ycd.BuildCutsceneMap(i);
|
|
}
|
|
Ycds[i] = ycd;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public void Update(float newTime)
|
|
{
|
|
if (newTime > Duration)
|
|
{
|
|
newTime = 0.0f; //stop or loop?
|
|
}
|
|
|
|
if (newTime >= PlaybackTime)
|
|
{
|
|
RaiseEvents(newTime);
|
|
}
|
|
else
|
|
{
|
|
//reset playback to beginning, and seek to newTime
|
|
RaiseEvents(Duration);//raise all events up to the end first
|
|
PlaybackTime = 0.0f;
|
|
NextLoadEvent = 0;
|
|
NextPlayEvent = 0;
|
|
NextCameraCut = 0;
|
|
NextConcatData = 0;
|
|
RaiseEvents(newTime);
|
|
}
|
|
|
|
PlaybackTime = newTime;
|
|
|
|
int cutIndex = 0;
|
|
float cutStart = 0.0f;
|
|
for (cutIndex = 0; cutIndex < CameraCutList?.Length; cutIndex++)
|
|
{
|
|
var cutTime = CameraCutList[cutIndex];
|
|
if (cutTime > newTime) break;
|
|
cutStart = cutTime;
|
|
}
|
|
|
|
float cutOffset = newTime - cutStart;//offset into the current cut
|
|
|
|
|
|
void updateObjectTransform(CutsceneObject obj, ClipMapEntry cme, ushort boneTag, byte posTrack, byte rotTrack)
|
|
{
|
|
if (cme != null)
|
|
{
|
|
if (cme.Clip is ClipAnimation canim)
|
|
{
|
|
if (canim.Animation != null)
|
|
{
|
|
var t = canim.GetPlaybackTime(cutOffset);
|
|
var f = canim.Animation.GetFramePosition(t);
|
|
var p = canim.Animation.FindBoneIndex(boneTag, posTrack);
|
|
var r = canim.Animation.FindBoneIndex(boneTag, rotTrack);
|
|
if (p >= 0) obj.Position = canim.Animation.EvaluateVector4(f, p, true).XYZ();
|
|
if (r >= 0) obj.Rotation = canim.Animation.EvaluateQuaternion(f, r, true);
|
|
}
|
|
}
|
|
else if (cme.Clip is ClipAnimationList alist)
|
|
{
|
|
if (alist.Animations?.Data != null)
|
|
{
|
|
foreach (var anim in alist.Animations.Data)
|
|
{
|
|
var t = anim.GetPlaybackTime(cutOffset);
|
|
var f = anim.Animation.GetFramePosition(t);
|
|
var p = anim.Animation.FindBoneIndex(boneTag, posTrack);
|
|
var r = anim.Animation.FindBoneIndex(boneTag, rotTrack);
|
|
if (p >= 0) obj.Position = anim.Animation.EvaluateVector4(f, p, true).XYZ();
|
|
if (r >= 0) obj.Rotation = anim.Animation.EvaluateQuaternion(f, r, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
var ycd = (cutIndex < (Ycds?.Length ?? 0)) ? Ycds[cutIndex] : null;
|
|
if (ycd?.CutsceneMap != null)
|
|
{
|
|
ClipMapEntry cme = null;
|
|
|
|
if (CameraObject != null)
|
|
{
|
|
ycd.CutsceneMap.TryGetValue(CameraObject.Name, out cme);
|
|
|
|
updateObjectTransform(CameraObject, cme, 0, 7, 8);
|
|
|
|
|
|
if (cme != null)
|
|
{
|
|
var pos = Position;
|
|
var rot = Rotation;
|
|
pos = pos + rot.Multiply(CameraObject.Position);
|
|
rot = rot * CameraObject.Rotation * CameraRotationOffset;
|
|
CameraObject.Position = pos;
|
|
CameraObject.Rotation = rot;
|
|
}
|
|
|
|
}
|
|
|
|
if (SceneObjects != null)
|
|
{
|
|
foreach (var obj in SceneObjects.Values)
|
|
{
|
|
if (obj.Enabled == false) continue;
|
|
|
|
var pos = Position;
|
|
var rot = Rotation;
|
|
var animate = (obj.Ped != null) || (obj.Prop != null) || (obj.Vehicle != null) || (obj.Weapon != null);
|
|
if (animate)
|
|
{
|
|
ycd.CutsceneMap.TryGetValue(obj.AnimHash, out cme);
|
|
if (cme != null)
|
|
{
|
|
cme.OverridePlayTime = true;
|
|
cme.PlayTime = cutOffset;
|
|
updateObjectTransform(obj, cme, 0, 5, 6); //using root animation bone ids
|
|
pos = pos + rot.Multiply(obj.Position);
|
|
rot = rot * obj.Rotation;
|
|
}
|
|
obj.AnimClip = cme;
|
|
}
|
|
if (obj.Ped != null)
|
|
{
|
|
obj.Ped.Position = pos;
|
|
obj.Ped.Rotation = rot;
|
|
obj.Ped.UpdateEntity();
|
|
obj.Ped.AnimClip = cme;
|
|
}
|
|
if (obj.Prop != null)
|
|
{
|
|
obj.Prop.Position = pos;
|
|
obj.Prop.Orientation = rot;
|
|
}
|
|
if (obj.Vehicle != null)
|
|
{
|
|
obj.Vehicle.Position = pos;
|
|
obj.Vehicle.Rotation = rot;
|
|
obj.Vehicle.UpdateEntity();
|
|
}
|
|
if (obj.Weapon != null)
|
|
{
|
|
obj.Weapon.Position = pos;
|
|
obj.Weapon.Rotation = rot;
|
|
obj.Weapon.UpdateEntity();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
public void Render(Renderer renderer)
|
|
{
|
|
|
|
if (SceneObjects != null)
|
|
{
|
|
foreach (var obj in SceneObjects.Values)
|
|
{
|
|
if (obj.Enabled == false) continue;
|
|
|
|
if (obj.Ped != null)
|
|
{
|
|
renderer.RenderPed(obj.Ped);
|
|
}
|
|
if (obj.Prop != null)
|
|
{
|
|
renderer.RenderArchetype(obj.Prop.Archetype, obj.Prop, null, true, obj.AnimClip);
|
|
}
|
|
if (obj.Vehicle != null)
|
|
{
|
|
renderer.RenderVehicle(obj.Vehicle, obj.AnimClip);
|
|
}
|
|
if (obj.Weapon != null)
|
|
{
|
|
renderer.RenderWeapon(obj.Weapon, obj.AnimClip);
|
|
}
|
|
}
|
|
foreach (var obj in SceneObjects.Values)
|
|
{
|
|
if (obj.Enabled == false) continue;
|
|
|
|
if (obj.HideEntity != null)
|
|
{
|
|
renderer.RenderHideEntity(obj.HideEntity);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void RaiseEvents(float upToTime)
|
|
{
|
|
|
|
int i;
|
|
for (i = NextLoadEvent; i < LoadEvents?.Length; i++)
|
|
{
|
|
var e = LoadEvents[i];
|
|
if (e != null)
|
|
{
|
|
if (e.fTime > upToTime) break;
|
|
RaiseEvent(e);
|
|
}
|
|
}
|
|
NextLoadEvent = i;
|
|
|
|
for (i = NextPlayEvent; i < PlayEvents?.Length; i++)
|
|
{
|
|
var e = PlayEvents[i];
|
|
if (e != null)
|
|
{
|
|
if (e.fTime > upToTime) break;
|
|
RaiseEvent(e);
|
|
}
|
|
}
|
|
NextPlayEvent = i;
|
|
|
|
for (i = NextCameraCut; i < CameraCutList?.Length; i++)
|
|
{
|
|
var c = CameraCutList[i];
|
|
if (c > upToTime) break;
|
|
}
|
|
NextCameraCut = i;
|
|
|
|
for (i = NextConcatData; i < ConcatDatas?.Length; i++)
|
|
{
|
|
var c = ConcatDatas[i];
|
|
if (c.fStartTime > upToTime) break;
|
|
if (c.cSceneName == 0) break;
|
|
|
|
Position = c.vOffset;
|
|
Rotation = Quaternion.RotationAxis(Vector3.UnitZ, c.fRotation * 0.0174532925f); //is this right?
|
|
}
|
|
NextConcatData = i;
|
|
|
|
|
|
}
|
|
private void RaiseEvent(CutEvent e)
|
|
{
|
|
|
|
switch (e.iEventId)
|
|
{
|
|
case CutEventType.LoadScene: LoadScene(e); break;
|
|
case CutEventType.LoadAnimation: LoadAnimation(e); break;
|
|
case CutEventType.LoadAudio: LoadAudio(e); break;
|
|
case CutEventType.LoadModels: LoadModels(e); break;
|
|
case CutEventType.LoadRayfireDes: LoadRayfireDes(e); break;
|
|
case CutEventType.LoadParticles: LoadParticles(e); break;
|
|
case CutEventType.LoadOverlays: LoadOverlays(e); break;
|
|
case CutEventType.LoadGxt2: LoadGxt2(e); break;
|
|
case CutEventType.UnloadModels: UnloadModels(e); break;
|
|
case CutEventType.UnloadRayfireDes: UnloadRayfireDes(e); break;
|
|
case CutEventType.EnableScreenFade: EnableScreenFade(e); break;
|
|
case CutEventType.EnableHideObject: EnableHideObject(e); break;
|
|
case CutEventType.EnableFixupModel: EnableFixupModel(e); break;
|
|
case CutEventType.EnableBlockBounds: EnableBlockBounds(e); break;
|
|
case CutEventType.EnableAnimation: EnableAnimation(e); break;
|
|
case CutEventType.EnableParticleEffect: EnableParticleEffect(e); break;
|
|
case CutEventType.EnableOverlay: EnableOverlay(e); break;
|
|
case CutEventType.EnableAudio: EnableAudio(e); break;
|
|
case CutEventType.EnableCamera: EnableCamera(e); break;
|
|
case CutEventType.EnableLight: EnableLight(e); break;
|
|
case CutEventType.DisableScreenFade: DisableScreenFade(e); break;
|
|
case CutEventType.DisableHideObject: DisableHideObject(e); break;
|
|
case CutEventType.DisableBlockBounds: DisableBlockBounds(e); break;
|
|
case CutEventType.DisableAnimation: DisableAnimation(e); break;
|
|
case CutEventType.DisableParticleEffect: DisableParticleEffect(e); break;
|
|
case CutEventType.DisableOverlay: DisableOverlay(e); break;
|
|
case CutEventType.DisableAudio: DisableAudio(e); break;
|
|
case CutEventType.DisableCamera: DisableCamera(e); break;
|
|
case CutEventType.DisableLight: DisableLight(e); break;
|
|
case CutEventType.Subtitle: Subtitle(e); break;
|
|
case CutEventType.PedVariation: PedVariation(e); break;
|
|
case CutEventType.CameraCut: CameraCut(e); break;
|
|
case CutEventType.CameraShadowCascade: CameraShadowCascade(e); break;
|
|
case CutEventType.CameraUnk1: CameraUnk1(e); break;
|
|
case CutEventType.CameraUnk2: CameraUnk2(e); break;
|
|
case CutEventType.CameraUnk3: CameraUnk3(e); break;
|
|
case CutEventType.CameraUnk4: CameraUnk4(e); break;
|
|
case CutEventType.CameraUnk5: CameraUnk5(e); break;
|
|
case CutEventType.CameraUnk6: CameraUnk6(e); break;
|
|
case CutEventType.CameraUnk7: CameraUnk7(e); break;
|
|
case CutEventType.CameraUnk8: CameraUnk8(e); break;
|
|
case CutEventType.DecalUnk1: DecalUnk1(e); break;
|
|
case CutEventType.DecalUnk2: DecalUnk2(e); break;
|
|
case CutEventType.PropUnk1: PropUnk1(e); break;
|
|
case CutEventType.Unk1: Unk1(e); break;
|
|
case CutEventType.Unk2: Unk2(e); break;
|
|
case CutEventType.VehicleUnk1: VehicleUnk1(e); break;
|
|
case CutEventType.PedUnk1: PedUnk1(e); break;
|
|
default: break;
|
|
}
|
|
|
|
}
|
|
|
|
private void LoadScene(CutEvent e)
|
|
{
|
|
var args = e.EventArgs as CutLoadSceneEventArgs;
|
|
if (args == null)
|
|
{ return; }
|
|
|
|
|
|
Position = args.vOffset;
|
|
Rotation = Quaternion.RotationAxis(Vector3.UnitZ, args.fRotation * 0.0174532925f);//is this right?
|
|
|
|
}
|
|
private void LoadAnimation(CutEvent e)
|
|
{
|
|
var args = e.EventArgs as CutNameEventArgs;
|
|
if (args == null)
|
|
{ return; }
|
|
|
|
}
|
|
private void LoadAudio(CutEvent e)
|
|
{
|
|
var args = e.EventArgs as CutNameEventArgs;
|
|
if (args == null)
|
|
{ return; }
|
|
|
|
}
|
|
private void LoadModels(CutEvent e)
|
|
{
|
|
var args = e.EventArgs as CutObjectIdListEventArgs;
|
|
if (args == null)
|
|
{ return; }
|
|
|
|
if (args.iObjectIdList == null) return;
|
|
|
|
foreach (var objid in args.iObjectIdList)
|
|
{
|
|
CutsceneObject obj = null;
|
|
SceneObjects.TryGetValue(objid, out obj);
|
|
if (obj != null)
|
|
{
|
|
obj.Enabled = true;
|
|
}
|
|
}
|
|
}
|
|
private void LoadRayfireDes(CutEvent e)
|
|
{
|
|
}
|
|
private void LoadParticles(CutEvent e)
|
|
{
|
|
var args = e.EventArgs as CutObjectIdListEventArgs;
|
|
if (args == null)
|
|
{ return; }
|
|
|
|
}
|
|
private void LoadOverlays(CutEvent e)
|
|
{
|
|
var args = e.EventArgs as CutObjectIdListEventArgs;
|
|
if (args == null)
|
|
{ return; }
|
|
|
|
}
|
|
private void LoadGxt2(CutEvent e)
|
|
{
|
|
if (GameFileCache == null)
|
|
{ return; }
|
|
if (Gxt2File != null)
|
|
{ }
|
|
|
|
var args = e.EventArgs as CutFinalNameEventArgs;
|
|
if (args == null)
|
|
{ return; }
|
|
|
|
var namel = args.cName?.ToLowerInvariant();
|
|
var namehash = JenkHash.GenHash(namel);
|
|
|
|
RpfFileEntry gxt2entry = null;
|
|
GameFileCache.Gxt2Dict.TryGetValue(namehash, out gxt2entry);
|
|
|
|
if (gxt2entry != null) //probably should do this load async
|
|
{
|
|
Gxt2File = GameFileCache.RpfMan.GetFile<Gxt2File>(gxt2entry);
|
|
|
|
if (Gxt2File != null)
|
|
{
|
|
for (int i = 0; i < Gxt2File.TextEntries.Length; i++)
|
|
{
|
|
var te = Gxt2File.TextEntries[i];
|
|
GlobalText.Ensure(te.Text, te.Hash);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
private void UnloadModels(CutEvent e)
|
|
{
|
|
var args = e.EventArgs as CutObjectIdListEventArgs;
|
|
if (args == null)
|
|
{ return; }
|
|
|
|
if (args.iObjectIdList == null) return;
|
|
|
|
foreach (var objid in args.iObjectIdList)
|
|
{
|
|
CutsceneObject obj = null;
|
|
SceneObjects.TryGetValue(objid, out obj);
|
|
if (obj != null)
|
|
{
|
|
obj.Enabled = false;
|
|
}
|
|
}
|
|
}
|
|
private void UnloadRayfireDes(CutEvent e)
|
|
{
|
|
}
|
|
private void EnableHideObject(CutEvent e)
|
|
{
|
|
var oe = e as CutObjectIdEvent;
|
|
if (oe == null) return;
|
|
|
|
CutsceneObject cso = null;
|
|
SceneObjects.TryGetValue(oe.iObjectId, out cso);
|
|
if (cso != null)
|
|
{
|
|
cso.Enabled = true;
|
|
}
|
|
}
|
|
private void EnableFixupModel(CutEvent e)
|
|
{
|
|
}
|
|
private void EnableBlockBounds(CutEvent e)
|
|
{
|
|
var oe = e as CutObjectIdEvent;
|
|
if (oe == null) return;
|
|
|
|
}
|
|
private void EnableScreenFade(CutEvent e)
|
|
{
|
|
}
|
|
private void EnableAnimation(CutEvent e)
|
|
{
|
|
var oe = e as CutObjectIdEvent;
|
|
if (oe == null) return;
|
|
|
|
}
|
|
private void EnableParticleEffect(CutEvent e)
|
|
{
|
|
}
|
|
private void EnableOverlay(CutEvent e)
|
|
{
|
|
}
|
|
private void EnableAudio(CutEvent e)
|
|
{
|
|
var args = e.EventArgs as CutNameEventArgs;
|
|
if (args == null)
|
|
{ return; }
|
|
|
|
}
|
|
private void EnableCamera(CutEvent e)
|
|
{
|
|
var oe = e as CutObjectIdEvent;
|
|
if (oe == null) return;
|
|
|
|
}
|
|
private void EnableLight(CutEvent e)
|
|
{
|
|
}
|
|
private void DisableHideObject(CutEvent e)
|
|
{
|
|
var oe = e as CutObjectIdEvent;
|
|
if (oe == null) return;
|
|
|
|
CutsceneObject cso = null;
|
|
SceneObjects.TryGetValue(oe.iObjectId, out cso);
|
|
if (cso != null)
|
|
{
|
|
cso.Enabled = false;
|
|
}
|
|
}
|
|
private void DisableBlockBounds(CutEvent e)
|
|
{
|
|
var oe = e as CutObjectIdEvent;
|
|
if (oe == null) return;
|
|
|
|
}
|
|
private void DisableScreenFade(CutEvent e)
|
|
{
|
|
}
|
|
private void DisableAnimation(CutEvent e)
|
|
{
|
|
var oe = e as CutObjectIdEvent;
|
|
if (oe == null) return;
|
|
|
|
}
|
|
private void DisableParticleEffect(CutEvent e)
|
|
{
|
|
}
|
|
private void DisableOverlay(CutEvent e)
|
|
{
|
|
}
|
|
private void DisableAudio(CutEvent e)
|
|
{
|
|
var args = e.EventArgs as CutNameEventArgs;
|
|
if (args == null)
|
|
{ return; }
|
|
|
|
}
|
|
private void DisableCamera(CutEvent e)
|
|
{
|
|
var oe = e as CutObjectIdEvent;
|
|
if (oe == null) return;
|
|
|
|
}
|
|
private void DisableLight(CutEvent e)
|
|
{
|
|
}
|
|
private void Subtitle(CutEvent e)
|
|
{
|
|
var args = e.EventArgs as CutSubtitleEventArgs;
|
|
if (args == null)
|
|
{ return; }
|
|
|
|
if (WorldForm != null)
|
|
{
|
|
var txt = args.cName.ToString();
|
|
var dur = args.fSubtitleDuration;
|
|
|
|
txt = txt.Replace("~z~", "");
|
|
txt = txt.Replace("~c~~n~", "\n - ");
|
|
txt = txt.Replace("~n~", "\n");
|
|
txt = txt.Replace("~c~", " - ");
|
|
txt = txt.Replace("~t~", " - ");
|
|
|
|
WorldForm.ShowSubtitle(txt, dur);
|
|
}
|
|
}
|
|
private void PedVariation(CutEvent e)
|
|
{
|
|
var args = e.EventArgs as CutObjectVariationEventArgs;
|
|
if (args == null)
|
|
{ return; }
|
|
|
|
var oe = e as CutObjectIdEvent;
|
|
if (oe == null)
|
|
{ return; }
|
|
|
|
|
|
CutsceneObject cso = null;
|
|
SceneObjects.TryGetValue(oe.iObjectId, out cso);
|
|
|
|
if (cso?.Ped != null)
|
|
{
|
|
int comp = args.iComponent;
|
|
int drbl = args.iDrawable;
|
|
int texx = args.iTexture;
|
|
|
|
Task.Run(() =>
|
|
{
|
|
cso.Ped.SetComponentDrawable(comp, drbl, 0, texx, GameFileCache);
|
|
});
|
|
}
|
|
|
|
|
|
}
|
|
private void CameraCut(CutEvent e)
|
|
{
|
|
var args = e.EventArgs as CutCameraCutEventArgs;
|
|
if (args == null)
|
|
{ return; }
|
|
|
|
var oe = e as CutObjectIdEvent;
|
|
if (oe == null)
|
|
{ return; }
|
|
|
|
|
|
CutsceneObject obj = null;
|
|
SceneObjects.TryGetValue(oe.iObjectId, out obj);
|
|
if (obj == null)
|
|
{ return; }
|
|
|
|
|
|
var pos = Position;
|
|
var rot = Rotation * Quaternion.RotationAxis(Vector3.UnitX, 1.57079632679f);
|
|
obj.Position = pos + rot.Multiply(args.vPosition);
|
|
obj.Rotation = rot * args.vRotationQuaternion;
|
|
|
|
CameraNearClip = (args.fNearDrawDistance > 0) ? Math.Min(args.fNearDrawDistance, 0.5f) : 0.5f;
|
|
CameraFarClip = (args.fFarDrawDistance > 0) ? Math.Max(args.fFarDrawDistance, 1000.0f) : 12000.0f;
|
|
CameraClipUpdate = true;
|
|
CameraObject = obj;
|
|
}
|
|
private void CameraShadowCascade(CutEvent e)
|
|
{
|
|
}
|
|
private void CameraUnk1(CutEvent e)
|
|
{
|
|
}
|
|
private void CameraUnk2(CutEvent e)
|
|
{
|
|
}
|
|
private void CameraUnk3(CutEvent e)
|
|
{
|
|
}
|
|
private void CameraUnk4(CutEvent e)
|
|
{
|
|
}
|
|
private void CameraUnk5(CutEvent e)
|
|
{
|
|
}
|
|
private void CameraUnk6(CutEvent e)
|
|
{
|
|
}
|
|
private void CameraUnk7(CutEvent e)
|
|
{
|
|
}
|
|
private void CameraUnk8(CutEvent e)
|
|
{
|
|
}
|
|
private void DecalUnk1(CutEvent e)
|
|
{
|
|
}
|
|
private void DecalUnk2(CutEvent e)
|
|
{
|
|
}
|
|
private void PropUnk1(CutEvent e)
|
|
{
|
|
}
|
|
private void Unk1(CutEvent e)
|
|
{
|
|
}
|
|
private void Unk2(CutEvent e)
|
|
{
|
|
}
|
|
private void VehicleUnk1(CutEvent e)
|
|
{
|
|
}
|
|
private void PedUnk1(CutEvent e)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
private T[] RecastArray<T>(object[] arr) where T : class
|
|
{
|
|
if (arr == null) return null;
|
|
var r = new T[arr.Length];
|
|
for (int i = 0; i < arr.Length; i++)
|
|
{
|
|
r[i] = arr[i] as T;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
|
|
private void CreateSceneObjects()
|
|
{
|
|
SceneObjects = new Dictionary<int, CutsceneObject>();
|
|
|
|
if (Objects == null) return;
|
|
|
|
|
|
var refCounts = new Dictionary<MetaHash, int>();
|
|
|
|
foreach (var obj in Objects.Values)
|
|
{
|
|
var sobj = new CutsceneObject();
|
|
sobj.Init(obj, GameFileCache);
|
|
SceneObjects[sobj.ObjectID] = sobj;
|
|
|
|
if (sobj.AnimHash != 0)
|
|
{
|
|
int refcount = 0;
|
|
var hash = sobj.AnimHash;
|
|
refCounts.TryGetValue(hash, out refcount);
|
|
if (refcount > 0)
|
|
{
|
|
var newstr = hash.ToString() + "^" + refcount.ToString();
|
|
sobj.AnimHash = JenkHash.GenHash(newstr);
|
|
}
|
|
refcount++;
|
|
refCounts[hash] = refcount;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
[TypeConverter(typeof(ExpandableObjectConverter))] public class CutsceneObject
|
|
{
|
|
public int ObjectID { get; set; }
|
|
public CutObject CutObject { get; set; }
|
|
public MetaHash Name { get; set; }
|
|
|
|
public Vector3 Position { get; set; }
|
|
public Quaternion Rotation { get; set; }
|
|
|
|
public Ped Ped { get; set; }
|
|
public YmapEntityDef Prop { get; set; }
|
|
public Vehicle Vehicle { get; set; }
|
|
public Weapon Weapon { get; set; }
|
|
public YmapEntityDef HideEntity { get; set; }
|
|
|
|
public MetaHash AnimHash { get; set; }
|
|
public ClipMapEntry AnimClip { get; set; }
|
|
|
|
public bool Enabled { get; set; } = false;
|
|
|
|
|
|
public void Init(CutObject obj, GameFileCache gfc)
|
|
{
|
|
CutObject = obj;
|
|
ObjectID = obj?.iObjectId ?? -1;
|
|
|
|
if (obj is CutNamedObject nobj)
|
|
{
|
|
Name = nobj.cName;
|
|
}
|
|
|
|
if (obj is CutAnimationManagerObject anim)
|
|
{
|
|
}
|
|
else if (obj is CutAssetManagerObject ass)
|
|
{
|
|
}
|
|
else if (obj is CutCameraObject cam)
|
|
{
|
|
}
|
|
else if (obj is CutPedModelObject ped)
|
|
{
|
|
InitPed(ped, gfc);
|
|
}
|
|
else if (obj is CutPropModelObject prop)
|
|
{
|
|
InitProp(prop, gfc);
|
|
}
|
|
else if (obj is CutVehicleModelObject veh)
|
|
{
|
|
InitVehicle(veh, gfc);
|
|
}
|
|
else if (obj is CutWeaponModelObject weap)
|
|
{
|
|
InitWeapon(weap, gfc);
|
|
}
|
|
else if (obj is CutHiddenModelObject hid)
|
|
{
|
|
InitHiddenModel(hid, gfc);
|
|
}
|
|
else if (obj is CutFixupModelObject fix)
|
|
{
|
|
}
|
|
else if (obj is CutRayfireObject rayf)
|
|
{
|
|
}
|
|
else if (obj is CutParticleEffectObject eff)
|
|
{
|
|
}
|
|
else if (obj is CutAnimatedParticleEffectObject aeff)
|
|
{
|
|
}
|
|
else if (obj is CutLightObject light)
|
|
{
|
|
}
|
|
else if (obj is CutAnimatedLightObject alight)
|
|
{
|
|
}
|
|
else if (obj is CutDecalObject dec)
|
|
{
|
|
}
|
|
else if (obj is CutOverlayObject ovr)
|
|
{
|
|
}
|
|
else if (obj is CutAudioObject aud)
|
|
{
|
|
}
|
|
else if (obj is CutSubtitleObject sub)
|
|
{
|
|
}
|
|
else if (obj is CutBlockingBoundsObject blk)
|
|
{
|
|
}
|
|
else if (obj is CutScreenFadeObject fad)
|
|
{
|
|
}
|
|
else
|
|
{ }
|
|
}
|
|
|
|
private void InitPed(CutPedModelObject ped, GameFileCache gfc)
|
|
{
|
|
|
|
Ped = new Ped();
|
|
Ped.Init(ped.StreamingName, gfc);
|
|
Ped.LoadDefaultComponents(gfc);
|
|
|
|
if (ped.StreamingName == JenkHash.GenHash("player_zero"))
|
|
{
|
|
//for michael, switch his outfit so it's not glitching everywhere (until it's fixed?)
|
|
Ped.SetComponentDrawable(3, 27, 0, 0, gfc);
|
|
Ped.SetComponentDrawable(4, 19, 0, 0, gfc);
|
|
Ped.SetComponentDrawable(6, null, null, gfc);
|
|
}
|
|
|
|
AnimHash = ped.StreamingName;
|
|
}
|
|
|
|
private void InitProp(CutPropModelObject prop, GameFileCache gfc)
|
|
{
|
|
|
|
Prop = new YmapEntityDef();
|
|
Prop.SetArchetype(gfc.GetArchetype(prop.StreamingName));
|
|
|
|
AnimHash = prop.StreamingName;
|
|
}
|
|
|
|
private void InitVehicle(CutVehicleModelObject veh, GameFileCache gfc)
|
|
{
|
|
var name = veh.StreamingName.ToString();
|
|
|
|
Vehicle = new Vehicle();
|
|
Vehicle.Init(name, gfc);
|
|
|
|
AnimHash = veh.StreamingName;
|
|
}
|
|
|
|
private void InitWeapon(CutWeaponModelObject weap, GameFileCache gfc)
|
|
{
|
|
var name = weap.StreamingName.ToString();
|
|
|
|
Weapon = new Weapon();
|
|
Weapon.Init(name, gfc);
|
|
|
|
AnimHash = weap.StreamingName;
|
|
}
|
|
|
|
private void InitHiddenModel(CutHiddenModelObject hid, GameFileCache gfc)
|
|
{
|
|
|
|
HideEntity = new YmapEntityDef();
|
|
HideEntity._CEntityDef.archetypeName = hid.cName;
|
|
HideEntity.SetPosition(hid.vPosition);
|
|
|
|
}
|
|
|
|
|
|
public override string ToString()
|
|
{
|
|
return CutObject?.ToString() ?? (ObjectID.ToString() + ": " + Name.ToString());
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|