const int MAX_NUMBER_OF_INGREDIENTS = 2;
const int MAXIMUM_RESULTS = 10;
const float DEFAULT_SPAWN_DISTANCE = 0.6;
class RecipeAnimationInfo
{
string m_IngredientName;
int m_AnimationUID;
bool m_ItemVisible;
void RecipeAnimationInfo(string ingredient, int animationID, bool itemVisible)
{
m_IngredientName = ingredient;
m_AnimationUID = animationID;
m_ItemVisible = itemVisible;
}
}
class RecipeBase
{
protected const int BASE_CRAFT_ANIMATION_ID = DayZPlayerConstants.CMD_ACTIONFB_CRAFTING;
string m_ItemsToCreate[MAXIMUM_RESULTS];
ref array<string> m_Ingredients[MAX_NUMBER_OF_INGREDIENTS];
ref array<string> m_SoundCategories[MAX_NUMBER_OF_INGREDIENTS];
protected ref array<ref RecipeAnimationInfo> m_AnimationInfos = new array<ref RecipeAnimationInfo>(); // 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<ItemBase> m_IngredientsToBeDeleted = new array<ItemBase>;
string m_Name;
int m_ID;
int m_NumberOfResults;
int m_RecipeUID;//obsolete
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<string>;
m_SoundCategories[i] = new array<string>;
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 = BASE_CRAFT_ANIMATION_ID;
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<string> 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() || g_Game.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 = BASE_CRAFT_ANIMATION_ID, bool showItem = false)
{
InsertIngredientEx(index, ingredient, "", uid, showItem);
}
void InsertIngredientEx(int index, string ingredient, string soundCategory, DayZPlayerConstants uid = BASE_CRAFT_ANIMATION_ID, bool showItem = false)
{
array<string> ptr = m_Ingredients[index];
ptr.Insert(ingredient);
m_SoundCategories[index].Insert(soundCategory);
if(uid != BASE_CRAFT_ANIMATION_ID)
{
RecipeAnimationInfo rai = new RecipeAnimationInfo(ingredient, uid, showItem);
int animationIndex;
for(animationIndex = 0; animationIndex < m_AnimationInfos.Count(); animationIndex++)
{
if(g_Game.IsKindOf(ingredient, m_AnimationInfos[animationIndex].m_IngredientName))
break;
}
m_AnimationInfos.InsertAt(rai, animationIndex);
}
}
void RemoveIngredient(int index, string ingredient)
{
array<string> 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<ItemBase> 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<ItemBase> 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;
}
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<ItemBase> spawned_objects = new array<ItemBase>;
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<ItemBase> 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<string> items)
{
for (int i = 0; i < MAX_NUMBER_OF_INGREDIENTS; i++)
{
array<string> 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<string> ptr = m_Ingredients[ingredientIndex];
for (int x = 0; x < ptr.Count(); x++)
{
if (g_Game.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<string> 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<string> 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()//obsolete
{
return BASE_CRAFT_ANIMATION_ID;
}
RecipeAnimationInfo GetRecipeAnimationInfo(PlayerBase player, ItemBase mainItem, ItemBase target)
{
RecipeAnimationInfo recipeAnimationInfo;
int found = false;
for(int i = 0; i < m_AnimationInfos.Count();i++)
{
recipeAnimationInfo = m_AnimationInfos[i];
if(g_Game.IsKindOf(mainItem.GetType(), recipeAnimationInfo.m_IngredientName))
{
found = true;
break;
}
}
if(!found)
{
recipeAnimationInfo = new RecipeAnimationInfo("ItemBase", BASE_CRAFT_ANIMATION_ID, false);
}
return recipeAnimationInfo;
}
}