DayZ 1.24
Loading...
Searching...
No Matches
PluginRecipesManager.c
Go to the documentation of this file.
7
8const float ACCEPTABLE_DISTANCE = 5;
9
10const int SANITY_CHECK_ACCEPTABLE_RESULT = ERecipeSanityCheck.NOT_OWNED_BY_ANOTHER_LIVE_PLAYER | ERecipeSanityCheck.CLOSE_ENOUGH;
11
12class PluginRecipesManager extends PluginRecipesManagerBase
13{
15 static ref map<typename, bool> m_RecipesInitializedItem = new ref map<typename, bool>;
16
17
19 const int MAX_NUMBER_OF_RECIPES = GetMaxNumberOfRecipes();
20 const int MAX_CONCURENT_RECIPES = 128;
21 const int MAX_INGREDIENTS = 5;
22
24 int m_ResolvedRecipes[MAX_CONCURENT_RECIPES];
25
26 bool m_EnableDebugCrafting = false;
27 ItemBase m_Ingredients[MAX_INGREDIENTS];
28 int m_IngredientBitMask[MAX_INGREDIENTS];
29 int m_IngredientBitMaskSize[MAX_INGREDIENTS];
30
31 int m_BitsResults[MAX_INGREDIENTS];
32
36
37 ItemBase m_sortedIngredients[MAX_NUMBER_OF_INGREDIENTS];
38
39 ref array<int> m_RecipesMatched = new array<int>;
40 ref array<string> m_CachedItems = new array<string>;
41
42 ref array<ref RecipeBase> m_RecipeList = new array<ref RecipeBase>;//all recipes
43 static ref map<string, int> m_RecipeNamesList = new map<string, int>;//all recipes
44
46
48 {
49 return 2048;
50 }
51
53 {
54
57
58 myTimer1 = new Timer();
59 }
60
62 {
63 }
64
65
67 {
69 }
70
72 {
73 m_EnableDebugCrafting = enable;
74 }
75
77 {
78 if (m_RecipeList[recipe_id])
79 return m_RecipeList[recipe_id].GetName();
80
81 return "";
82 }
83
84
85 //will fill the map 'ids' with valid recipes for the 'item1' and 'item2' items, where the key is the recipe ID, and the value is the recipe name
87 {
88 if (item1 == NULL || item2 == NULL)
89 {
90 if (ids) ids.Clear();
91 return 0;
92 }
93
94 if ((item1.GetInventory().IsAttachment() && item1.GetHierarchyParent().DisassembleOnLastDetach()) || (item2.GetInventory().IsAttachment() && item2.GetHierarchyParent().DisassembleOnLastDetach()))
95 {
96 if (ids) ids.Clear();
97 return 0;
98 }
99 m_Ingredients[0] = item1;
100 m_Ingredients[1] = item2;
101
102 return GetValidRecipesProper(2, m_Ingredients, ids, player);
103 }
104
106 {
107 if (ids) ids.Clear();
109 int numOfRecipes = SortIngredients(num_of_items, items, m_ResolvedRecipes);
110 //will return number of cached recipes for the 2 items being considered,
111 //and save their indexes in m_ResolvedRecipes, please note the m_ResolvedRecipes is a static array,
112 //and does not clear up with each query, so the number of recipes returned is critical to make sure we don't accidentally
113 //mix in some other indexes from previous queries(and obviously loop only as far as we have to)
114 //this also saves the ingredients in the correct assignment as ingredient 1/2 into m_ingredient1/m_ingredient2 arrays, these 3 arrays
115 //therefore provide you with information per each index with: recipeid,ingredient1,ingredient2
116 if (numOfRecipes == 0) return 0;
117 int found = 0;
119 for (int i = 0; i < numOfRecipes; i++)
120 {
121 p_recipe = m_RecipeList[m_ResolvedRecipes[i]];
122
123 if (p_recipe.CheckRecipe(m_ingredient1[i], m_ingredient2[i], player) == true)
124 {
125 if (ids) ids.Insert(p_recipe.GetID());
126 found++;
127 }
128 }
129 return found;
130 }
131
132
134 {
135 if (m_RecipeList[recipe_id]) return m_RecipeList[recipe_id].GetLengthInSecs();
136 return 0;
137 }
138
140 {
141 if (m_RecipeList[recipe_id]) return m_RecipeList[recipe_id].GetSpecialty();
142 return 0;
143 }
144
146 {
147 if (m_RecipeList[recipe_id]) return m_RecipeList[recipe_id].IsInstaRecipe();
148 else return false;
149 }
150
151 override void OnInit()
152 {
153 super.OnInit();
154 //ReadCacheFromFile(PATH_CACHE_FILE);//read the cache from a file
155 //GenerateHumanReadableRecipeList();
156 }
157
158
160 {
161 Debug.Log("CallbackGenerateCache", "recipes");
163 //SaveCacheToFile(PATH_CACHE_FILE);//generate the cache and save it to a file
164 //ReadCacheFromFile(PATH_CACHE_FILE);
165 }
166
167 protected void GenerateRecipeCache()
168 {
169 GetGame().ProfilerStart("m_RecipeCache");
170
171 //m_CacheBasesMap.Clear();
172 m_CachedItems.Clear();
173 PluginRecipesManager.m_RecipeCache.Clear();
174
176
180 //Debug.Log("Got here 0","caching");
181 string config_path;
182 string child_name;
183 int scope;
185 WalkRecipes();
186 for (int i = 0; i < all_config_paths.Count(); i++)
187 {
189 int children_count = GetGame().ConfigGetChildrenCount(config_path);
190
191 for (int x = 0; x < children_count; x++)
192 {
193 GetGame().ConfigGetChildName(config_path, x, child_name);
194 scope = GetGame().ConfigGetInt(config_path + " " + child_name + " scope");
195
196 if (scope == 2)
197 {
198 GetGame().ConfigGetFullPath(config_path + " " + child_name,/*out*/ full_path);
200 }
201 }
202 }
203 GetGame().ProfilerStop("m_RecipeCache");
204 }
205
207 {
208 //Print("------------- WalkRecipes --------------");
209 for (int c = 0; c < m_RecipeList.Count(); c++)
210 {
211 RecipeBase recipe = m_RecipeList.Get(c);
212 if (recipe)
213 {
214 //Print(recipe.ClassName());
215 int recipe_id = recipe.GetID();
216 for (int i = 0; i < MAX_NUMBER_OF_INGREDIENTS; i++)
217 {
218 array<string> list = recipe.m_Ingredients[i];
219
220 for (int x = 0; x < list.Count(); x++)
221 {
222 string ingredient = list.Get(x);
223 int mask = Math.Pow(2, i);
224 CacheObject co = m_RecipeCache.Get(ingredient);
225
226 if (!co)
227 {
228 co = new CacheObject;
229 m_RecipeCache.Insert(ingredient, co);
230 }
231 co.AddRecipe(recipe_id, mask);
232 }
233 }
234 }
235 }
236 }
237
238
239 // moved outside method to speed things up
248
249 //this will take the item class name and resolve it against all processed recipes
251 {
252 m_ItemName = full_path.Get(0);
253 m_CoItem = m_RecipeCache.Get(m_ItemName);
254 //Print(m_ItemName);
255 if (!m_CoItem)
256 {
257 m_CoItem = new CacheObject;
258 m_RecipeCache.Insert(m_ItemName, m_CoItem);
259 }
260 for (int i = 1; i < full_path.Count(); i++)
261 {
262 m_BaseName = full_path.Get(i);
263 m_CoBase = m_RecipeCache.Get(m_BaseName);
264 if (m_CoBase) //resolve new base classes
265 {
266 m_RcpsArray = m_RecipeCache.Get(m_BaseName).GetRecipes();
267
268 for (int x = 0; x < m_RcpsArray.Count(); x++) //base recipes
269 {
270 m_RecipeID = m_RcpsArray.Get(x);
271
272 //item_mask = m_CoItem.GetMaskByRecipeID(m_RecipeID);
273 m_BaseMask = m_CoBase.GetMaskByRecipeID(m_RecipeID);
274
275 m_CoItem.AddRecipe(m_RecipeID, m_BaseMask);
276 }
277 }
278 }
279
280
281 }
282
284 {
285 m_Ingredients[0] = item_a;
286 m_Ingredients[1] = item_b;
287
288 if (!item_a || !item_b)
289 {
290 Error("PerformRecipeServer - one of the items null !!");
291 return;
292 }
293
294 SortIngredientsInRecipe(id, 2, m_Ingredients, m_sortedIngredients);
295
296 bool is_recipe_valid = CheckRecipe(id, m_sortedIngredients[0], m_sortedIngredients[1], player);
297 bool passed_sanity_check = RecipeSanityCheck(2, m_sortedIngredients, player);
298
299 if (!is_recipe_valid)
300 {
301 Error("PerformRecipeServer - recipe not valid !!");
302 return;
303 }
305 {
306 Error("PerformRecipeServer - recipe failed to pass sanity check !!");
307 return;
308 }
309 RecipeBase ptrRecipe = m_RecipeList[id];
310 ptrRecipe.PerformRecipe(m_sortedIngredients[0], m_sortedIngredients[1], player);
311 //player.RequestCraftingDisable();
312
313 }
314
316 {
317 FileHandle file = OpenFile("$profile:RecipeDump.txt", FileMode.WRITE);
318 if (file == 0)
319 {
320 //error message
321 PrintString("failed to open file RecipeDump");
322 return;
323 }
325 for (int i = 0; i < PluginRecipesManager.m_RecipeCache.Count(); i++)
326 {
327 string key = PluginRecipesManager.m_RecipeCache.GetKey(i);
328 CacheObject value = PluginRecipesManager.m_RecipeCache.GetElement(i);
329
330 string line = key;
331 recipes.Clear();
332 recipes.InsertAll(value.GetRecipes());
333
334 //PrintString("Item: " + key);
335
336 for (int x = 0; x < recipes.Count(); x++)
337 {
338 int recipe_id = recipes.Get(x);
340 line += "," + recipe_name;
341 }
343 }
345 }
346
348 {
349 CacheObject co = PluginRecipesManager.m_RecipeCache.Get(itemName);
350 array<int> ids = co.GetRecipes();
351
353 foreach (int recipeID : ids)
354 recipes.Insert(m_RecipeList[recipeID]);
355
356 return recipes;
357 }
358
360 {
362
363 for (int i = 0; i < num_of_ingredients; i++)
364 {
365 ItemBase item = items[i];
366 Man item_owner_player = item.GetHierarchyRootPlayer();
367 vector item_pos = item.GetPosition();
368 vector player_pos = player.GetPosition();
369
371 check_results[i] = check_results[i] | ERecipeSanityCheck.IS_IN_PLAYER_INVENTORY;
372
374 check_results[i] = check_results[i] | ERecipeSanityCheck.NOT_OWNED_BY_ANOTHER_LIVE_PLAYER;
375
378 }
379 for (i = 0; i < num_of_ingredients; i++)
380 {
382 return false;
383 }
384 return true;
385 }
386
387 override protected void RegisterRecipe(RecipeBase recipe)
388 {
389
390 if (m_RegRecipeIndex >= MAX_NUMBER_OF_RECIPES)
391 Error("Exceeded max. number of recipes, max set to: " + MAX_NUMBER_OF_RECIPES.ToString());
392
393 m_RegRecipeIndex = m_RecipeList.Insert(recipe);
394 recipe.SetID(m_RegRecipeIndex);
395 m_RecipeNamesList.Insert(recipe.ClassName(), m_RegRecipeIndex);
396 //Print("RegisterRecipe: " +recipe.ClassName() + ", "+ m_RegRecipeIndex.ToString());
397 }
398
399 override protected void UnregisterRecipe(string clasname)
400 {
402 //Print("UnregisterRecipe: " + recipe_id.ToString());
403 if (recipe_id != -1)
404 {
405 m_RecipeNamesList.Remove(clasname);
406 //Print(m_RecipeList[recipe_id]);
407 m_RecipeList[recipe_id] = null;
408 //Print(m_RecipeList[recipe_id]);
409 }
410 }
411
413 {
414 if (m_RecipeNamesList.Contains(classname))
415 return m_RecipeNamesList.Get(classname);
416 return -1;
417 }
418
419 protected bool CheckRecipe(int id, ItemBase item1, ItemBase item2, PlayerBase player)//requires ordered items
420 {
421 RecipeBase p_recipe = m_RecipeList[id];
422 return p_recipe.CheckRecipe(item1, item2, player);
423 }
424
425 protected void PrintCache()
426 {
427 for (int i = 0; i < PluginRecipesManager.m_RecipeCache.Count(); i++)
428 {
429 string key = PluginRecipesManager.m_RecipeCache.GetKey(i);
430 CacheObject co = PluginRecipesManager.m_RecipeCache.GetElement(i);
431
432 PrintString("Item: " + key);
433 co.DebugPrint();
434 PrintString("----------------");
435 }
436 }
439 {
440 ClearResults();
441
442 for (int i = 0; i < num_of_ingredients; i++)
443 {
444 CacheObject co_item = PluginRecipesManager.m_RecipeCache.Get(ingredients_unsorted[i].GetType());
445 m_IngredientBitMask[i] = co_item.GetMaskByRecipeID(id);
446 m_IngredientBitMaskSize[i] = co_item.GetBitCountByRecipeID(id);
447 }
448
450
451 if (result)
452 {
453 for (i = 0; i < num_of_ingredients; i++)
454 {
455 int index = Math.Log2(m_BitsResults[i]);
457 }
458 }
459 //PrintResultMasks(num_of_ingredients);
460 return result;
461 }
462
463 protected void ClearResults()
464 {
465 for (int i = 0; i < MAX_INGREDIENTS; i++)
466 m_BitsResults[i] = 0;
467
468 }
469
470 protected bool ResolveIngredients(int num_of_ingredients, int passes = 0)
471 {
472 int rightmost_bit;
473 int smallest = 99999;
474 int smallest_index = 0;
475
476 for (int i = 0; i < num_of_ingredients; i++)
477 {
478 int count = m_IngredientBitMaskSize[i];
479 if (count != 0 && count < smallest)
480 {
481 smallest = m_IngredientBitMaskSize[i];
483 }
484 }
485
486 rightmost_bit = m_IngredientBitMask[smallest_index] & (-m_IngredientBitMask[smallest_index]);
487 m_BitsResults[smallest_index] = m_BitsResults[smallest_index] | rightmost_bit;
488
489 for (int x = 0; x < num_of_ingredients; x++)
490 {
491 m_IngredientBitMask[x] = ~rightmost_bit & m_IngredientBitMask[x];
492 m_IngredientBitMask[smallest_index] = 0;
493 m_IngredientBitMaskSize[smallest_index] = 0;
494 }
495
496 // check validity
497 int check_sum_vertical = 0;
498
499 for (int z = 0; z < num_of_ingredients; z++)
500 {
501 check_sum_vertical = check_sum_vertical | m_IngredientBitMask[z];//vertical sum
502 check_sum_vertical = check_sum_vertical | m_BitsResults[z];//vertical sum
503 if ((m_IngredientBitMask[z] | m_BitsResults[z]) == 0)
504 {
505 return false;//horizontal check
506 }
507 }
508
509 if (check_sum_vertical != (Math.Pow(2, num_of_ingredients) - 1)) return false; //vertical check
510
511 passes++;
512
514 {
515 if (!ResolveIngredients(num_of_ingredients, passes)) return false;
516 }
517 return true;
518 }
519
520
521 protected void PrintResultMasks(int num)
522 {
523 for (int i = 0; i < num; i++)
524 Debug.Log("results mask(" + i.ToString() + ") = " + m_BitsResults[i].ToString());
525 }
526
529 {
530 int count = 0;
531 int smallest = 9999;
532 int smallest_index = 0;
533 m_RecipesMatched.Clear();
534
535 /*
536 m_Ingredients[0] = item_a;
537 m_Ingredients[1] = item_b;
538 */
539 //find the item with smallest number of recipes
541
542 for (int i = 0; i < num_of_ingredients; i++)
543 {
544 CacheObject cobject = PluginRecipesManager.m_RecipeCache.Get(items[i].GetType());
545 if (!cobject)
546 return 0;
547 if (cobject.GetNumberOfRecipes() < smallest)
548 {
549 smallest = cobject.GetNumberOfRecipes();
552 }
553 }
554
555 //look for matches
556 array<int> recipes = co_least_recipes.GetRecipes();
557 for (int x = 0; x < recipes.Count(); x++)
558 {
559 int id = recipes.Get(x);
560 for (int z = 0; z < num_of_ingredients; z++)
561 {
562 if (z != smallest_index)
563 {
564 CacheObject cobject2 = PluginRecipesManager.m_RecipeCache.Get(items[z].GetType());
565 if (cobject2.IsContainRecipe(id))
566 {
567 m_RecipesMatched.Insert(id);
568 count++;
569 }
570 }
571 }
572 }
573 return count;
574 }
575
577 {
578 int count = 0;
579 for (int i = 0; i < m_RecipesMatched.Count() && i < MAX_CONCURENT_RECIPES; i++)
580 {
581 int recipe_id = m_RecipesMatched.Get(i);
582
583 if (SortIngredientsInRecipe(recipe_id, num_of_ingredients, items_unsorted, m_sortedIngredients))//...and now we need to determine which item will be which ingredient number
584 {
586 m_ingredient1[count] = m_sortedIngredients[0];
587 m_ingredient2[count] = m_sortedIngredients[1];
588 //m_ingredient3[count] = m_sortedIngredients[2];
589 count++;
590 }
591 }
592 return count;
593 }
594
595
596 protected void CreateAllRecipes()
597 {
599 }
600
601
603 {
605 ItemBase sorted[2];
606
607 unsorted[0] = item1;
608 unsorted[1] = item2;
609
611
612 RecipeBase recipe = m_RecipeList[recipeID];
613 string soundCat = recipe.GetSoundCategory(0, sorted[0]);
614
615 if (!soundCat)
616 soundCat = recipe.GetSoundCategory(1, sorted[1]);
617
618 return soundCat;
619 }
620
621
622}
eBleedingSourceType GetType()
DetachActionData m_ItemName
WorldCraftActionReciveData m_RecipeID
void CacheObject()
Definition CacheObject.c:35
Icon x
ref Timer myTimer1
const int SANITY_CHECK_ACCEPTABLE_RESULT
ERecipeSanityCheck
@ CLOSE_ENOUGH
@ IS_IN_PLAYER_INVENTORY
@ NOT_OWNED_BY_ANOTHER_LIVE_PLAYER
enum ERecipeSanityCheck ACCEPTABLE_DISTANCE
void RegisterRecipies()
Please do not delete commented recipes, they are usually commented out for a reason.
const int MAX_NUMBER_OF_INGREDIENTS
Definition RecipeBase.c:1
Definition Debug.c:14
static void Log(string message=LOG_DEFAULT, string plugin=LOG_DEFAULT, string author=LOG_DEFAULT, string label=LOG_DEFAULT, string entity=LOG_DEFAULT)
Prints debug message with normal prio.
Definition Debug.c:133
Definition EnMath.c:7
static int RecipeIDFromClassname(string classname)
void RegisterRecipe(RecipeBase recipe)
void UnregisterRecipe(string clasname)
bool ResolveIngredients(int num_of_ingredients, int passes=0)
void SetEnableDebugCrafting(bool enable)
void PerformRecipeServer(int id, ItemBase item_a, ItemBase item_b, PlayerBase player)
bool GetIsInstaRecipe(int recipe_id)
float GetRecipeSpecialty(int recipe_id)
int GetValidRecipesProper(int num_of_items, ItemBase items[], array< int > ids, PlayerBase player)
int GetRecipeIntersection(int num_of_ingredients, ItemBase items[])
fills an array with recipe IDs which 'item_a' and 'item_b' share
array< RecipeBase > GetRecipesForItem(string itemName)
bool CheckRecipe(int id, ItemBase item1, ItemBase item2, PlayerBase player)
float GetRecipeLengthInSecs(int recipe_id)
bool RecipeSanityCheck(int num_of_ingredients, InventoryItemBase items[], PlayerBase player)
string GetRecipeName(int recipe_id)
string GetSoundCategory(int recipeID, ItemBase item1, ItemBase item2)
void MatchItems(TStringArray full_path)
bool SortIngredientsInRecipe(int id, int num_of_ingredients, ItemBase ingredients_unsorted[], ItemBase ingredients_sorted[])
sorts ingredients correctly as either first or second ingredient based on their masks
int SortIngredients(int num_of_ingredients, ItemBase items_unsorted[], int resolved_recipes[])
int GetValidRecipes(ItemBase item1, ItemBase item2, array< int > ids, PlayerBase player)
static proto native float Distance(vector v1, vector v2)
Returns the distance between tips of two 3D vectors.
proto native CGame GetGame()
void Error(string err)
Messagebox with error message.
Definition EnDebug.c:90
array< string > TStringArray
Definition EnScript.c:666
void PrintString(string s)
Helper for printing out string expression. Example: PrintString("Hello " + var);.
Definition EnScript.c:344
FileMode
Definition EnSystem.c:383
proto void CloseFile(FileHandle file)
Close the File.
proto FileHandle OpenFile(string name, FileMode mode)
Opens File.
proto void FPrintln(FileHandle file, void var)
Write to file and add new line.
static proto float Log2(float x)
Returns the binary (base-2) logarithm of x.
static proto float Pow(float v, float power)
Return power of v ^ power.
const string CFG_VEHICLESPATH
Definition constants.c:209
const string CFG_WEAPONSPATH
Definition constants.c:210
const string CFG_MAGAZINESPATH
Definition constants.c:211