Skip to content

Commit

Permalink
Added stacking, arrows can now each have their own quality. Also fixe…
Browse files Browse the repository at this point in the history
…d many bugs and overall improvements.
  • Loading branch information
AnnanFay committed Oct 24, 2021
1 parent 7c1ed6f commit 5e75dcc
Show file tree
Hide file tree
Showing 7 changed files with 405 additions and 96 deletions.
5 changes: 4 additions & 1 deletion CraftingSkill/CraftingConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class CraftingConfig
{
private ConfigVariable<bool> __QuantisedQuality = new ConfigVariable<bool>("General", "QuantisedQuality", false);
private ConfigVariable<float> __StochasticVariance = new ConfigVariable<float>("General", "StochasticVariance", 0.0f);
private ConfigVariable<bool> __DebugTooltips = new ConfigVariable<bool>("General", "DebugTooltips", false);

// Experience for crafting
private ConfigVariable<float> __ExpScapeTier = new ConfigVariable<float>("ExpGain", "ExpScapeTier", 1.0f);
Expand Down Expand Up @@ -54,6 +55,7 @@ public class CraftingConfig
// Utility getters
public bool QuantisedQuality {get => __QuantisedQuality.Value; }
public float StochasticVariance {get => __StochasticVariance.Value; }
public bool DebugTooltips {get => __DebugTooltips.Value; }
public float ExpScapeTier {get => __ExpScapeTier.Value; }
public float ExpScapePower {get => __ExpScapePower.Value; }
public float ExpScapeLinear {get => __ExpScapeLinear.Value; }
Expand Down Expand Up @@ -98,7 +100,7 @@ public void InitConfig(string mod_id, ConfigFile config)
var serverConfigReceivedDelegateType = (Type)traverse.Type("ServerConfigReceivedDelegate").GetValue();
Type[] paramTypes = { typeof(string), typeof(ConfigFile), serverConfigReceivedDelegateType };
traverse.Method("RegisterMod", paramTypes).GetValue(mod_id, config, null);
} catch (Exception e) {
} catch (Exception) {
// registering mod failed, API may have changed
// pretend MCE doesn't exist
assembly = null;
Expand All @@ -111,6 +113,7 @@ public void InitConfig(string mod_id, ConfigFile config)

__QuantisedQuality.init(assembly, config, mod_id);
__StochasticVariance.init(assembly, config, mod_id);
__DebugTooltips.init(assembly, config, mod_id);
__ExpScapeTier.init(assembly, config, mod_id);
__ExpScapePower.init(assembly, config, mod_id);
__ExpScapeLinear.init(assembly, config, mod_id);
Expand Down
210 changes: 173 additions & 37 deletions CraftingSkill/CraftingSkill.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ public class CraftingSkillsPlugin : BaseUnityPlugin
{
public const String MOD_ID = "annanfay.mod.crafting_skill";
public const string MOD_NAME = "CraftingSkill";
public const string MOD_VERSION = "0.0.2";
public const string MOD_VERSION = "0.0.3";
public const int CRAFTING_SKILL_ID = 1605;

Harmony harmony;

private static CraftingConfig config = new CraftingConfig();
public static CraftingConfig config = new CraftingConfig();
private static Dictionary<string, Texture2D> cachedTextures = new Dictionary<string, Texture2D>();

public CraftingSkillsPlugin() {
Expand All @@ -43,7 +43,7 @@ void Awake()

SkillInjector.RegisterNewSkill(CRAFTING_SKILL_ID, "Crafting", "Craft higher quality items as you level", 1.0f, LoadIconTexture(), Skills.SkillType.Unarmed);

ExtendedItemData.LoadExtendedItemData += QualityComponent.OnNewExtendedItemData;
ExtendedItemData.LoadExtendedItemData += QualityComponent.OnLoadExtendedItemData;
ExtendedItemData.NewExtendedItemData += QualityComponent.OnNewExtendedItemData;
}

Expand Down Expand Up @@ -105,6 +105,45 @@ public static Stream GetManifestResourceStream(string filename)
return null;
}


public static void DropHeadFix(ItemDrop.ItemData item)
{
// if item stack is less than quality stack remove from front until they match
// this happens after using arrows, consuming food, etc.

QualityComponent comp = item.Extended()?.GetComponent<QualityComponent>();
// item will be deleted if required, so no need to handle stack 0 case
if (comp != null && item.m_stack > 0 && item.m_stack < comp.Quality.Quantity) {
int toRemove = comp.Quality.Quantity - item.m_stack;
if (config.DebugTooltips)
{
ZLog.Log("DropHeadFix APPLIED #" + item.Extended()?.GetUniqueId() + " | " + item.m_stack + " < " + comp.Quality.Quantity);
}
comp.Quality.Shift(toRemove);
comp.Save();
}
}

public static void DropTailFix(ItemDrop.ItemData item)
{
// called in handlers explicitly when new item stacks are created
// discards fron back of stack until counts match

QualityComponent comp = item.Extended()?.GetComponent<QualityComponent>();
if (comp == null) return;

if (config.DebugTooltips)
{
ZLog.Log("DropTailFix? #" + item.Extended()?.GetUniqueId() + " | " + item.m_stack + " < " + comp.Quality.Quantity);
}
// item will be deleted if required, so no need to handle stack 0 case
if (item.m_stack > 0 && item.m_stack < comp.Quality.Quantity) {
int toRemove = comp.Quality.Quantity - item.m_stack;
comp.Quality.Pop(toRemove);
}
comp.Save();
}

[HarmonyPatch(typeof(ItemDrop.ItemData))]
public static class ItemDataPatcher
{
Expand All @@ -127,7 +166,7 @@ static void GetTooltip(ItemDrop.ItemData item, int qualityLevel, bool crafting,
);
}
Recipe recipe = ObjectDB.instance.GetRecipe(item);
if (recipe != null) {
if (isCraftingRecipe(recipe)) {
__result += String.Format(
"\n{0}: <color=orange>{1}</color>",
CraftExperienceLabel,
Expand All @@ -145,18 +184,18 @@ public static float GetItemQualityScalingFactor(ItemDrop.ItemData item, float mi
return min + (max - min) * qualityComp.Quality.ScalingFactor(config);
}


// First override the simple methods which returns floats
// public float GetArmor(int quality){}
// public float GetWeight(){}
// public float GetMaxDurability(int quality){}
// public float GetBaseBlockPower(int quality){}
// public float GetDeflectionForce(int quality){}
// public float GetArmor(int quality){}
// public float GetWeight(){}
// public float GetMaxDurability(int quality){}
// public float GetBaseBlockPower(int quality){}
// public float GetDeflectionForce(int quality){}
// public float GetArmor(int quality){}
[HarmonyPostfix]
[HarmonyPatch("GetArmor", typeof(int))]
public static float GetArmor(float __result, ItemDrop.ItemData __instance)
{
DropHeadFix(__instance);
return __result * GetItemQualityScalingFactor(__instance, config.ArmorStart, config.ArmorStop);
}

Expand All @@ -173,6 +212,7 @@ public static float GetWeight(float __result, ItemDrop.ItemData __instance)
[HarmonyPatch("GetMaxDurability", typeof(int))]
public static float GetMaxDurability(float __result, ItemDrop.ItemData __instance)
{
DropHeadFix(__instance);
return __result * GetItemQualityScalingFactor(__instance, config.MaxDurabilityStart, config.MaxDurabilityStop);
}

Expand All @@ -181,6 +221,7 @@ public static float GetMaxDurability(float __result, ItemDrop.ItemData __instanc
[HarmonyPatch("GetBaseBlockPower", typeof(int))]
public static float GetBaseBlockPower(float __result, ItemDrop.ItemData __instance)
{
DropHeadFix(__instance);
return __result * GetItemQualityScalingFactor(__instance, config.BaseBlockPowerStart, config.BaseBlockPowerStop);
}

Expand All @@ -189,6 +230,7 @@ public static float GetBaseBlockPower(float __result, ItemDrop.ItemData __instan
[HarmonyPatch("GetDeflectionForce", typeof(int))]
public static float GetDeflectionForce(float __result, ItemDrop.ItemData __instance)
{
DropHeadFix(__instance);
return __result * GetItemQualityScalingFactor(__instance, config.DeflectionForceStart, config.DeflectionForceStop);
}

Expand All @@ -197,6 +239,7 @@ public static float GetDeflectionForce(float __result, ItemDrop.ItemData __insta
[HarmonyPatch("GetDamage", typeof(int))]
public static void GetDamage(ref HitData.DamageTypes __result, ItemDrop.ItemData __instance)
{
DropHeadFix(__instance);
float scalingFactor = GetItemQualityScalingFactor(__instance, config.DamageStart, config.DamageStop);
__result.Modify(scalingFactor);
}
Expand Down Expand Up @@ -248,6 +291,27 @@ public static float GetCraftExperience(Recipe recipe, int craftLevel)
return exp;
}

public static bool isCraftingRecipe(Recipe recipe)
{
// # Full list of stations used in recipes as of 0.147.3:
// # - identifier: `$piece_forge` in game name: Forge
// # - identifier: `$piece_workbench` in game name: Workbench
// # - identifier: `$piece_cauldron` in game name: Cauldron
// # - identifier: `$piece_stonecutter` in game name: Stonecutter
// See also (added at some point after above list):
// - $piece_artisanstation
// - $piece_oven
if (recipe == null) return false;

string craftingStationName = recipe?.m_craftingStation?.m_name;
bool isNoStation = craftingStationName == null;
bool isForgeRecipe = craftingStationName == "$piece_forge";
bool isWorkbenchRecipe = craftingStationName == "$piece_workbench";
bool isStonecutterRecipe = craftingStationName == "$piece_stonecutter";
bool isArtisanRecipe = craftingStationName == "$piece_artisanstation";
return isWorkbenchRecipe || isForgeRecipe || isNoStation || isStonecutterRecipe || isArtisanRecipe;
}

[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
public static class InventoryGuiPatcherDoCrafting
{
Expand Down Expand Up @@ -281,23 +345,7 @@ Recipe ___m_craftRecipe
return;
}
// ZLog.LogError($"Craft Succeeded? {___m_craftRecipe.m_item.m_itemData.m_shared.m_name} {craftLevel} {___m_craftRecipe.m_craftingStation?.m_name}");

// # Full list of stations used in recipes as of 0.147.3:
// # - identifier: `$piece_forge` in game name: Forge
// # - identifier: `$piece_workbench` in game name: Workbench
// # - identifier: `$piece_cauldron` in game name: Cauldron
// # - identifier: `$piece_stonecutter` in game name: Stonecutter
// See also (added at some point after above list):
// - $piece_artisanstation
// - $piece_oven

string craftingStationName = ___m_craftRecipe.m_craftingStation?.m_name;
bool isNoStation = craftingStationName == null;
bool isForgeRecipe = craftingStationName == "$piece_forge";
bool isWorkbenchRecipe = craftingStationName == "$piece_workbench";
bool isStonecutterRecipe = craftingStationName == "$piece_stonecutter";
bool isArtisanRecipe = craftingStationName == "$piece_artisanstation";
if (isWorkbenchRecipe || isForgeRecipe || isNoStation || isStonecutterRecipe || isArtisanRecipe) {
if (isCraftingRecipe(___m_craftRecipe)) {
float craftExperience = GetCraftExperience(___m_craftRecipe, craftLevel);
player.RaiseSkill((Skills.SkillType)CRAFTING_SKILL_ID, craftExperience);
// float SkillLevel = player.GetSkillFactor((Skills.SkillType)CRAFTING_SKILL_ID);
Expand All @@ -316,7 +364,7 @@ static void Prefix(InventoryGui __instance,
// private fields
ItemDrop.ItemData ___m_craftUpgradeItem,
int ___m_craftVariant,
List<ItemDrop.ItemData> m_tempWornItems
List<ItemDrop.ItemData> ___m_tempWornItems
)
{
if (Player.m_localPlayer == null)
Expand All @@ -329,10 +377,10 @@ static void Prefix(InventoryGui __instance,
{
return;
}
m_tempWornItems.Clear();
Player.m_localPlayer.GetInventory().GetWornItems(m_tempWornItems);
___m_tempWornItems.Clear();
Player.m_localPlayer.GetInventory().GetWornItems(___m_tempWornItems);
var CanRepair = __instance.GetType().GetMethod("CanRepair", BindingFlags.NonPublic | BindingFlags.Instance);
foreach (ItemDrop.ItemData tempWornItem in m_tempWornItems)
foreach (ItemDrop.ItemData tempWornItem in ___m_tempWornItems)
{
if ((bool)CanRepair.Invoke(__instance, new object[] { tempWornItem }))
{
Expand All @@ -342,13 +390,101 @@ static void Prefix(InventoryGui __instance,
}
}
}
}

[HarmonyPatch]
static class ItemDataClonePatches
{
[HarmonyPatch(typeof(DropTable), "AddItemToList")]
static void Postfix(List<ItemDrop.ItemData> toDrop, DropTable.DropData data) {
DropTailFix(toDrop.Last());
}

[HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemDrop.ItemData), typeof(int), typeof(int), typeof(int)})]
static void Postfix(Inventory __instance, ItemDrop.ItemData item, int amount, int x, int y) {
if (config.DebugTooltips)
{
ZLog.Log("AddItem(amount:" + amount + ", x:"+x+", y:"+y+")");
}

var target = __instance.GetItemAt(x, y);

QualityComponent comp = item.Extended()?.GetComponent<QualityComponent>();
if (config.DebugTooltips)
{
ZLog.Log("... AddItem, source #" + item.Extended()?.GetUniqueId() + " | " + comp);
}
if (comp == null) return;

QualityComponent targetComp = target.Extended()?.GetComponent<QualityComponent>();
if (config.DebugTooltips)
{
ZLog.Log("... AddItem, target = #" + target.Extended()?.GetUniqueId() + " | " + targetComp + " " + (targetComp == null ? "NULL" : (target.m_stack + " > " + targetComp.Quality.Quantity)));
}
// existing item, needs extra quality data
if (targetComp != null && target.m_stack > targetComp.Quality.Quantity) {
targetComp.Quality.MergeInto(comp.Quality);
targetComp.Save();
}
DropTailFix(target);
}


[HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemDrop.ItemData) })]
static void Postfix(Inventory __instance, ItemDrop.ItemData item, List<ItemDrop.ItemData> ___m_inventory) {
// We need to recover from an item stack being automatically spread across multiple stacks in target inventory
// Example: Have stacks of 99, 99 and 99 arrows in your inventory. Crafting 20 arrows will deposit 1 arrow
// in each stack and 17 in a new stack.

// item is a reference to item (maybe also in inventory) which we take qualities from as needed
// it will likely have more qualities than stacksize, while targets for fixing have more stack than qualities

QualityComponent comp = item.Extended()?.GetComponent<QualityComponent>();

if (comp == null)
{
return;
}

var sourceQuality = comp.Quality;
if (item.m_shared.m_maxStackSize > 1)
{
foreach (ItemDrop.ItemData other in ___m_inventory)
{
if (item.m_shared.m_name != other.m_shared.m_name && item.m_quality != other.m_quality)
{
continue;
}

QualityComponent targetComp = other.Extended()?.GetComponent<QualityComponent>();
if (targetComp == null || other.m_stack <= targetComp.Quality.Quantity)
{
continue;
}
var needed = other.m_stack - targetComp.Quality.Quantity;
var quals = sourceQuality.Shift(needed);
targetComp.Quality.Qualities.AddRange(quals);
targetComp.Save();

Debug.Assert(other.m_stack == targetComp.Quality.Quantity);
// DropTailFix(target);
}
if (sourceQuality.Quantity > 0) {
comp.Save();
}
}
}

// static void Postfix(InventoryGui __instance, Player player,
// // private fields
// Recipe ___m_craftRecipe
// )
// {
// }
[HarmonyPatch(typeof(ItemDrop), "DropItem")]
static void Postfix(ref ItemDrop __result, ItemDrop.ItemData item, int amount, Vector3 position, Quaternion rotation) {
DropTailFix(__result.m_itemData);
}

[HarmonyPatch(typeof(ItemStand), "UpdateAttach")]
static void Postfix(ItemStand __instance, ItemDrop.ItemData ___m_queuedItem) {
// Drop on the origin item as we can't easily access ZDO stored item
DropTailFix(___m_queuedItem);
}
}
}
}
4 changes: 2 additions & 2 deletions CraftingSkill/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.0.1.2")]
[assembly: AssemblyFileVersion("0.0.1.2")]
[assembly: AssemblyVersion("0.0.3.0")]
[assembly: AssemblyFileVersion("0.0.3.0")]
Loading

0 comments on commit 5e75dcc

Please sign in to comment.