- Joined
- Nov 7, 2014
- Messages
- 571
Recipe (the name of the library) allows you to declaratively define your item recipes (as opposed to writing [v]Jass)
using JSON (JavaScript Object Notation).
A simple tool (an .html page) is provided that converts the recipes into ordinary jass.
Recipe tries to be fast (the generated jass code is only assignments to an array, which is processed by 2 functions).
In order to use Recipe you have to include the following code into your map:
After that you have to come up with some recipes and write them in the following manner
Notice that each recipe has a list of "ingredients", i.e what the recipe requires
and a list of products, i.e what the recipe produces when all the ingredients are present in the unit's inventory.
Each ingredient is a simple "object" with 2 properties
Each product has a type (and other properties depending on the type).
There are currenly two types of products: 'item' and 'random-set'.
A product with type of 'item' has the properties:
A product with type of 'random-set' has the properties:
My suggestion is to write the recies in your favourite editor that has
syntax highlighting for javascript (as opposed to directily in the right
box in wc3-item-recipe.html).
After you are done writing your recipes you use the wc3-item-recipe.html "tool"
to convert them to jass (it should be self explanatory how to use it).
And the last step is to copy the generated jass code and put it in your.
Make sure it get's executed before you call Recipe_evaluate.
PS:
wc3-item-recipe.html is uploaded as wc3-item-recipe.txt
Simply rename wc3-item-recipe.txt to wc3-item-recipe.html and open it with your favourite browser.
using JSON (JavaScript Object Notation).
A simple tool (an .html page) is provided that converts the recipes into ordinary jass.
Recipe tries to be fast (the generated jass code is only assignments to an array, which is processed by 2 functions).
In order to use Recipe you have to include the following code into your map:
JASS:
library Recipe
globals
/* export */ integer array RECIPE
// product types
// public means those vars can be referenced outside of the library
// with Recipe_<var-name>
public integer ITEM = 1
public integer RANDOM_SET = 2
// RANDOM_SET methods to choose the items from the set
// pick means that once an item was picked it can't be
// picked again; kind of like an urn with balls, i.e
// you pick a ball and never return it back
//
public integer RANDOM_SET_PICK = 1
// the metaphor for roll comes from rolling a die (singular for dice)
// if you roll it 2 times you could get the same number
//
public integer RANDOM_SET_ROLL = 2
private boolean array random_set_item_was_picked
endglobals
public function merge_charge_items takes unit u returns nothing
local integer inventory_size = UnitInventorySize(u)
local integer current_item_index
local item current_item
local integer current_item_id
local item matching_item
local integer matching_item_index
local integer total_charges
set current_item_index = 0
loop
exitwhen current_item_index >= inventory_size
set current_item = UnitItemInSlot(u, current_item_index)
if current_item != null and GetItemCharges(current_item) > 0 then
// Find all items with ids matching that of current_item,
// sum up their charges and remove them. Then set
// the charges of the current_item to the sum of all charges.
set current_item_id = GetItemTypeId(current_item)
set total_charges = GetItemCharges(current_item)
set matching_item_index = current_item_index + 1
loop
exitwhen matching_item_index >= inventory_size
set matching_item = UnitItemInSlot(u, matching_item_index)
if matching_item != null and GetItemTypeId(matching_item) == current_item_id then
set total_charges = total_charges + GetItemCharges(matching_item)
// The unit drops the item but it doesn't get removed from the game.
// call UnitRemoveItemFromSlot(u, matching_item_index)
// Same.
// call UnitRemoveItem(u, matching_item)
// Removes the item from the unit's inventory and from the game,
// which makes sense.
call RemoveItem(matching_item)
endif
set matching_item_index = matching_item_index + 1
endloop
call SetItemCharges(current_item, total_charges)
endif
set current_item_index = current_item_index + 1
endloop
set current_item = null
set matching_item = null
endfunction
public function evaluate takes unit u returns boolean
local integer recipe_index
local integer recipe_length
local integer ingredient_index
local integer ingredient_id
local integer ingredient_charges
local integer ingredients_count
local boolean ingredient_matched
local boolean ingredients_matched
local integer ingredients_end_index
local integer product_index
// local integer products_count
local integer products_end_index
local integer product_type
local integer product_id
local integer product_charges
local item product_item
local integer inventory_size = UnitInventorySize(u)
local integer current_item_index
local item current_item
local integer current_item_charges
local integer charges_diff
local integer random_set_index
local integer random_set_items_count
local integer random_set_method
local integer random_set_item_base_index
local integer random_set_item_id
local integer random_set_item_charges
local item random_set_item
local integer random_set_pick
local integer random_set_roll
local integer random_set_item_was_picked_index
call merge_charge_items(u)
set recipe_index = 0
loop
set recipe_length = RECIPE[recipe_index]
exitwhen recipe_length == 0
set ingredients_count = RECIPE[recipe_index + 1]
set ingredients_end_index = recipe_index + 2 + 2 * ingredients_count
set ingredients_matched = true
set ingredient_index = recipe_index + 2
loop
exitwhen ingredient_index >= ingredients_end_index
set ingredient_id = RECIPE[ingredient_index]
set ingredient_charges = RECIPE[ingredient_index + 1]
set ingredient_matched = false
set current_item_index = 0
loop
exitwhen current_item_index >= inventory_size
set current_item = UnitItemInSlot(u, current_item_index)
if current_item != null and GetItemTypeId(current_item) == ingredient_id and GetItemCharges(current_item) >= ingredient_charges then
// next ingredient
set ingredient_matched = true
exitwhen true
endif
set current_item_index = current_item_index + 1
endloop
if not ingredient_matched then
// next recipe
set ingredients_matched = false
exitwhen true
endif
set ingredient_index = ingredient_index + 2
endloop
if ingredients_matched then
// debug call say("recipe matched")
// Goood, the recipe matched. Now remove
// the ingredients of the recipe from the unit
// and add the products of the recipe to it.
set ingredient_index = recipe_index + 2
loop
exitwhen ingredient_index >= ingredients_end_index
set ingredient_id = RECIPE[ingredient_index]
set ingredient_charges = RECIPE[ingredient_index + 1]
set current_item_index = 0
loop
exitwhen current_item_index >= inventory_size
set current_item = UnitItemInSlot(u, current_item_index)
if current_item != null and GetItemTypeId(current_item) == ingredient_id and GetItemCharges(current_item) >= ingredient_charges then
set current_item_charges = GetItemCharges(current_item)
set charges_diff = current_item_charges - ingredient_charges
if charges_diff == 0 then
// We had just enough charges for the recipe.
call RemoveItem(current_item)
else
// We have some remaining charges.
call SetItemCharges(current_item, charges_diff)
endif
// next ingredient
exitwhen true
endif
set current_item_index = current_item_index + 1
endloop
set ingredient_index = ingredient_index + 2
endloop
// set products_count = RECIPE[ingredients_end_index]
set products_end_index = recipe_index + recipe_length
set product_index = ingredients_end_index // + 1
loop
exitwhen product_index >= products_end_index
set product_type = RECIPE[product_index]
if product_type == ITEM then
set product_id = RECIPE[product_index + 1]
set product_charges = RECIPE[product_index + 2]
set product_index = product_index + 3
set product_item = UnitAddItemById(u, product_id)
call SetItemCharges(product_item, product_charges)
elseif product_type == RANDOM_SET then
set random_set_items_count = RECIPE[product_index + 1]
set random_set_method = RECIPE[product_index + 2]
if random_set_method == RANDOM_SET_ROLL then
set random_set_roll = RECIPE[product_index + 3]
set random_set_index = product_index + 4
set product_index = product_index + 4 + 2 * random_set_items_count
loop
exitwhen random_set_roll == 0
set random_set_roll = random_set_roll - 1
set random_set_item_base_index = random_set_index + 2 * GetRandomInt(0, random_set_items_count - 1)
set random_set_item_id = RECIPE[random_set_item_base_index]
set random_set_item_charges = RECIPE[random_set_item_base_index + 1]
set random_set_item = UnitAddItemById(u, random_set_item_id)
call SetItemCharges(random_set_item, random_set_item_charges)
endloop
elseif random_set_method == RANDOM_SET_PICK then
set random_set_pick = RECIPE[product_index + 3]
set random_set_index = product_index + 4
set product_index = product_index + 4 + 2 * random_set_items_count
// reset the random_set_item_was_picked array
set random_set_item_was_picked_index = random_set_index
loop
exitwhen random_set_item_was_picked_index >= product_index // points to the start of the next product
set random_set_item_was_picked[random_set_item_was_picked_index] = false
set random_set_item_was_picked_index = random_set_item_was_picked_index + 2
endloop
loop
exitwhen random_set_pick == 0
set random_set_item_base_index = random_set_index + 2 * GetRandomInt(0, random_set_items_count - 1)
if not random_set_item_was_picked[random_set_item_base_index] then
set random_set_item_was_picked[random_set_item_base_index] = true
set random_set_pick = random_set_pick - 1
set random_set_item_id = RECIPE[random_set_item_base_index]
set random_set_item_charges = RECIPE[random_set_item_base_index + 1]
set random_set_item = UnitAddItemById(u, random_set_item_id)
call SetItemCharges(random_set_item, random_set_item_charges)
endif
endloop
endif
endif
endloop
// TODO: come up with recipes that cascade and implement the
// recursive step.
//
// Because this recipe matched and the unit recived the products
// of the recipe, it is possible that another recipe will
// match as well, i.e it could cascade, so recurse?
//
// It turns out we are already recursive because we are called
// every time a unit picks up an item and that includes the
// time when we give the unit a recipe's products.
// So every time we call UnitAddItemById(...) we recurse.
// A recipe matched no need to check the other recipes.
exitwhen true // last recipe
else
// debug call say("recipe didn't match")
// This recipe didn't match, try the next one.
set recipe_index = recipe_index + recipe_length
endif
endloop
set current_item = null
set product_item = null
set random_set_item = null
return false
endfunction
endlibrary
After that you have to come up with some recipes and write them in the following manner
JASS:
[
{ ingredients: [{ id: 'phea', charges: 3 }],
products: [{ type: 'item', id: 'pghe', charges: 1 }]},
{ ingredients: [{ id: 'mlst' },
{ id: 'engs' }],
products: [{ type: 'item', id: 'mlst' },
{ type: 'random-set',
set: [{ id: 'hlst', charges: 1 },
{ id: 'shar', charges: 1 },
{ id: 'infs', charges: 1 },
{ id: 'mnst', charges: 1 }],
pick: 3 }]},
]
Notice that each recipe has a list of "ingredients", i.e what the recipe requires
and a list of products, i.e what the recipe produces when all the ingredients are present in the unit's inventory.
Each ingredient is a simple "object" with 2 properties
JASS:
{
id: '<id-of-the-item>',
charges: <number-of-charges> // this property is optional, defaults to 0
}
Each product has a type (and other properties depending on the type).
There are currenly two types of products: 'item' and 'random-set'.
A product with type of 'item' has the properties:
JASS:
{
type: 'item',
id: '<id-of-the-item>',
charges: <number-of-charges> // optional, defaults to 0
}
A product with type of 'random-set' has the properties:
JASS:
{
type: 'random-set',
set: <a-list-of-set-items>,
// A "set-item" is similiar to an ingredient, i.e
// it has only two properties: id and charges
// The 3rd propery of a random-set can be either "pick", or "roll"
pick: <number-of-items-to-pick> OR roll: <number-of-items-to-roll>
// pick means that once an item was picked it can't be
// picked again; kind of like an urn with balls, i.e
// you pick a ball from the urn but never return it back
// the metaphor for roll comes from rolling a die (singular for dice)
// if you roll it 2 times you could get the same number
}
My suggestion is to write the recies in your favourite editor that has
syntax highlighting for javascript (as opposed to directily in the right
box in wc3-item-recipe.html).
After you are done writing your recipes you use the wc3-item-recipe.html "tool"
to convert them to jass (it should be self explanatory how to use it).
And the last step is to copy the generated jass code and put it in your.
Make sure it get's executed before you call Recipe_evaluate.
JASS:
library YourMap initializer init uses Recipe
private function init_recipes takes nothing returns nothing
// make sure the generated jass code is runned before you call Recipe_evaluate(...)
// I.e copy-paste the generated jass code here... for example
endfunction
private function on_item_pickup takes nothing returns nothing
local unit u = GetTriggerUnit()
// Recpe_evaluate takes a unit and tries to match the items
// in the unit's inventory to the ingredients of all the recipes.
// If a recipe is found the unit recives the products of the recipe.
call Recipe_evaluate(u)
// There is one more function that you might want to use
// call Recipe_merge_charge_items(u)
// If a unit has multiple items with the same id
// it sums their charges and leaves only one of them with
// charges equal to the sum.
set u = null
endfunction
private function init takes nothing returns nothing
local trigger t
call ExecuteFunc(SCOPE_PRIVATE + "init_recipes")
set t = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(t, P, EVENT_PLAYER_UNIT_PICKUP_ITEM, null)
call TriggerAddAction(t, function on_item_pickup)
endfunction
endlibrary
PS:
wc3-item-recipe.html is uploaded as wc3-item-recipe.txt
Simply rename wc3-item-recipe.txt to wc3-item-recipe.html and open it with your favourite browser.