const int MAX_NUMBER_OF_INGREDIENTS = 2; const int MAXIMUM_RESULTS = 10; const float DEFAULT_SPAWN_DISTANCE = 0.6; class RecipeBase { string m_ItemsToCreate[MAXIMUM_RESULTS]; ref array m_Ingredients[MAX_NUMBER_OF_INGREDIENTS]; ref array m_SoundCategories[MAX_NUMBER_OF_INGREDIENTS]; protected ref array m_AnimationUIDs = new array(); // used for overriding animation based on ingredient ItemBase m_Items[MAX_NUMBER_OF_INGREDIENTS]; ItemBase m_IngredientsSorted[MAX_NUMBER_OF_INGREDIENTS]; //if the recipe is valid, this array will contain all ingredients sorted against the recipe ingredients ref array m_IngredientsToBeDeleted = new array; string m_Name; int m_ID; int m_NumberOfResults; int m_RecipeUID; float m_AnimationLength = 1;//animation length in relative time units float m_Specialty = 0;// value > 0 for roughness, value < 0 for precision bool m_IsInstaRecipe;//should this recipe be performed instantly without animation bool m_AnywhereInInventory;//is this recipe valid even when neither of the items is in hands float m_MinQuantityIngredient[MAX_NUMBER_OF_INGREDIENTS]; float m_MaxQuantityIngredient[MAX_NUMBER_OF_INGREDIENTS]; float m_MinDamageIngredient[MAX_NUMBER_OF_INGREDIENTS]; float m_MaxDamageIngredient[MAX_NUMBER_OF_INGREDIENTS]; bool m_IngredientUseSoftSkills[MAX_NUMBER_OF_INGREDIENTS]; float m_IngredientAddHealth[MAX_NUMBER_OF_INGREDIENTS]; float m_IngredientAddQuantity[MAX_NUMBER_OF_INGREDIENTS]; float m_IngredientSetHealth[MAX_NUMBER_OF_INGREDIENTS]; bool m_IngredientDestroy[MAX_NUMBER_OF_INGREDIENTS]; bool m_ResultSetFullQuantity[MAXIMUM_RESULTS]; float m_ResultSetQuantity[MAXIMUM_RESULTS]; float m_ResultSetHealth[MAXIMUM_RESULTS]; float m_ResultSpawnDistance[MAXIMUM_RESULTS]; int m_ResultToInventory[MAXIMUM_RESULTS]; int m_ResultInheritsHealth[MAXIMUM_RESULTS]; int m_ResultInheritsColor[MAXIMUM_RESULTS]; int m_ResultReplacesIngredient[MAXIMUM_RESULTS]; bool m_ResultUseSoftSkills[MAXIMUM_RESULTS]; void RecipeBase() { for (int i = 0; i < MAX_NUMBER_OF_INGREDIENTS; i++) { m_Ingredients[i] = new array; m_SoundCategories[i] = new array; m_IngredientsSorted[i] = NULL; } for (i = 0; i < MAXIMUM_RESULTS; i++) { m_ResultSpawnDistance[i] = DEFAULT_SPAWN_DISTANCE; } m_NumberOfResults = 0; m_Name = "RecipeBase default name"; m_RecipeUID = DayZPlayerConstants.CMD_ACTIONFB_CRAFTING; Init(); } void Init(); protected void SetAnimation (DayZPlayerConstants uid) { m_RecipeUID = uid; } float GetLengthInSecs() { return m_AnimationLength * CRAFTING_TIME_UNIT_SIZE; } float GetSpecialty() { return m_Specialty; } bool IsRecipeAnywhere() { return m_AnywhereInInventory; } bool IsRepeatable() { return false; } bool CheckIngredientMatch(ItemBase item1, ItemBase item2) { if (item1 == NULL || item2 == NULL) return false; m_Items[0] = item1; m_Items[1] = item2; bool found = false; for (int i = 0; i < MAX_NUMBER_OF_INGREDIENTS; i++)//all ingredients { found = false; array tempArray = m_Ingredients[i]; for (int x = 0; x < tempArray.Count(); x++)//particular ingredient array { for (int z = 0; z < MAX_NUMBER_OF_INGREDIENTS; z++) { if (m_Items[z] != NULL) { ItemBase item = m_Items[z]; if (tempArray.Get(x) == item.GetType() || GetGame().IsKindOf(item.GetType(),tempArray.Get(x))) { found = true;//we found a match //m_IngredientsSorted.Insert(item); m_IngredientsSorted[i] = item; m_Items[z] = NULL; } } if (found) break;//we found a match, no need to check the remaining ingredients } if (found) break;//we found a match, no need to check this m_Ingredient array } if (!found) return false;// no match within an m_Ingredient array, no reason to continue the search, recipe is invalid } if (found) { return true; } else { return false; } } void InsertIngredient(int index, string ingredient, DayZPlayerConstants uid = DayZPlayerConstants.CMD_ACTIONFB_CRAFTING) { InsertIngredientEx(index, ingredient, "", uid); } void InsertIngredientEx(int index, string ingredient, string soundCategory, DayZPlayerConstants uid = DayZPlayerConstants.CMD_ACTIONFB_CRAFTING) { array ptr = m_Ingredients[index]; ptr.Insert(ingredient); m_SoundCategories[index].Insert(soundCategory); if(index == 0) { m_AnimationUIDs.Insert(uid); } } void RemoveIngredient(int index, string ingredient) { array ptr = m_Ingredients[index]; for (int i = 0; i < ptr.Count(); i++) { if (ptr[i] == ingredient) { ptr.Remove(i); m_SoundCategories[index].Remove(i); return; } } } void AddResult(string item) { m_ItemsToCreate[m_NumberOfResults] = item; m_NumberOfResults++; } string GetName() { return m_Name; } bool IsInstaRecipe() { return m_IsInstaRecipe; } //spawns results in the world void SpawnItems(ItemBase ingredients[], PlayerBase player, array spawned_objects) { spawned_objects.Clear();//just to make sure EntityAI object = NULL; for (int i = 0; i < m_NumberOfResults; i++) { string item_to_spawn = m_ItemsToCreate[i]; if (m_ResultInheritsColor[i] != -1) { ItemBase item = ingredients[m_ResultInheritsColor[i]]; string color = item.ConfigGetString("color"); string new_class_name = m_ItemsToCreate[i] + color; item_to_spawn = new_class_name; } if (m_ResultToInventory[i] == -1) { Debug.Log(" = "+m_ResultToInventory[i].ToString(),"recipes"); /* InventoryLocation inv_loc = new InventoryLocation; if (player.GetInventory().FindFirstFreeLocationForNewEntity(item_to_spawn, FindInventoryLocationType.ANY, inv_loc)) { object = SpawnItemOnLocation(item_to_spawn, inv_loc, false); } */ object = player.GetInventory().CreateInInventory(item_to_spawn); } else if (m_ResultToInventory[i] >= 0) { /* object = player.SpawnEntityOnGroundOnCursorDir(item_to_spawn, 0.5); ItemBase item_swap_with = ingredients[m_ResultToInventory[i]]; player.SwapEntities(true, item_swap_with, EntityAI.Cast(object)); */ } //spawning in inventory failed, spawning on the ground instead..... if (!object) { object = player.SpawnEntityOnGroundRaycastDispersed(item_to_spawn,m_ResultSpawnDistance[i]); if (!object) Error("failed to spawn entity "+item_to_spawn+" , make sure the classname exists and item can be spawned"); } spawned_objects.Insert(ItemBase.Cast(object)); object = null; } } //applies final modifications to results void ApplyModificationsResults(ItemBase sorted[], array results, ItemBase result, PlayerBase player) { float all_ingredients_health = 0;//this is used later in results float all_ingredients_health01 = 0;//combined damage % of ingredients int value_delta; for (int i = 0; i < MAX_NUMBER_OF_INGREDIENTS; i++) { ItemBase ingrd = ItemBase.Cast(sorted[i]); all_ingredients_health += ingrd.GetHealth("", "");//accumulate health of all ingredients, used in results all_ingredients_health01 += ingrd.GetHealth01("", ""); } //------------------- results ---------------------- for (i = 0; i < m_NumberOfResults; i++) { ItemBase res = results.Get(i); if (!res) { continue; } if (res.IsItemBase()) { value_delta = m_ResultSetQuantity[i]; ItemBase resIb = ItemBase.Cast(res); if (!resIb.IsMagazine())//is not a magazine { if (m_ResultSetFullQuantity[i] == 1)//<------m_ResultSetFullQuantity { resIb.SetQuantityMax(); } else if (value_delta != -1)//<------m_ResultSetQuantity { resIb.SetQuantity(value_delta); } } else//is magazine { Magazine mgzn = Magazine.Cast(resIb); if (m_ResultSetFullQuantity[i] == 1)//<------m_ResultSetFullQuantity { mgzn.ServerSetAmmoMax(); } else if (value_delta != -1)//<------m_ResultSetQuantity { mgzn.ServerSetAmmoCount(value_delta); } } } if (m_ResultSetHealth[i] != -1)//<------m_ResultSetHealth { value_delta = m_ResultSetHealth[i]; res.SetHealth("","",value_delta); } if (m_ResultInheritsHealth[i] != -1)//<------m_ResultInheritsHealth { if (m_ResultInheritsHealth[i] >= 0) { int ing_number = m_ResultInheritsHealth[i]; ItemBase ing = sorted[ing_number]; if (ing) { float ing_health01 = ing.GetHealth01("",""); res.SetHealth("", "", ing_health01 * res.GetMaxHealth("","")); Debug.Log("Inheriting health from ingredient:"+m_ResultInheritsHealth[i].ToString(),"recipes"); } } else if (m_ResultInheritsHealth[i] == -2) { float average_health01 = all_ingredients_health01 / MAX_NUMBER_OF_INGREDIENTS; res.SetHealth("", "", average_health01 * res.GetMaxHealth("","")); } } if (m_ResultReplacesIngredient[i] != -1)//<------ResultReplacesIngredient { if (m_ResultReplacesIngredient[i] > -1) { int ing_num = m_ResultReplacesIngredient[i]; ItemBase ingr = sorted[ing_num]; if (ingr) { MiscGameplayFunctions.TransferItemProperties(ingr, res); MiscGameplayFunctions.TransferInventory(ingr, res, player); } } } } } void DeleleIngredientsPass() { for (int i = 0; i < m_IngredientsToBeDeleted.Count(); i++) { ItemBase ingredient = m_IngredientsToBeDeleted.Get(i); ingredient.Delete(); } m_IngredientsToBeDeleted.Clear(); } //applies final modifications to ingredients void ApplyModificationsIngredients(ItemBase sorted[], PlayerBase player) { //---------------------- ingredients ---------------------- for (int i = 0; i < MAX_NUMBER_OF_INGREDIENTS; i++) { ItemBase ingredient = sorted[i]; if (m_IngredientDestroy[i] == 1)//<------m_IngredientDestroy { if (ingredient) m_IngredientsToBeDeleted.Insert(ingredient); } else { if (m_IngredientAddHealth[i] != 0)//<------m_IngredientAddHealth { float health_delta = m_IngredientAddHealth[i]; ingredient.AddHealth("","",health_delta); } else if (m_IngredientSetHealth[i] != -1)//<------m_IngredientSetHealth { float new_health = m_IngredientSetHealth[i]; ingredient.SetHealth("","",new_health); } if (m_IngredientAddQuantity[i] != 0)//<------m_IngredientAddQuantity { float quantity_delta = m_IngredientAddQuantity[i]; if (!ingredient.IsMagazine()) { ItemBase obj = ingredient; bool isDestroyed = obj.AddQuantity(quantity_delta, true); if (isDestroyed) { continue; } } else { Magazine mag = Magazine.Cast(ingredient); int newQuantity = mag.GetAmmoCount() + quantity_delta; if (newQuantity <= 0) { if (mag) m_IngredientsToBeDeleted.Insert(mag); continue; } else { mag.ServerSetAmmoCount(newQuantity); } } } } } } //checks the recipe conditions bool CheckConditions(ItemBase sorted[]) { for (int i = 0; i < MAX_NUMBER_OF_INGREDIENTS; i++) { ItemBase ingredient = sorted[i]; if (!ingredient.IsMagazine()) { if (ingredient.GetQuantityMax() !=0 && m_MinQuantityIngredient[i] >= 0 && ingredient.GetQuantity() < m_MinQuantityIngredient[i]) { //Debug.Log("Recipe condition check failing1: m_MinQuantityIngredient","recipes"); return false; } if (m_MaxQuantityIngredient[i] >= 0 && ingredient.GetQuantity() > m_MaxQuantityIngredient[i]) { //Debug.Log("Recipe condition check failing1: m_MaxQuantityIngredient","recipes"); return false; } } else { Magazine mag1 = Magazine.Cast(ingredient); if (m_MinQuantityIngredient[i] >= 0 && mag1.GetAmmoCount() < m_MinQuantityIngredient[i]) { //Debug.Log("Recipe condition check failing1: m_MinQuantityIngredient[0]","recipes"); return false; } if (m_MaxQuantityIngredient[i] >= 0 && mag1.GetAmmoCount() > m_MaxQuantityIngredient[i]) { //Debug.Log("Recipe condition check failing1: m_MaxQuantityIngredient[0]","recipes"); return false; } } int dmg3 = ingredient.GetHealthLevel(); if (m_MinDamageIngredient[i] >= 0 && ingredient.GetHealthLevel() < m_MinDamageIngredient[i]) { int dmg = ingredient.GetHealthLevel(); //Debug.Log("Recipe condition check failing1: m_MinDamageIngredient[0]","recipes"); return false; } if (m_MaxDamageIngredient[i] >= 0 && ingredient.GetHealthLevel() > m_MaxDamageIngredient[i]) { int dmg2 = ingredient.GetHealthLevel(); //Debug.Log("Recipe condition check failing1: m_MaxDamageIngredient[0]","recipes"); return false; } } return true; } //checks overall validity of this recipe bool CheckRecipe(ItemBase item1, ItemBase item2, PlayerBase player) { if (item1 == NULL || item2 == NULL) { Error("recipe invalid, at least one of the ingredients is NULL"); return false; } ItemBase item_in_hand = player.GetItemInHands(); if (!IsRecipeAnywhere() && (item1 != item_in_hand && item2 != item_in_hand)) { return false; } m_IngredientsSorted[0] = item1; m_IngredientsSorted[1] = item2; if (CanDo(m_IngredientsSorted, player) && CheckConditions(m_IngredientsSorted)) { return true; } return false; } protected void CheckIngredientAnimOverride() { array tempArray = m_Ingredients[0]; for ( int i; i < m_AnimationUIDs.Count(); i++ ) { if (m_IngredientsSorted[0].ClassName() == tempArray[i] || m_IngredientsSorted[1].ClassName() == tempArray[i]) SetAnimation(m_AnimationUIDs[i]); } } void OnSelectedRecipe(ItemBase item1, ItemBase item2, PlayerBase player) { if (item1 == NULL || item2 == NULL) { Error("CheckRecipe: recipe invalid, at least one of the ingredients is NULL"); //Debug.Log("recipe invalid, at least one of the ingredients is NULL","recipes"); return; } OnSelected(item1,item2,player); } void OnSelected(ItemBase item1, ItemBase item2, PlayerBase player) { } //performs this recipe void PerformRecipe(ItemBase item1, ItemBase item2, PlayerBase player) { if (item1 == NULL || item2 == NULL) { Error("PerformRecipe: recipe invalid, at least one of the ingredients is NULL"); Debug.Log("PerformRecipe: at least one of the ingredients is NULL","recipes"); } if (CheckRecipe(item1,item2,player)) { array spawned_objects = new array; SpawnItems(m_IngredientsSorted, player,spawned_objects); ApplyModificationsResults(m_IngredientsSorted, spawned_objects, NULL, player); ApplyModificationsIngredients(m_IngredientsSorted, player); Do(m_IngredientsSorted, player, spawned_objects, m_Specialty); DeleleIngredientsPass(); } else { Debug.Log("CheckRecipe failed on server","recipes"); } } void ApplySoftSkillsSpecialty(PlayerBase player) { } bool CanDo(ItemBase ingredients[], PlayerBase player) { //Debug.Log("Called Can Do on a recipe id:" + m_ID.ToString(),"recipes"); for (int i = 0; i < MAX_NUMBER_OF_INGREDIENTS; i++) { if (ingredients[i].GetInventory() && ingredients[i].GetInventory().AttachmentCount() > 0) return false; } return true; } void Do(ItemBase ingredients[], PlayerBase player, array results, float specialty_weight) { //Debug.Log("Called Do on a recipe id:" + m_ID.ToString(),"recipes"); } int GetID() { return m_ID; } void SetID(int id) { m_ID = id; } void GetAllItems(array items) { for (int i = 0; i < MAX_NUMBER_OF_INGREDIENTS; i++) { array ptr = m_Ingredients[i]; for (int x = 0; x < ptr.Count(); x++) { items.Insert(ptr.Get(x)); } } } string GetSoundCategory(int ingredientIndex, ItemBase item) { string itemType = item.GetType(); array ptr = m_Ingredients[ingredientIndex]; for (int x = 0; x < ptr.Count(); x++) { if (GetGame().IsKindOf(itemType, ptr.Get(x))) { return m_SoundCategories[ingredientIndex].Get(x); } } return ""; } bool IsItemInRecipe(string item) { for (int i = 0; i < MAX_NUMBER_OF_INGREDIENTS; i++) { array ptr = m_Ingredients[i]; for (int x = 0; x < ptr.Count(); x++) { if (ptr.Get(x) == item) return true; } } return false; } //! returns a mask which marks ingredient positions for a given item in this recipe(for example mask of value 3 [....000011] means this item is both ingredient 1 and 2 in this recipe[from right to left]) int GetIngredientMaskForItem(string item) { int mask = 0; for (int i = 0; i < MAX_NUMBER_OF_INGREDIENTS; i++) { array ptr = m_Ingredients[i]; for (int x = 0; x < ptr.Count(); x++) { if (ptr.Get(x) == item) { mask = ((int)Math.Pow(2, i)) | mask; } } } return mask; } int GetAnimationCommandUID() { if (m_AnimationUIDs.Count() > 0) CheckIngredientAnimOverride(); return m_RecipeUID; } }