private static void Postfix(LevelUpState state, UnitDescriptor unit) { if (!settings.toggleMulticlass) { return; } if (!unit.IsPartyOrPet()) { return; } //if (Mod.IsCharGen()) Main.Log($"stack: {System.Environment.StackTrace}"); if (IsAvailable()) { Main.multiclassMod.AppliedMulticlassSet.Clear(); Main.multiclassMod.UpdatedProgressions.Clear(); // Some companions have predefined levels so in some cases we get called iteratively for each level so we will make sure we only apply multiclass on the last level if (unit.TryGetPartyMemberForLevelUpVersion(out var ch) && ch.TryGetClass(state.SelectedClass, out var cl) && unit != ch.Descriptor && state.NextClassLevel <= cl.Level ) { Mod.Debug($"SelectClass_Apply_Patch, unit: {unit.CharacterName.orange()} isCH: {unit == ch.Descriptor}) - skip - lvl:{state.NextClassLevel} vs {cl.Level} ".green()); return; } // get multi-class setting var useDefaultMulticlassOptions = state.IsCharGen(); var options = MulticlassOptions.Get(useDefaultMulticlassOptions ? null : unit); Mod.Trace($"SelectClass_Apply_Patch, unit: {unit.CharacterName.orange()} useDefaultMulticlassOptions: {useDefaultMulticlassOptions} isCharGen: {state.IsCharGen()} is1stLvl: {state.IsFirstCharacterLevel} isPHChar: {unit.CharacterName == "Player Character"} level: {state.NextClassLevel.ToString().yellow()}".cyan().bold()); if (options == null || options.Count == 0) { return; } Mod.Trace($" selected options: {options}".orange()); //selectedMulticlassSet.ForEach(cl => Main.Log($" {cl}")); // applying classes var selectedClass = state.SelectedClass; StateReplacer stateReplacer = new(state); foreach (var characterClass in Main.multiclassMod.AllClasses) { if (characterClass.IsMythic != selectedClass.IsMythic) { continue; } if (Main.multiclassMod.AppliedMulticlassSet.Contains(characterClass)) { Mod.Warn($"SelectClass_Apply_Patch - duplicate application of multiclass detected: {characterClass.name.yellow()}"); continue; } if (options.Contains(characterClass)) { Mod.Trace($" checking {characterClass.HashKey()} {characterClass.GetDisplayName()} "); } if (characterClass != stateReplacer.SelectedClass && characterClass.IsMythic == state.IsMythicClassSelected && options.Contains(characterClass) ) { stateReplacer.Replace(null, 0); // TODO - figure out and document what this is doing Mod.Trace($" {characterClass.Name} matches".cyan()); //stateReplacer.Replace(characterClass, unit.Progression.GetClassLevel(characterClass)); if (new SelectClass(characterClass).Check(state, unit)) { Mod.Trace($" - {nameof(SelectClass)}.{nameof(SelectClass.Apply)}*({characterClass}, {unit})".cyan()); unit.Progression.AddClassLevel_NotCharacterLevel(characterClass); //state.NextClassLevel = unit.Progression.GetClassLevel(characterClass); //state.SelectedClass = characterClass; characterClass.RestrictPrerequisites(unit, state); //EventBus.RaiseEvent<ILevelUpSelectClassHandler>(h => h.HandleSelectClass(unit, state)); Main.multiclassMod.AppliedMulticlassSet.Add(characterClass); } } } stateReplacer.Restore(); Mod.Trace($" checking archetypes for {unit.CharacterName}".cyan()); // applying archetypes ForEachAppliedMulticlass(state, unit, () => { Mod.Trace($" {state.SelectedClass.HashKey()} SelectClass-ForEachApplied".cyan().bold()); var selectedClass = state.SelectedClass; var archetypeOptions = options.ArchetypeOptions(selectedClass); foreach (var archetype in state.SelectedClass.Archetypes) { // here is where we need to start supporting multiple archetypes of the same class if (archetypeOptions.Contains(archetype)) { Mod.Trace($" adding archetype: ${archetype.Name}".cyan().bold()); AddArchetype addArchetype = new(state.SelectedClass, archetype); unit.SetClassIsGestalt(addArchetype.CharacterClass, true); if (addArchetype.Check(state, unit)) { addArchetype.Apply(state, unit); } } }