Skip to content

Commit

Permalink
[KK,KKS] Make ext data properly propagate in story mode
Browse files Browse the repository at this point in the history
  • Loading branch information
ManlyMarco committed Dec 4, 2021
1 parent afafe58 commit dc09eed
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 70 deletions.
173 changes: 111 additions & 62 deletions src/Shared.Core/Chara/CharaCustomFunctionController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,48 +90,48 @@ public void SetExtendedData(PluginData data)
if (ExtendedDataId == null) throw new ArgumentException(nameof(ExtendedDataId));
ExtendedSave.SetExtendedDataById(ChaFileControl, ExtendedDataId, data);

#if KK //todo || KKS
// Needed for propagating changes back to the original charFile since they don't get copied back.
var heroine = ChaControl.GetHeroine();
if (heroine != null)
#if KK || KKS
if (KoikatuAPI.GetCurrentGameMode() == GameMode.MainGame)
{
ExtendedSave.SetExtendedDataById(heroine.charFile, ExtendedDataId, data);

if (ChaControl != heroine.chaCtrl)
// In main game store ext data for the character inside of the main chaFile object (the one that gets saved to game saves).
// This allows saving ext data inside talk scenes and H scenes without losing it after exiting to main map.
var heroine = ChaControl.GetHeroine();
if (heroine != null)
{
ExtendedSave.SetExtendedDataById(heroine.chaCtrl.chaFile, ExtendedDataId, data);

// Update other instance to reflect the new ext data
var other = heroine.chaCtrl.GetComponent(GetType()) as CharaCustomFunctionController;
if (other != null) other.OnReloadInternal(KoikatuAPI.GetCurrentGameMode());
}

var npc = heroine.GetNPC();
if (npc != null && npc.chaCtrl != null && npc.chaCtrl != ChaControl)
{
ExtendedSave.SetExtendedDataById(npc.chaCtrl.chaFile, ExtendedDataId, data);

// Update other instance to reflect the new ext data
var other = npc.chaCtrl.GetComponent(GetType()) as CharaCustomFunctionController;
if (other != null) other.OnReloadInternal(KoikatuAPI.GetCurrentGameMode());
ExtendedSave.SetExtendedDataById(heroine.charFile, ExtendedDataId, data);

if (ChaControl != heroine.chaCtrl)
{
ExtendedSave.SetExtendedDataById(heroine.chaCtrl.chaFile, ExtendedDataId, data);
// Update other instance to reflect the new ext data
CharacterApi.Hooks.SetDirty(heroine, true);

}

var npc = heroine.GetNPC();
if (npc != null && npc.chaCtrl != null && npc.chaCtrl != ChaControl && npc.chaCtrl != heroine.chaCtrl)
{
ExtendedSave.SetExtendedDataById(npc.chaCtrl.chaFile, ExtendedDataId, data);
// Update other instance to reflect the new ext data
CharacterApi.Hooks.SetDirty(heroine, true);
}
}
}

var player = ChaControl.GetPlayer();
if (player != null)
{
ExtendedSave.SetExtendedDataById(player.charFile, ExtendedDataId, data);

if (ChaControl != player.chaCtrl)
else
{
ExtendedSave.SetExtendedDataById(player.chaCtrl.chaFile, ExtendedDataId, data);

// Update other instance to reflect the new ext data
var other = player.chaCtrl.GetComponent(GetType()) as CharaCustomFunctionController;
if (other != null) other.OnReloadInternal(KoikatuAPI.GetCurrentGameMode());
var player = ChaControl.GetPlayer();
if (player != null)
{
ExtendedSave.SetExtendedDataById(player.charFile, ExtendedDataId, data);

if (ChaControl != player.chaCtrl)
{
ExtendedSave.SetExtendedDataById(player.chaCtrl.chaFile, ExtendedDataId, data);
// Update other instance to reflect the new ext data
CharacterApi.Hooks.SetDirty(player, true);
}
}
}
}

#endif
}

Expand Down Expand Up @@ -174,6 +174,7 @@ public void SetCoordinateExtendedData(ChaFileCoordinate coordinate, PluginData d
/// </summary>
protected abstract void OnCardBeingSaved(GameMode currentGameMode);

internal void OnCardBeingSavedInternal() => OnCardBeingSavedInternal(KoikatuAPI.GetCurrentGameMode());
internal void OnCardBeingSavedInternal(GameMode gamemode)
{
if (!_wasLoaded)
Expand Down Expand Up @@ -220,8 +221,13 @@ protected virtual void OnReload(GameMode currentGameMode, bool maintainState) {
/// <param name="currentGameMode">Game mode we are currently in</param>
protected virtual void OnReload(GameMode currentGameMode) { }

internal void OnReloadInternal() => OnReloadInternal(KoikatuAPI.GetCurrentGameMode());
internal void OnReloadInternal(GameMode currentGameMode)
{
#if KK || KKS
if (currentGameMode == GameMode.MainGame)
CharacterApi.Hooks.SetDirty(ChaControl.GetHeroine(), false);
#endif
try
{
if (!ControllerRegistration.MaintainState)
Expand All @@ -237,15 +243,15 @@ internal void OnReloadInternal(GameMode currentGameMode)
}
}

// issue with stopallcoroutines
// todo hook startcoroutine and check if active, if not then show stack trace
///// <summary>
///// Hides base StartCoroutine and runs it on the plugin instance
///// </summary>
//public new Coroutine StartCoroutine(IEnumerator routine)
//{
// return KKAPI.KoikatuAPI.Instance.StartCoroutine(routine);
//}
// issue with stopallcoroutines
// todo hook startcoroutine and check if active, if not then show stack trace
///// <summary>
///// Hides base StartCoroutine and runs it on the plugin instance
///// </summary>
//public new Coroutine StartCoroutine(IEnumerator routine)
//{
// return KKAPI.KoikatuAPI.Instance.StartCoroutine(routine);
//}

/// <summary>
/// Fired just before current coordinate is saved to a coordinate card. Use <see cref="SetCoordinateExtendedData"/> to save data to it.
Expand Down Expand Up @@ -367,10 +373,11 @@ protected virtual void Start()
OnReloadInternal(KoikatuAPI.GetCurrentGameMode());
}

#region New ExtData //todo backpropagating in KK story mode, copy whole data blocks?
#region New ExtData

private ChaFileCoordinate GetCoordinate(int coordinateId)
{
// Get coord from the current ChaControl since it can be temporarily changed and would mess up ext data of clothes inside heroine.chaCtrl if we saved there
KoikatuAPI.Assert(ChaControl.nowCoordinate != null, "ChaControl.nowCoordinate != null");
#if KK || KKS
return (coordinateId < 0 ? ChaControl.nowCoordinate : ChaFileControl.coordinate[coordinateId]);
Expand All @@ -380,6 +387,33 @@ private ChaFileCoordinate GetCoordinate(int coordinateId)
#endif
}

private ChaFile GetExtDataTargetChaFile(bool setDirty)
{
#if KK || KKS
// In main game store ext data for the character inside of the main chaFile object (the one that gets saved to game saves) instead of its copies.
// This allows saving ext data inside talk scenes and H scenes without losing it after exiting to main map.
if (KoikatuAPI.GetCurrentGameMode() == GameMode.MainGame)
{
var heroine = ChaControl.GetHeroine();
if (heroine != null)
{
if (setDirty)
CharacterApi.Hooks.SetDirty(heroine, true);
return heroine.charFile;
}

var player = ChaControl.GetPlayer();
if (player != null)
{
if (setDirty)
CharacterApi.Hooks.SetDirty(player, true);
return player.charFile;
}
}
#endif
return ChaFileControl;
}

/// <summary>
/// Get extended data for specific clothes.
/// Do not store this data because it might change without notice, for example when clothing is copied. Always call Get at the point where you need the data, not earlier.
Expand Down Expand Up @@ -427,9 +461,10 @@ public PluginData GetAccessoryExtData(int accessoryPartId, int coordinateId = -1
/// </summary>
public PluginData GetBodyExtData()
{
KoikatuAPI.Assert(ChaFileControl.custom != null, "ChaFileControl.custom != null");
KoikatuAPI.Assert(ChaFileControl.custom.body != null, "ChaFileControl.custom.body != null");
ChaFileControl.custom.body.TryGetExtendedDataById(ExtendedDataId, out var data);
var chafile = GetExtDataTargetChaFile(false);
KoikatuAPI.Assert(chafile.custom != null, "chafile.custom != null");
KoikatuAPI.Assert(chafile.custom.body != null, "chafile.custom.body != null");
chafile.custom.body.TryGetExtendedDataById(ExtendedDataId, out var data);
return data;
}
/// <summary>
Expand All @@ -442,9 +477,10 @@ public PluginData GetBodyExtData()
/// </summary>
public PluginData GetFaceExtData()
{
KoikatuAPI.Assert(ChaFileControl.custom != null, "ChaFileControl.custom != null");
KoikatuAPI.Assert(ChaFileControl.custom.face != null, "ChaFileControl.custom.face != null");
ChaFileControl.custom.face.TryGetExtendedDataById(ExtendedDataId, out var data);
var chafile = GetExtDataTargetChaFile(false);
KoikatuAPI.Assert(chafile.custom != null, "chafile.custom != null");
KoikatuAPI.Assert(chafile.custom.face != null, "chafile.custom.face != null");
chafile.custom.face.TryGetExtendedDataById(ExtendedDataId, out var data);
return data;
}
/// <summary>
Expand All @@ -457,8 +493,9 @@ public PluginData GetFaceExtData()
/// </summary>
public PluginData GetParameterExtData()
{
KoikatuAPI.Assert(ChaFileControl.parameter != null, "ChaFileControl.parameter != null");
ChaFileControl.parameter.TryGetExtendedDataById(ExtendedDataId, out var data);
var chafile = GetExtDataTargetChaFile(false);
KoikatuAPI.Assert(chafile.parameter != null, "chafile.parameter != null");
chafile.parameter.TryGetExtendedDataById(ExtendedDataId, out var data);
return data;
}

Expand Down Expand Up @@ -502,9 +539,13 @@ public void SetAccessoryExtData(PluginData data, int accessoryPartId, int coordi
/// <param name="data">Extended data to save.</param>
public void SetBodyExtData(PluginData data)
{
KoikatuAPI.Assert(ChaFileControl.custom != null, "ChaFileControl.custom != null");
KoikatuAPI.Assert(ChaFileControl.custom.body != null, "ChaFileControl.custom.body != null");
ChaFileControl.custom.body.SetExtendedDataById(ExtendedDataId, data);
var chafile = GetExtDataTargetChaFile(true);
KoikatuAPI.Assert(chafile.custom != null, "chafile.custom != null");
KoikatuAPI.Assert(chafile.custom.body != null, "chafile.custom.body != null");
chafile.custom.body.SetExtendedDataById(ExtendedDataId, data);
// Save both to the main chafile and to the current instance in case it gets saved by something
if (chafile != ChaFileControl)
ChaFileControl.custom.body.SetExtendedDataById(ExtendedDataId, data);
}
/// <summary>
/// Set extended data for character's face (face sliders, eye settings).
Expand All @@ -514,9 +555,13 @@ public void SetBodyExtData(PluginData data)
/// <param name="data">Extended data to save.</param>
public void SetFaceExtData(PluginData data)
{
KoikatuAPI.Assert(ChaFileControl.custom != null, "ChaFileControl.custom != null");
KoikatuAPI.Assert(ChaFileControl.custom.face != null, "ChaFileControl.custom.face != null");
ChaFileControl.custom.face.SetExtendedDataById(ExtendedDataId, data);
var chafile = GetExtDataTargetChaFile(true);
KoikatuAPI.Assert(chafile.custom != null, "chafile.custom != null");
KoikatuAPI.Assert(chafile.custom.face != null, "chafile.custom.face != null");
chafile.custom.face.SetExtendedDataById(ExtendedDataId, data);
// Save both to the main chafile and to the current instance in case it gets saved by something
if (chafile != ChaFileControl)
ChaFileControl.custom.face.SetExtendedDataById(ExtendedDataId, data);
}
/// <summary>
/// Set extended data for character's parameters (personality, preferences, traits).
Expand All @@ -526,8 +571,12 @@ public void SetFaceExtData(PluginData data)
/// <param name="data">Extended data to save.</param>
public void SetParameterExtData(PluginData data)
{
KoikatuAPI.Assert(ChaFileControl.parameter != null, "ChaFileControl.parameter != null");
ChaFileControl.parameter.SetExtendedDataById(ExtendedDataId, data);
var chafile = GetExtDataTargetChaFile(true);
KoikatuAPI.Assert(chafile.parameter != null, "chafile.parameter != null");
chafile.parameter.SetExtendedDataById(ExtendedDataId, data);
// Save both to the main chafile and to the current instance in case it gets saved by something
if (chafile != ChaFileControl)
ChaFileControl.parameter.SetExtendedDataById(ExtendedDataId, data);
}

#endregion
Expand Down
2 changes: 1 addition & 1 deletion src/Shared.Core/Chara/CharacterApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ internal static void Init()
};

if (KoikatuAPI.EnableDebugLogging)
RegisterExtraBehaviour<TestCharaCustomFunctionController>(null);
RegisterExtraBehaviour<TestCharaCustomFunctionController>(nameof(TestCharaCustomFunctionController));
}

private static void CreateOrAddBehaviours(ChaControl target)
Expand Down
49 changes: 44 additions & 5 deletions src/Shared.Core/Chara/TestCharaCustomFunctionController.cs
Original file line number Diff line number Diff line change
@@ -1,29 +1,68 @@
#if AI || HS2
using System;
using System.Collections.Generic;
using ExtensibleSaveFormat;
#if AI || HS2
using AIChara;
#endif

namespace KKAPI.Chara
{
internal sealed class TestCharaCustomFunctionController : CharaCustomFunctionController
{
public string id;
public static int No;
protected override void OnCardBeingSaved(GameMode currentGameMode)
{
KoikatuAPI.Logger.LogWarning("CharaController - OnCardBeingSaved - currentGameMode:" + currentGameMode);
SetParameterExtData(new PluginData() { data = new Dictionary<string, object> { { "id", id } } });
SetBodyExtData(new PluginData() { data = new Dictionary<string, object> { { "id", id } } });
SetFaceExtData(new PluginData() { data = new Dictionary<string, object> { { "id", id } } });
SetExtendedData(new PluginData() { data = new Dictionary<string, object> { { "id", id } } });
// todo test if ext data gets carried over when saving inside talk scene and h
Console.WriteLine($"Save ID to ext data - {id} | Chara - {ChaControl.name}");

KoikatuAPI.Logger.LogWarning($"event:OnCardBeingSaved chara:{ChaControl.name} currentGameMode:{currentGameMode}");
}

protected override void OnReload(GameMode currentGameMode, bool maintainState)
{
KoikatuAPI.Logger.LogWarning($"CharaController - OnReload - currentGameMode:{currentGameMode}; maintainState:{maintainState}");
var a = GetExtendedData();
var b = GetBodyExtData();
var c = GetParameterExtData();
var d = GetFaceExtData();
KoikatuAPI.Assert((a == null && b == null && c == null && d == null) || (a != null && b != null && c != null && d != null), "ext data not lining up");
if (a != null)
{
KoikatuAPI.Assert(b != null, "b != null");
KoikatuAPI.Assert(c != null, "c != null");
KoikatuAPI.Assert(d != null, "d != null");
var newId = a.data["id"] as string;
KoikatuAPI.Assert(newId == b?.data["id"] as string, "a.data[\"id\"] == b.data[\"id\"]");
KoikatuAPI.Assert(newId == c?.data["id"] as string, "a.data[\"id\"] == c.data[\"id\"]");
KoikatuAPI.Assert(newId == d?.data["id"] as string, "a.data[\"id\"] == d.data[\"id\"]");
Console.WriteLine($"ID get from ext data - {newId} | Old ID - {id} | Chara - {ChaControl.name}");
id = newId;
}
if (id == null)
{
id = $"{No++} - {SceneApi.GetLoadSceneName()} - {SceneApi.GetAddSceneName()}";
Console.WriteLine($"New ID assigned - {id} | Chara - {ChaControl.name} | {ChaFileControl.parameter.fullname}");
SetParameterExtData(new PluginData() { data = new Dictionary<string, object> { { "id", id } } });
SetBodyExtData(new PluginData() { data = new Dictionary<string, object> { { "id", id } } });
SetFaceExtData(new PluginData() { data = new Dictionary<string, object> { { "id", id } } });
SetExtendedData(new PluginData() { data = new Dictionary<string, object> { { "id", id } } });
}

KoikatuAPI.Logger.LogWarning($"event:OnReload chara:{ChaControl.name} currentGameMode:{currentGameMode} maintainState:{maintainState}");
}

protected override void OnCoordinateBeingLoaded(ChaFileCoordinate coordinate, bool maintainState)
{
KoikatuAPI.Logger.LogWarning($"CharaController - OnCoordinateBeingLoaded - coordinate:{coordinate?.coordinateFileName}; maintainState:{maintainState}");
KoikatuAPI.Logger.LogWarning($"event:OnCoordinateBeingLoaded chara:{ChaControl.name} coordinate:{coordinate?.coordinateFileName}; maintainState:{maintainState}");
}

protected override void OnCoordinateBeingSaved(ChaFileCoordinate coordinate)
{
KoikatuAPI.Logger.LogWarning($"CharaController - OnCoordinateBeingSaved - coordinate:{coordinate?.coordinateFileName}");
KoikatuAPI.Logger.LogWarning($"event:OnCoordinateBeingSaved chara:{ChaControl.name} coordinate:{coordinate?.coordinateFileName}");
}
}
}
7 changes: 7 additions & 0 deletions src/Shared.Core/KoikatuAPIBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ public partial class KoikatuAPI
[System.ComponentModel.Browsable(false)]
public static bool EnableDebugLogging
{
#if DEBUG
get => true;
#else
get => EnableDebugLoggingSetting.Value;
#endif
set => EnableDebugLoggingSetting.Value = value;
}

Expand Down Expand Up @@ -230,6 +234,9 @@ internal static void Assert(bool success, string error)
//todo uncomment? or keep commented for easier future debugging?
//if (!EnableDebugLogging) return;
Logger.LogWarning("Assertion failed: " + error + "\nat: " + new System.Diagnostics.StackTrace(1));
#if DEBUG
System.Diagnostics.Debugger.Break();
#endif
}

internal void OnQuitting(EventArgs e)
Expand Down
Loading

0 comments on commit dc09eed

Please sign in to comment.