HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

What would be a better API for a recipe system?

09-06-2011, 10:06 AM#1
Anitarf
Right now, I am deciding between two different approaches. The first is the standard one with methods for adding items to the recipe:
call ItemRecipe.create(result).add(item1).add(item2).addCharges(item3, 60)
However, I also had an idea for a second approach where I abuse the [] operators. This results in much less verbose recipe creations, however due to the operator abuse you can't simply prefix all your recipe creations with "call":
set i = ItemRecipe.create(result)[item1][item2]
set ItemRecipe.create(result)[item1][item2][item3]=60
The brackets at the end look nice but the start of the line is just bad, especially since it's different for recipes that contain a charges requirement.

Which of the two do you think is better? Can you think of an alternative that is better than both? Keep in mind that the API needs to support both adding item count requirements and item charges requirements.
09-06-2011, 11:00 AM#2
Bribe
Keep it with operators, people who don't like the "set ItemRecipe.create(result)" API should do this:

local ItemRecipe recipe = ItemRecipe.create(result)
set recipe[item1][item2][item3] = 60
09-06-2011, 11:53 AM#3
BBQ
"Abusing" operators in such way brings way too much abstraction to the code. I'd stick with the first API.
09-08-2011, 05:53 AM#4
jerryme
Thanks to share your ideas! :)
09-14-2011, 05:10 AM#5
Yrth
I like both pretty equally
the first one seems a bit easier to understand what exactly is happening for a new user
the second one seems like it'd be easier for people who just want it to work and aren't very programming savvy. then again, the sytax (on API 2) for a charged item might be too weird for some people.
09-16-2011, 05:56 PM#6
Anitarf
I decided to use the first API after I started writing the documentation. I could find no simple way of explaining all those syntax exceptions that come with the operator approach. If I require multiple paragraphs and examples to describe my API, then it's a bad API.

New question: what's the preferable approach for charged-item-stacking-on-pickup-even-if-inventory-is-full? There are two options:
  • Any charged item that's dropped on the ground hidden and replaced by a powerup item so the heroes can pick it up even with a full inventory. Once a hero picks it up, triggers check if the hero already has an item of the same type, in which case charges from the hidden item are added to it, else if the hero has an empty inventory slot the hidden item is added to it, else the powerup item is recreated.
  • Any item pickup order targeting a charged item that is issued to a unit with an already full inventory is checked by a trigger, if the ordered unit already has an item of the same type in its inventory then it is ordered to move to the position of the target item instead. A periodic timer then checks the unit's distance to the target until it reaches it, at which point the trigger transfers the item's charges to the unit and removes it (provided the item has not already been picked up by someone else in the meantime and the unit still has an item of the same type in its inventory).
The first approach requires an additional powerup version of each item that is stackable in this way and users have to specify such item pairs to the system. The second approach requires periodic distance checks, also it doesn't play the item pickup sound since the "picking up" of the item is entirely triggered, wonder if any of that is important though.
09-16-2011, 10:25 PM#7
tooltiperror
RecipeAddItem minority reporting in.
09-17-2011, 01:10 AM#8
Fledermaus
Quote:
Originally Posted by Anitarf
Any charged item that's dropped on the ground hidden and replaced by a powerup item so the heroes can pick it up even with a full inventory. Once a hero picks it up, triggers check if the hero already has an item of the same type, in which case charges from the hidden item are added to it, else if the hero has an empty inventory slot the hidden item is added to it, else the powerup item is recreated.
This.
09-17-2011, 03:42 AM#9
watermelon_1234
^This. The first option won't bother the user if they don't want to implement it.
09-17-2011, 07:03 PM#10
Yrth
I like option 1 much, much more

duplicate dummy items aren't that much of a big deal considering how rarely charge items are used anyways.
in most rpg's its pretty much only potions and those are bought (are bought charged items handled as well?)
in anything else there aren't usually enough items (much less charged ones) for this to be a hassle.
09-17-2011, 10:56 PM#11
Anitarf
Quote:
Originally Posted by Yrth
in most rpg's its pretty much only potions and those are bought (are bought charged items handled as well?)
Well, the idea was that users would make their shops sell the powerups instead of the original items. Of course, a powerup bought from a shop would not have a hidden charged item attached to it the way a powerup created when a charged item is dropped would, so we need an alternative way of knowing how much charges of the charged item the unit should get. I can think of two ways:
  • When users specify a new charged item/powerup pair, the system creates one instance of the charged item type to see what its default charges are, removes it and then uses this default value whenever a powerup without an attached charged item is obtained.
  • Users need to specify the default number of charges when they define a new charged item/powerup pair. This is a bit more work, but it makes it possible to define multiple pairs that use the same charged item, so a shop could sell either a single potion or ten potions at a discount price, for example.
09-18-2011, 10:36 PM#12
BBQ
Quote:
Originally Posted by Anitarf
  • Users need to specify the default number of charges when they define a new charged item/powerup pair. This is a bit more work, but it makes it possible to define multiple pairs that use the same charged item, so a shop could sell either a single potion or ten potions at a discount price, for example.
This. Without a single doubt.
09-22-2011, 04:27 PM#13
Anitarf
Thank you all for your feedback. If anyone's interested, this is what I've come up with so far.
09-23-2011, 09:18 PM#14
Yrth
this is awesome
it actually makes me want to do an rpg style game just so i can play with item recipes

Quote:
//* The .addCharges method allows you to specify a required number
//* of item charges rather than a required number of items for the
//* recipe.
the wording of this is somewhere between confusing and inaccurate.
the first time i read it as: this recipe will now be based on charged items only
when i reread it, i understood what you were going for but i still think the wording is confusing.
howbout something like:
//* The .addCharges method functions similarly to .add, however it requires that the given //* item (ingredientItemId) have at least a certain amount of charges (chargeCount)

also this next recommendation might make you cry tears of blood, so i apologize in advanced.
it might be better to have the AUTOMATICALLY_MERGE_ITEMS bool be part of the recipe rather than a global for the entire system.
I've seen a lot of maps (and see there being a lot more) that auto merge basic items, but require a trip to, as a generic example, a forge for the more advanced stuff.

a step further would be to add a way to differentiate which items combine at what "forge".
for example say you want a forge and a factory
recipe 1 combines only at the forge
and recipe 2 only at the factory
as is, you'd call .checkUnitItems(u) and everything would come together, regardless of the unit being at forge or factory.

but even though I think this would be cool to have as a feature, its use would probably be seldom and it might very well cause some ugly code.

I guess a way around the first part (global vs member auto-combine) would be to add every item in a non-auto-combine recipe to the item filter
but it feels like the member example would be much prettier

I'll add some more once I throw all this stuff in a test map (or you upload yours :D) when i get home later on
09-24-2011, 08:32 AM#15
Anitarf
Quote:
Originally Posted by Yrth
the wording of this is somewhere between confusing and inaccurate.
the first time i read it as: this recipe will now be based on charged items only
when i reread it, i understood what you were going for but i still think the wording is confusing.
howbout something like:
//* The .addCharges method functions similarly to .add, however it requires that the given //* item (ingredientItemId) have at least a certain amount of charges (chargeCount)
The charges may be distributed among multiple items, though. Your wording seems to suggest that a single item in the unit's inventory must have that many charges for the recipe to compile and that you can make a recipe require multiple such charged items, which is not the case. The system will only look at the sum of charges on all items of that type in a unit's inventory.

Quote:
it might be better to have the AUTOMATICALLY_MERGE_ITEMS bool be part of the recipe rather than a global for the entire system.
I've seen a lot of maps (and see there being a lot more) that auto merge basic items, but require a trip to, as a generic example, a forge for the more advanced stuff.
If the auto recipes and the forge recipes don't use the same items, you could fix this in the item filter. In that case, then the forge wouldn't auto-combine items, so you'd need to give it an ability that triggers a full recipe check, which I think is how you'd want it to work anyway. However, if you want the forge to auto-combine too, I could merge the unit and item filters so that you could make some items auto-combine if picked up by a forge, but not if picked up by anybody else.

However, if some items are shared by both forge recipes and auto recipes, none of that would help. I could add the boolean to individual recipes to not make them auto-combine, but that still leaves the problem of forges being able to combine non-forge recipes when you use their forge ability.

The simplest solution I see to this problem is to simply paste an additional copy of the entire system into the map, rename the struct into ForgeRecipe, change its unit filter so it only works with forges, and create all your forge recipes there. You can then copy it again for the factory recipes and so on. I could still merge the two filters so that you can use a single copy of the system for multiple units when there's no overlap in ingredients, though.