Skip to content

Commit e4ca249

Browse files
Merge pull request #575 from FFXIV-CombatReborn/Vulcan
Raph/Language Fixes
2 parents 17a6d02 + 3ed1c96 commit e4ca249

File tree

6 files changed

+261
-94
lines changed

6 files changed

+261
-94
lines changed

GatherBuddy/Crafting/CraftingGameInterop.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,8 +1071,17 @@ private static CraftState TransitionFromInProgress()
10711071
if (Dalamud.Conditions[ConditionFlag.ExecutingCraftingAction])
10721072
return CraftState.WaitAction;
10731073

1074-
if (_nextActionAllowedAt != DateTime.MinValue && DateTime.Now < _nextActionAllowedAt)
1074+
if (_nextActionAllowedAt != DateTime.MinValue)
1075+
{
1076+
if (DateTime.Now < _nextActionAllowedAt)
1077+
return CraftState.InProgress;
1078+
1079+
_nextActionAllowedAt = DateTime.MinValue;
1080+
var cachedRecommendation = CraftingProcessor.NextRecommendation;
1081+
if (cachedRecommendation.Action != VulcanSkill.None && _vulcanCraftState != null && _vulcanStepState != null)
1082+
ExecuteSolverRecommendation(_vulcanCraftState, _vulcanStepState, cachedRecommendation);
10751083
return CraftState.InProgress;
1084+
}
10761085

10771086
if (_vulcanCraftState != null && _vulcanStepState != null)
10781087
{
@@ -1110,13 +1119,12 @@ private static CraftState TransitionFromInProgress()
11101119
if (recommendation.Action != VulcanSkill.None)
11111120
{
11121121
var delayMs = GatherBuddy.Config.VulcanExecutionDelayMs;
1113-
if (delayMs > 0 && _nextActionAllowedAt == DateTime.MinValue)
1122+
if (delayMs > 0)
11141123
{
11151124
GatherBuddy.Log.Debug($"[Crafting] Delaying next action by {delayMs}ms");
11161125
_nextActionAllowedAt = DateTime.Now.AddMilliseconds(delayMs);
11171126
return CraftState.InProgress;
11181127
}
1119-
_nextActionAllowedAt = DateTime.MinValue;
11201128
ExecuteSolverRecommendation(_vulcanCraftState, _vulcanStepState, recommendation);
11211129
}
11221130
}

GatherBuddy/Crafting/CraftingListManager.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using System.Text;
45
using Newtonsoft.Json;
56

67
namespace GatherBuddy.Crafting;
@@ -149,4 +150,71 @@ public void Reload()
149150
{
150151
Load();
151152
}
153+
154+
public string? ExportList(int id)
155+
{
156+
var list = GetListByID(id);
157+
if (list == null) return null;
158+
159+
var copy = JsonConvert.DeserializeObject<CraftingListDefinition>(JsonConvert.SerializeObject(list))!;
160+
copy.ID = 0;
161+
copy.CreatedAt = DateTime.MinValue;
162+
copy.ExpandedList.Clear();
163+
copy.DefaultPrecraftMacroId = null;
164+
copy.DefaultFinalMacroId = null;
165+
166+
foreach (var item in copy.Recipes)
167+
{
168+
if (item.CraftSettings != null)
169+
{
170+
item.CraftSettings.SelectedMacroId = null;
171+
item.CraftSettings.MacroMode = MacroOverrideMode.Inherit;
172+
}
173+
}
174+
175+
foreach (var settings in copy.PrecraftCraftSettings.Values)
176+
{
177+
settings.SelectedMacroId = null;
178+
settings.MacroMode = MacroOverrideMode.Inherit;
179+
}
180+
181+
var json = JsonConvert.SerializeObject(copy, Formatting.None);
182+
GatherBuddy.Log.Information($"[CraftingListManager] Exported list '{list.Name}'");
183+
return Convert.ToBase64String(Encoding.UTF8.GetBytes(json));
184+
}
185+
186+
public (CraftingListDefinition? List, string? Error) ImportList(string base64)
187+
{
188+
try
189+
{
190+
var json = Encoding.UTF8.GetString(Convert.FromBase64String(base64.Trim()));
191+
var source = JsonConvert.DeserializeObject<CraftingListDefinition>(json);
192+
if (source == null)
193+
return (null, "Failed to deserialize list data.");
194+
if (source.Recipes.Count == 0)
195+
return (null, "The exported list contains no recipes.");
196+
197+
var name = string.IsNullOrWhiteSpace(source.Name) ? "Imported List" : source.Name;
198+
var newList = CreateNewList(name);
199+
newList.Recipes = source.Recipes;
200+
newList.Consumables = source.Consumables;
201+
newList.PrecraftOptions = source.PrecraftOptions;
202+
newList.PrecraftCraftSettings = source.PrecraftCraftSettings;
203+
newList.SkipIfEnough = source.SkipIfEnough;
204+
newList.QuickSynthAll = source.QuickSynthAll;
205+
Save();
206+
207+
GatherBuddy.Log.Information($"[CraftingListManager] Imported list '{newList.Name}' with {newList.Recipes.Count} recipes");
208+
return (newList, null);
209+
}
210+
catch (FormatException)
211+
{
212+
return (null, "Clipboard text is not valid base64. Make sure you copied the full export string.");
213+
}
214+
catch (Exception ex)
215+
{
216+
GatherBuddy.Log.Error($"[CraftingListManager] Import failed: {ex.Message}");
217+
return (null, $"Import failed: {ex.Message}");
218+
}
219+
}
152220
}

GatherBuddy/Crafting/CraftingQueueProcessor.cs

Lines changed: 26 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ public enum QueueState
4040
private int _currentProcessedRecipeTotal = 0;
4141
private DateTime _craftHangSince = DateTime.MinValue;
4242
private bool _lastCraftWasQuickSynth = false;
43+
private Dictionary<uint, RaphaelSolveRequest> _enqueuedRaphaelRequests = new();
44+
private uint _jobSwitchRequestedFor = 0u;
4345

4446
public QueueState CurrentState => _currentState;
4547
public int CurrentQueueIndex => _currentQueueIndex;
@@ -71,6 +73,8 @@ public void StartQueue(List<CraftingListItem> queue, CraftingListConsumableSetti
7173
_raphaelCoordinator = raphaelCoordinator;
7274
_listConsumables = listConsumables;
7375
_consumableDelayUntil = DateTime.MinValue;
76+
_enqueuedRaphaelRequests.Clear();
77+
_jobSwitchRequestedFor = 0u;
7478
GatherBuddy.Log.Information($"[CraftingQueueProcessor] Starting queue with {_queue.Count} recipes");
7579
GatherBuddy.Log.Debug($"[CraftingQueueProcessor] RaphaelCoordinator is {(raphaelCoordinator != null ? "present" : "null")}");
7680
StateChanged?.Invoke(_currentState);
@@ -201,7 +205,7 @@ private unsafe void UpdateJobSwitch()
201205

202206
if (currentJob != requiredJob)
203207
{
204-
if (_tasks.Count == 0)
208+
if (_tasks.Count == 0 && _jobSwitchRequestedFor != requiredJob)
205209
{
206210
GatherBuddy.Log.Information($"[CraftingQueueProcessor] Job switch needed: {requiredJob}");
207211
bool needExitCraft = CraftingGameInterop.CurrentState == CraftingGameInterop.CraftState.IdleBetween;
@@ -213,16 +217,12 @@ private unsafe void UpdateJobSwitch()
213217
}
214218

215219
_tasks.Add(() => { SwitchJob(requiredJob); return CraftingTasks.TaskResult.Done; });
216-
}
217-
218-
if (_tasks.Count == 0)
219-
{
220-
GatherBuddy.Log.Debug($"[CraftingQueueProcessor] Job switch complete");
221-
TransitionToRaphaelOrCraft();
220+
_jobSwitchRequestedFor = requiredJob;
222221
}
223222
}
224223
else
225224
{
225+
_jobSwitchRequestedFor = 0u;
226226
TransitionToRaphaelOrCraft();
227227
}
228228
}
@@ -270,6 +270,20 @@ private void TransitionToRaphaelOrCraft()
270270
}
271271

272272
var recipeItem = _queue[_currentQueueIndex];
273+
if (_enqueuedRaphaelRequests.TryGetValue(recipeItem.RecipeId, out var enqueuedRequest))
274+
{
275+
var currentRequest = BuildRaphaelRequestForRecipe(recipeItem.RecipeId);
276+
if (currentRequest != null && enqueuedRequest.GetKey() != currentRequest.GetKey())
277+
{
278+
GatherBuddy.Log.Warning($"[CraftingQueueProcessor] Stats mismatch for recipe {recipeItem.RecipeId} — saved gearset may be outdated.");
279+
GatherBuddy.Log.Warning($"[CraftingQueueProcessor] Enqueued key: {enqueuedRequest.GetKey()}, Current key: {currentRequest.GetKey()}");
280+
_enqueuedRaphaelRequests[recipeItem.RecipeId] = currentRequest;
281+
_raphaelCoordinator!.EnqueueSolvesFromRequests(new[] { currentRequest });
282+
_currentState = QueueState.WaitingForRaphaelSolution;
283+
StateChanged?.Invoke(_currentState);
284+
return;
285+
}
286+
}
273287
if (IsRaphaelSolutionReady(recipeItem.RecipeId))
274288
{
275289
GatherBuddy.Log.Debug($"[CraftingQueueProcessor] Raphael solution ready for recipe {recipeItem.RecipeId}");
@@ -316,35 +330,9 @@ private bool IsRaphaelSolutionReady(uint recipeId)
316330
if (_raphaelCoordinator == null)
317331
return false;
318332

319-
var recipe = RecipeManager.GetRecipe(recipeId);
320-
if (recipe == null)
321-
return false;
322-
323-
var requiredJob = (uint)(recipe.Value.CraftType.RowId + 8);
324-
var gearsetStats = GearsetStatsReader.ReadGearsetStatsForJob(requiredJob);
325-
if (gearsetStats == null)
333+
var request = BuildRaphaelRequestForRecipe(recipeId);
334+
if (request == null)
326335
return false;
327-
328-
var recipeItem = _queue.FirstOrDefault(r => r.RecipeId == recipeId);
329-
var consumableSettings = BuildConsumableSettings(recipeItem);
330-
if (consumableSettings != null)
331-
gearsetStats = GearsetStatsReader.ApplyConsumablesToStats(gearsetStats, consumableSettings);
332-
var ingredientPreferences = recipeItem?.IngredientPreferences;
333-
int initialQuality = ingredientPreferences != null && ingredientPreferences.Count > 0
334-
? QualityCalculator.CalculateInitialQuality(recipe.Value, ingredientPreferences)
335-
: 0;
336-
337-
var specialist = GatherBuddy.Config.RaphaelSolverConfig.RaphaelAllowSpecialistActions && gearsetStats.Specialist;
338-
var request = new RaphaelSolveRequest(
339-
RecipeId: recipeId,
340-
Level: gearsetStats.Level,
341-
Craftsmanship: gearsetStats.Craftsmanship,
342-
Control: gearsetStats.Control,
343-
CP: gearsetStats.CP,
344-
Manipulation: gearsetStats.Manipulation,
345-
Specialist: specialist,
346-
InitialQuality: initialQuality
347-
);
348336

349337
return _raphaelCoordinator.TryGetSolution(request, out var solution) && solution != null && !solution.IsFailed;
350338
}
@@ -801,6 +789,7 @@ private unsafe void EnqueueRaphaelSolvesFromCraftStates(List<CraftingListItem> q
801789
);
802790

803791
requests.Add(request);
792+
_enqueuedRaphaelRequests.TryAdd(recipe.RowId, request);
804793
}
805794
catch (Exception ex)
806795
{
@@ -1129,6 +1118,8 @@ public void Reset()
11291118
_currentProcessedRecipeCount = 0;
11301119
_currentProcessedRecipeTotal = 0;
11311120
_craftHangSince = DateTime.MinValue;
1121+
_enqueuedRaphaelRequests.Clear();
1122+
_jobSwitchRequestedFor = 0u;
11321123
}
11331124

11341125
public void TestRepair()
Lines changed: 62 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Linq;
43
using System.Text.RegularExpressions;
4+
using Dalamud.Game;
55
using GatherBuddy.Vulcan;
6+
using Lumina.Excel.Sheets;
67

78
namespace GatherBuddy.Crafting;
89

@@ -13,6 +14,9 @@ public class MacroParser
1314
RegexOptions.Compiled | RegexOptions.IgnoreCase
1415
);
1516

17+
private static Dictionary<string, VulcanSkill>? _localizedLookup;
18+
private static Dictionary<string, VulcanSkill>? _englishLookup;
19+
1620
public static UserMacro? ParseInGameMacro(string macroText, string name = "Imported Macro")
1721
{
1822
if (string.IsNullOrWhiteSpace(macroText))
@@ -35,7 +39,7 @@ public class MacroParser
3539
{
3640
var actionName = match.Groups[1].Success ? match.Groups[1].Value : match.Groups[2].Value;
3741
var actionId = GetActionIdFromName(actionName);
38-
42+
3943
if (actionId > 0)
4044
{
4145
actions.Add(actionId);
@@ -55,7 +59,7 @@ public class MacroParser
5559
}
5660

5761
GatherBuddy.Log.Information($"[MacroParser] Parsed {actions.Count} actions from macro");
58-
62+
5963
return new UserMacro
6064
{
6165
Name = name,
@@ -67,56 +71,66 @@ public class MacroParser
6771

6872
private static uint GetActionIdFromName(string actionName)
6973
{
70-
var normalizedName = actionName.Trim().ToLowerInvariant()
74+
var normalized = NormalizeName(actionName);
75+
76+
var localized = _localizedLookup ??= BuildLookup(GatherBuddy.Language);
77+
if (localized.TryGetValue(normalized, out var skill))
78+
return (uint)skill;
79+
80+
var english = _englishLookup ??= BuildLookup(ClientLanguage.English);
81+
if (english.TryGetValue(normalized, out skill))
82+
return (uint)skill;
83+
84+
return 0;
85+
}
86+
87+
private static string NormalizeName(string name)
88+
=> name.Trim().ToLowerInvariant()
7189
.Replace(" ", "")
7290
.Replace("'", "")
91+
.Replace("\u2019", "")
7392
.Replace("-", "");
7493

75-
var skill = normalizedName switch
94+
private static Dictionary<string, VulcanSkill> BuildLookup(ClientLanguage language)
95+
{
96+
var lookup = new Dictionary<string, VulcanSkill>();
97+
98+
foreach (VulcanSkill skill in Enum.GetValues<VulcanSkill>())
7699
{
77-
"basicsynthesis" => VulcanSkill.BasicSynthesis,
78-
"carefulsynthesis" => VulcanSkill.CarefulSynthesis,
79-
"rapidsynthesis" => VulcanSkill.RapidSynthesis,
80-
"groundwork" => VulcanSkill.Groundwork,
81-
"intensivesynthesis" => VulcanSkill.IntensiveSynthesis,
82-
"prudentsynthesis" => VulcanSkill.PrudentSynthesis,
83-
"musclememory" => VulcanSkill.MuscleMemory,
84-
85-
"basictouch" => VulcanSkill.BasicTouch,
86-
"standardtouch" => VulcanSkill.StandardTouch,
87-
"advancedtouch" => VulcanSkill.AdvancedTouch,
88-
"hastytouch" => VulcanSkill.HastyTouch,
89-
"preparatorytouch" => VulcanSkill.PreparatoryTouch,
90-
"precisetouch" => VulcanSkill.PreciseTouch,
91-
"prudenttouch" => VulcanSkill.PrudentTouch,
92-
"trainedfinesse" => VulcanSkill.TrainedFinesse,
93-
"reflect" => VulcanSkill.Reflect,
94-
"refinedtouch" => VulcanSkill.RefinedTouch,
95-
"daringtouch" => VulcanSkill.DaringTouch,
96-
97-
"byregotsblessing" => VulcanSkill.ByregotsBlessing,
98-
"trainedeye" => VulcanSkill.TrainedEye,
99-
"delicatesynthesis" => VulcanSkill.DelicateSynthesis,
100-
101-
"veneration" => VulcanSkill.Veneration,
102-
"innovation" => VulcanSkill.Innovation,
103-
"greatstrides" => VulcanSkill.GreatStrides,
104-
"tricksofthetrade" => VulcanSkill.TricksOfTrade,
105-
"mastersmend" => VulcanSkill.MastersMend,
106-
"manipulation" => VulcanSkill.Manipulation,
107-
"wastenot" => VulcanSkill.WasteNot,
108-
"wastenotii" => VulcanSkill.WasteNot2,
109-
"observe" => VulcanSkill.Observe,
110-
"carefulobservation" => VulcanSkill.CarefulObservation,
111-
"finalappraisal" => VulcanSkill.FinalAppraisal,
112-
"heartandsoul" => VulcanSkill.HeartAndSoul,
113-
"quickinnovation" => VulcanSkill.QuickInnovation,
114-
"immaculatemend" => VulcanSkill.ImmaculateMend,
115-
"trainedperfection" => VulcanSkill.TrainedPerfection,
116-
117-
_ => VulcanSkill.None
118-
};
100+
var id = (uint)skill;
101+
if (id < 3 || id >= 200000)
102+
continue;
103+
104+
try
105+
{
106+
string name;
107+
if (id >= 100000)
108+
{
109+
var sheet = Dalamud.GameData.GetExcelSheet<CraftAction>(language);
110+
if (sheet == null || !sheet.TryGetRow(id, out var row)) continue;
111+
name = row.Name.ToString().Trim();
112+
}
113+
else
114+
{
115+
var sheet = Dalamud.GameData.GetExcelSheet<Lumina.Excel.Sheets.Action>(language);
116+
if (sheet == null || !sheet.TryGetRow(id, out var row)) continue;
117+
name = row.Name.ToString().Trim();
118+
}
119+
120+
if (string.IsNullOrEmpty(name)) continue;
121+
122+
var normalized = NormalizeName(name);
123+
lookup.TryAdd(normalized, skill);
124+
125+
GatherBuddy.Log.Debug($"[MacroParser] Registered '{name}' -> {skill} ({language})");
126+
}
127+
catch (Exception ex)
128+
{
129+
GatherBuddy.Log.Debug($"[MacroParser] Failed to register skill {skill}: {ex.Message}");
130+
}
131+
}
119132

120-
return (uint)skill;
133+
GatherBuddy.Log.Information($"[MacroParser] Built {language} lookup with {lookup.Count} entries");
134+
return lookup;
121135
}
122136
}

GatherBuddy/Crafting/RaphaelSolveData.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public class RaphaelSolveCoordinatorConfig
5151
public bool RaphaelEnabled { get; set; } = true;
5252
public int MaxConcurrentRaphaelProcesses { get; set; } = 1;
5353
public int RaphaelTimeoutMinutes { get; set; } = 5;
54-
public bool RaphaelBackloadProgress { get; set; } = true;
54+
public bool RaphaelBackloadProgress { get; set; } = false;
5555
public bool RaphaelAllowSpecialistActions { get; set; } = false;
5656
public bool AutoClearSolutionCache { get; set; } = true;
5757
public int SolutionCacheMaxAgeDays { get; set; } = 30;

0 commit comments

Comments
 (0)