Groups of things and duplicates

Does anyone know how to do the following concisely re duplicates?
I want to have multiple items in a location, on a player, on an NPC. Example: An orc fires an arrow, of which he has 10. Player carries 15 arrows, and the location contains 3 arrows, and increases each time an arrow is fired and drops to the floor.
These arrows are all identical and differ only in where they are.

What I am doing now is kludgy and awkward. Pseudocode might look like this:

An arrowType is a kind of thing. An arrow has a property called qty. 
Printed name of arrow is "[qty of arrow] arrows.".
Arrow is an arrowType. 1 Arrow (qty = 20) is at location Store.
MyArrow is an arrowType. Player carries 1 MyArrow (qty = 10).
1 arrow is nowhere (out of play) or a virtual space storeroom for later.
It might be called itsArrow for an arrow that belongs to a third entity, neither player nor location.

Currently, when I buy arrows, I convert each arrow to MyArrow and adjust the qty property of myArrow and the Store’s arrow. If I sell myArrow, I convert it to arrow in location Store and adjust both qty properties.

Surely there is a better way!

Here’s my current understanding: if your game uses objects in large quantities, such as arrows, cannonballs or coins, you’re right to assign a numeric property to each holder, because beyond a certain number of objects the game runs the risk of not being able to compile. So you need phrases associated with the action processing rules buying/selling/throwing… to keep track of these objects and maintain the counters.

Duplicates in the Inform sense are relevant when they are linked to a recycling logic. For example: the character can search for and consume quite rare medicinal herbs. These herbs move between nowhere (or an inaccessible system room called the herbarium) and the places where they are discovered as they are collected and consumed.

1 Like

That’s actually efficient enough—certainly better than duplicate arrow things (although Inform is capable of that). What would be better would be to have only arrow counters and one arrow, since the player will only interact with one arrow (if they examine it, etc. otherwise you can just update the counters).

I hope I’m understanding your question correctly, but you should be able to use arrow as a kind of thing, make duplicates of the kind, distribute them out, and then group the arrows in listing for tidy descriptions.

Here’s a sample of what I mean. I’m only going to define arrow as a kind of thing, but give the orc 10, give the player 15, and let 3 be in the room. Then I’ll move an arrow from the orc to the ground of the lab.

Code:

Lab is a room.

Orc is a man in the lab.

An arrow is a kind of thing.

Orc carries 10 arrows.
The player carries 15 arrows.
In the lab are 3 arrows.

Before listing contents:
	group arrows together.

When play begins:
	say "The orc has [list of arrows carried by orc].";
	say "The player has [list of arrows carried by the player].";
	say "The Lab has [list of arrows in Lab].";
	let orcArrow be a random arrow carried by orc;
	move orcArrow to Lab;
	say "Now the Lab has [list of arrows in Lab].";
	say "Now the orc has [list of arrows carried by orc].".

Output prior to the initial room description

The orc has ten arrows.
The player has fifteen arrows.
The Lab has three arrows.
Now the Lab has four arrows.
Now the orc has nine arrows.

You should be able to just have arrows be a kind of thing, and then specify phrases that will accurately distinguish which arrows are which. The key additional thing is just tidiness in listing contents:

Before listing contents:
	group arrows together.

You can read more about this activity in WI 18.13 in the documentation.

This also applies to when you look at your inventory after play begins in this example, the arrows are grouped together when describing them:

Lab
You can see Orc and four arrows here.

>i
You are carrying:
  fifteen arrows
1 Like

I guess I should also emphasize that this works best when all copies of an arrow are indistinguishable from each other. If you need some kind of variety of arrow, you may need to specify properties for the arrow kind in that case. Like, say, you need to distinguish between an arrow being broken or unbroken.

@walktothesun You can edit a post after publishing it using the pencil icon in the lower right corner of the post box.

1 Like

I appreciate the tip. My original reply actually had a few quick edits using this method. While my follow up comment could have been edited in, it was a judgment call to me, and I felt like it needed its own post to share as an afterthought.

This is unfortunately not something that Inform handles especially well. I don’t see an obvious better way to do it; you’re just prodding at a particularly weak point of the language.

OK, I get that. I’m going to try Parson’s response and see if that improves things a little. Thanks.

First, thanks for all the work you go to in trying to help me. Second, I’ll try your approach. I thought I had done something similar and it didn’t work (esp. the grouping together stuff–it removed it from player’s inventory to put into room) but I’ll follow your example and be more precise.

Your code parses and works fine. I need to understand the arrow is a kind of thing vs. an arrow. Usually I get a specificity arrow from the parser, but this works fine.
If I enter SHOWME ORC it lists each and every arrow. Grouping didn’t seem to affect the listing.
I think the arrow (type) is used as the arrow (thing), so the ambiguity is swallowed up by the output. I cannot put an arrow into a table else I get the specificity (can’t put kinds of things) in tables; they require constants, like arrow (thing).

Do you know how I can put an arrow (thing) into a table without the parser thinking it is an arrow (kind)?

I want to initialize the properties of my equipment, weapons, and armor with tables, but Parson’s code–although works fine for duplicates–won’t allow me to put a kind of thing into a table. How do I specify an arrow (kind) from an arrow (thing) for the table?
Here is Parson’s code augmented with mine.

Lab is a room.
Orc is a man in the lab.

A thing has a number called qty.
A thing has a real number called cost.
A thing has a real number called weight.

An arrow is a kind of thing. 
An xbow bolt is a thing. 

Orc carries 10 arrows. 
The player carries 15 arrows.
In the lab are 3 arrows.
In the lab are 9 xbow bolts.

Before listing contents:
	group arrows together.

When play begins:
	init Table of Arms;
	showme the contents of Table of Arms;
	say "The orc has [list of arrows carried by orc].";
	say "The player has [list of arrows carried by the player].";
	say "The Lab has [list of arrows in Lab].";
	let orcArrow be a random arrow carried by orc;
	move orcArrow to Lab;
	say "Now the Lab has [list of arrows in Lab].";
	say "Now the orc has [list of arrows carried by orc].".
	
Table of Arms
Item	Nbr	Cost	Weight
arrow 	12	0.2	0.2
xbow bolt 	20	0.5 	0.3

To assign traits to (LocalItem - a thing) consulting (LocalTable - a table name):
	choose row with an item of Localitem in Localtable;
	now qty of LocalItem is nbr entry;
	now cost of LocalItem is cost entry;
	now weight of LocalItem is weight entry;

To init (tbl - table name):
	repeat through tbl:
		let item be Item entry;
		assign traits to item consulting tbl;

The xbow bolt works fine but I get the error on arrow.

There are a few things here to speak to.

You will need to be careful about how you define arrows as things if you’re also trying to work with duplicates of kinds. If you try to say The player carries 15 arrows and arrow is not a kind, then Inform will literally create one object named 15 arrows and it won’t be what you might assume it is (15 separate arrows).

Lab is a room.

The player carries 15 arrows.
>i
You are carrying:
  15 arrows

>x 15 arrows
You see nothing special about 15 arrows.

>showme 15 arrows
**15 arrows - thing**
location: carried by yourself in Lab
unlit, inedible, portable, handled; singular-named, proper-named
description: none
initial appearance: none
printed name: "15 arrows"
printed plural name: none
indefinite article: none
list grouping key: none

“15 arrows” is just a single thing (as confusing as that is to read).

WI 4.14 talks about this trap

Firstly, a counted-out description like “two squares” is only allowed if it combines a number with the name of a kind which is already known (perhaps modified with adjectives, so “two open doors” is fine). If we say:

Two circles are in the Lab.

without having defined “circle” as a kind in advance, then only a single object will be created - whose name is “two circles”. (This is because many natural names start with numbers: “six of clubs”, for instance, referring to a single playing card, or “12 Hollywood Close” meaning a single house. We wouldn’t want such names to be misinterpreted.)

Secondly, if you want to specify a uniform weight and cost for a kind, then I think the technique found here from @Monsieur.HUT may help. Since you are probably not going to be able to specify a kind as a value on a row in a table, you may be able to work around this by giving each kind a unique ID and then pulling the cost/weight out of the table as needed.

Lab is a room.

Orc is a man in the lab.

Ammunition is a kind of thing.
Ammunition has a number called uniqueID.

An arrow is a kind of ammunition. The uniqueID of an arrow is 1.
A xbow bolt is a kind of ammunition. The uniqueID of a xbow bolt is 2.

The player carries 15 arrows.
Orc carries 10 arrows.

The wooden table is in the Lab. The wooden table is an open container.
	
Table of Arms
UID	cost	weight
1	0.2	0.2 [arrow]
2	0.5	0.5 [xbow bolt]

When play begins:
	say "The orc has [list of arrows carried by orc].";
	choose row with UID of 1 in the Table of Arms; [UID 1 = arrow]
	say "The orc's arrows weigh [the number of arrows carried by Orc times weight entry].";
	say "The player's arrows weigh [the number of arrows carried by the player times weight entry].".

Report inserting something into the wooden table:
	choose row with UID of 1 in the Table of Arms; [UID 1 = arrow]
	say "The player's arrows now weigh [the number of arrows carried by the player times weight entry].".
The orc has ten arrows.
The orc's arrows weigh 2.0.
The player's arrows weigh 3.0.

[...]

Lab
You can see Orc and a wooden table (empty) here.

>put arrow in wooden table
The player's arrows now weigh 2.8.

You put the arrow into the wooden table.

The issue of course is that you will need to look up the cost and weight from the table with a hard-coded unique ID.

The main gist of the workaround is to try to preserve using duplicates of kinds without resorting to making every specific instance of a kind (the player’s arrows, the orc’s arrows, etc.). You’ve probably already seen this, but you can’t define a kind, give the kind properties (cost as a real number, weight as a real number, etc.), and then specify precise values for those properties. (If arrow is a kind, and arrow has a real number called cost, the arrow kind cannot have a cost of 0.2)

So by moving the cost/weight properties to the Table of Arms, you would do table lookups any time you need to refer to cost or weight of the kind rather than setting the properties on a thing.

Edit: You’ll notice I’ve also done a bit of indirection too. I’ve defined ammunition as a kind of thing, and given the unique ID to the ammunition kind. I think then if you create kinds of ammunition, it allows you to define the unique ID property specifically.

Edit 2: Continuing the previous thought, with that same indirection, you could make cost and weight properties of the ammunition kind, and then set the properties of the arrow and xbow bolt kinds to specific cost and weight values. However, you will probably run into issues when you then need to refer to the cost and weight of an arrow. So given a choice, it’s probably preferable to use table lookups to define and retrieve these property values over having them be set as properties directly.

1 Like

To improve on this, and to be able to refer directly to the property of a duplicate (or even a single object, since the technique also works for creating single objects using tables), I run a series of phrases at the start of the game that complete the properties of the things. I use ID 10000 to separate the kinds from unique objects, but this is arbitrary.

To complete the settings for weapon kinds:
	repeat with LocalAsset running through things which are generic-weapon:
		let LocalUID be the UID of LocalAsset;
		if (LocalUID < 10000):
			choose a row with an UID of LocalUID in the Table of Assets-Weapons;
			now the printed plural name of LocalAsset is printed plural name entry;
			now the description-addon of LocalAsset is the description-addon entry;
			now the HPDamageMax of LocalAsset is the HPDamageMax entry;
			now the material of LocalAsset is the material entry;
			now the height of LocalAsset is the height entry;
			now the width of LocalAsset is the width entry;

In this way, all duplicates created will have the properties of their kind by default.

(Every time we talk about this topic, I use examples that relate to weapons or armour, but that’s to keep with the tone of the thread… my WIP is not a roguelike).

In my experience with multitudes of a kind, Inform 7 can do it, but you do want to plan so you don’t have an absurd number of objects the parser and the player needs to keep track of. Above a certain number you’ll have a much easier time by using a variable and abstracting the process of handling, trading, dropping, grouping, and most importantly disambiguating single, numbers, and all of a multitude of a kind of item.

Baker of Shireton required physical coins because they were a currency and also a physical puzzle solution. Since the player is selling bread, NPCs would pay with different numbers of coins. I found what seemed to work was having 50 physical coins existing in the world and I made up lore and automatic processes to justify a reason that the player would routinely have to clear the entire environment of coins that would invariably end up on tables, in the tip jar, in inventory so they could be recycled so the NPCs would keep having them to buy with. If they didn’t do it, the game would know when it was running low on coins and force the Baker to reflexively clear the area of coins automatically.

This game in all respects is very frustrating for a player. One of the intricate things to work out was making it possible to TAKE COIN, TAKE FIVE COINS, TAKE ALL COINS, PUT ALL COINS IN TIP JAR.

Similarly, the Baker makes bread, so there were something like 25 balls of dough (recycling multitudes) that would take adjectives that changed over time “risen” or “sourdough” that were swapped in the oven with one of 25 total “baked” bread or “burned” bread so the player could have bread baking while still making dough. Again, I made up reasons to set a limit - the rolling table could only hold 25 balls of dough. You could only fit so many loaves of bread in the display case. At one point I thought I was going to require bread to be in one of 25 pans to bake correctly, but that caused a major problem that I didn’t want to deal with and I eliminated the bread pans. PUT ALL LOAVES OF HOT SOURDOUGH BREAD IN THE DISPLAY CASE. PUT ALL LOAVES OF BURNED BREAD IN THE WASTE BIN.

I got everything to mostly work, and it was worth it for a game about baking and selling bread and on a meta level was a Brobdingnagian inventory management simulator. My advice is if you need large numbers of duplicate items is to take care that your entire game doesn’t become Arrow Simulator 2024 unless the archery is very important to the story.

3 Likes

[1] I have already encountered the disappointing result of creating “15 arrows” as a single thing bc I forgot to define a kind for arrows. :roll_eyes:
[2] This code example is extremely helpful. It clarifies duplicates, explains some concepts I have been missing, and shows how to manipulate them in a more native way. Since I have many more properties per kind than I showed in the example, I think this idea will speed my coding. The downside is that when a player wants to buy an item from a table of merchandize, the player sees an unrecognizable UID instead of the actual thing. Is there a way around that?
[3] Per your Edit 2, I have been able to specify properties to kinds and to subkinds. For example: Equipment is a kind of thing with several properties; e.g.Weapons, Armor, and Outfitting are kinds of Equipment with additional more-specific properties. Armor has an AC value; Weapons have a Damage value, etc.

Question: Doesn’t the properties of kinds carry down to all its things? Like Armor is a kind of thing. Armor has an AC of 13. Leather armor is a kind of armor.
Now all leather armor has an AC of 13.
It seems to be the case for me (but then this is not actual code.)

Yes, absolutely, but there’s nothing to stop you from writing:

The AC of a leather armor is usually 7.

In this way, the value of the AC property of the Leather armor kind will be substituted for the value of the AC property of the higher-level kind armor.

Good tip (and I appreciate the humor). My dilemma now is either (1) using native duplicates and restricting numbers of the same things; or (2) using a quantity property of the kind and creating only five things bc the player can only experience a max of five things this way. Printed name of arrow is “[qty of arrow] arrows.” They are stored out of play until needed.
For example, I can have an arrow with qty, so that one arrow (qty = 5) is carried by the player; another arrow (qty = 30) is carried by the store at the location; a third arrow is (say, on the floor) in the location (qty = 1), and two other arrows can be carried by NPCs (qty = 10 and 14 respectively). If I have another NPC or two, I add another arrow or two for retrieval.

Edit 1: It seems that if I use native duplicates, which are easier to manipulate, I still need to move them into and out of play as the location changes. I mean that Ganon’s Emporium has 30 arrows, but when I get to the Fighters’ Guild, which also sells/buys arrows, I can move Ganon’s 30 to the Guild (to keep the absolute number of arrow objects small). If I have to move things in and out of play anyway, it seems my option (2) above is better. Thoughts?