en:authors_main:task_editor:advanced:inventory

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
en:authors_main:task_editor:advanced:inventory [2026/06/15 21:31]
127.0.0.1 внешнее изменение
en:authors_main:task_editor:advanced:inventory [2026/09/18 06:51] (current)
Line 2: Line 2:
 ====== Player Inventory ====== ====== Player Inventory ======
  
-The QEng engine supports a virtual ​inventory ​system for teams or single players. This allows authors to design quests that involve finding and using virtual ​items (keys, tools, currency).+The inventory ​is a bag of items the player finds during the game and applies to things on the level: a key to a lock, a crowbar to a lid, a coin to a machine. An item is a string key (''​hammer123''​), and everything you do with it comes down to codes: the player applies an item, and the engine submits a code for them.
  
-===== What is Inventory? ===== +There are two versions of the inventory, and they live side by side.
-In the player interface, ​the inventory ​is displayed as a list of items with images, short descriptions, and action buttons.+
  
-{{ :authors_main:​inventory.png?​800 ​|Example ​of inventory display on a level}}+  * **The second** — a panel at the bottom of the screen, a bag with a counter, and items applied to **zones** on the task picture. One line in the task script turns it on, and it asks for no room in the task text. This page is about it. 
 +  * **The first** — a list of items inside a block the author sets aside, with two buttons on an item: the eye and "​use"​. It [[#​the_first_version|works the way it always worked]] and is described at the end of the page; running games are not deprived of it.
  
-Each item has a unique string identifier (key) that can be added, removed, or activated (used).+A level picks one of the two: whichever function the script called is the one that renders.
  
-===== How to Display ​the Inventory ​on a Level =====+===== What the Player Sees ===== 
 + 
 +At the bottom of the screen there is a bag button. It shows the total number of items and, separately, "​+N"​ — how many are new; while there are new ones, the bag shakes. 
 + 
 +{{ :​authors_main:​task_editor:​advanced:​inventory2_bag_en.png?​800 |The collapsed bag: how many items in total and how many of them are new}} 
 + 
 +The button opens a strip of items; new ones are highlighted yellow and stop being new as soon as the player touches them — hovers, taps, or drags. 
 + 
 +{{ :​authors_main:​task_editor:​advanced:​inventory2_panel_en.png?​800 |The open panel: the strip of items at the bottom of the screen, the new item highlighted}} 
 + 
 +There are two ways to apply an item, and they are equal: 
 + 
 +  * **drag** ​the item onto the place of application;​ 
 +  * **click** the item, then click the place of application. 
 + 
 +As soon as the item is "in hand", every place it can be applied to lights up on the level — the **zones**. At the same moment two more receivers appear next to the bag: "​use"​ (no zone, the item on its own) and the eye — look at the item full size. 
 + 
 +{{ :​authors_main:​task_editor:​advanced:​inventory2_zones_en.png?​800 |An item in hand: the zones have lit up on the picture, and next to the bag are the eye and "​use"​}} 
 + 
 +Let go past a receiver or click an empty spot, and the item goes back into the bag. 
 + 
 +What came of the application the player sees on the item itself: it fitted — the item jumps green; it did not — it shakes its head red. If a bonus took the item, it dissolves where it was carried to. Until the answer arrives the item pulses in place. 
 + 
 +The open panel and the point the player scrolled the strip to survive a page reload: a code is entered, the level changes — the bag stays open at the same place. 
 + 
 +The panel is pinned to the bottom of the window instead of standing in the task text, so on a phone the item and the place of application do not have to fit on the screen at once. 
 + 
 +{{ :​authors_main:​task_editor:​advanced:​inventory2_phone_en.png?​400 |A narrow screen: the strip and the receivers are pinned to the bottom and do not scroll away}} 
 + 
 +===== Turning It On ===== 
 + 
 +In the **task script** (the field at the bottom of the level):
  
-1. In the task text (in **"​Source"​** mode), insert an HTML container where the inventory list should be rendered: 
-<code HTML> 
-<div id="​inventory_container">​Loading inventory...</​div>​ 
-</​code>​ 
-2. In the task script field (at the bottom of the level settings), add the JS call: 
 <code JavaScript>​ <code JavaScript>​
-showInventoryItems('​inventory_container'​);+showInventoryItems2({gameId: 1234});
 </​code>​ </​code>​
  
-===== Managing ​Items =====+Nothing else is needed — no block in the task text, no ''​id'';​ the engine draws the panel itself. 
 + 
 +''​gameId''​ is the game whose folder holds the item images. It can be omitted, and then the current game is used — but see [[#​item_images_after_copying_a_game|copying a game]]. 
 + 
 +> **The call must be in the task script.** The engine loads the second version after seeing ''​showInventoryItems2''​ in the level'​s script; from the task text, from a bonus, or from the game's common header it will not load it — the function will simply not exist. 
 +> 
 +> And **there must be exactly one call**: it takes the accumulated list of items, so a second one within the same render draws an empty bag. 
 + 
 +===== Items ===== 
 + 
 +==== Where They Come From ==== 
 + 
 +An item is given out from a bonus'​s **"​Text after solution"​** — in **"​Source"​** mode:
  
-==== Adding an Item to the Inventory ==== 
-Adding items is linked to solving level bonuses (or spanning bonuses). 
-1. Create a bonus on the level (e.g., "Find a hammer"​). 
-2. In the **"​Text after solution"​** field of that bonus, in **"​Source"​** mode, write: 
 <code HTML> <code HTML>
 <​script>​ <​script>​
Line 33: Line 67:
 </​script>​ </​script>​
 </​code>​ </​code>​
-Once the team solves this bonus, the item with key ''​hammer123''​ will appear in their inventory. 
  
-==== Item Images ==== +It is taken away from there too, by a bonus of a later step:
-For the item ''​hammer123''​ to render correctly, upload two image files to the CDN with strict filenames: +
-  * **''​item_hammer123.jpg''​** — the item thumbnail displayed in the inventory list. +
-  * **''​item_hammer123_big.jpg''​** — the detailed full-size image displayed when clicking the eye icon (detailed view).+
  
-==== Removing an Item from the Inventory (Usage or Loss) ==== 
-To remove an item from a team (e.g. when giving it to a character or using it to unlock a door): 
-1. Create a bonus that is solved during the game. 
-2. In its **"​Text after solution"​** field, in **"​Source"​** mode, write: 
 <code HTML> <code HTML>
 <​script>​ <​script>​
Line 50: Line 76:
 </​code>​ </​code>​
  
-<note important> +> The engine runs the texts of solved bonuses **afresh on every redraw of the page**. The list of items is therefore stored nowhere: it is assembled from scratch every time out of what has already been solved. The server knows nothing about items. 
-**Important:** If an item was added via a **spanning** (global) bonus, it cannot be removed ​by a regular ​level bonus. ​It must be removed ​via a spanning ​bonus with a higher sequence number. + 
-</note>+The same fact gives the opposite trick: hand out items not from bonuses but from the **task script itself**, working out from the entered codes what should be in the bag right now. The shelf example level is built that way — a book can be put on the shelf, taken off, and swapped for another, and no separate codes are needed for it. 
 + 
 +> **Spanning bonus.** An item given by a spanning bonus cannot be taken away by an ordinary level bonus — only by a spanning bonus with a higher sequence number. 
 + 
 +**An item does not travel to the next level by itself.** A level'​s bonus does not run on another level, so the item is not there either. A thing carried through the whole game is given out by a **spanning** bonus. 
 + 
 +==== Item Images ==== 
 + 
 +An item's picture is looked up by file name: **''​item_<key>''​** is the icon in the strip, **''​item_<​key>​_big''​** is what the eye opens. 
 + 
 +There is no need to upload both by hand. Upload ​**one** picture named ''​item_hammer123.png''​ to the game's files: if it is **512 pixels or more on both sides**, the engine splits it into the icon and the large version itself. Smaller, and it stays a single file, and the eye shows that same one. 
 + 
 +In the list of uploaded files such a picture gets a ready-made line, ''​addInventoryItem('​hammer123'​)''​ — a click copies it to the clipboard. That is what goes into the bonus: ​an item is connected by its key, not by a file address. 
 + 
 +The engine keeps such a file's name as it is, even with "Keep the name" unchecked: the inventory looks the picture up by name. The key is taken from the name together with its case — it is exactly the string that goes into ''​addInventoryItem()''​. 
 + 
 +=== Format === 
 + 
 +By default the engine looks for ''​.jpg''​. Pictures in another format — including the transparent png the slot and the shadow in the panel were made for — are declared once per game, in the **"​Common HTML header"​**: 
 + 
 +<code HTML> 
 +<​script>​ 
 +setInventoryImageExt('​png'​); 
 +setInventoryBigExt('​png'​);​ 
 +</​script>​ 
 +</​code>​ 
 + 
 +The icon and the large view are declared separately: they may be in different formats. A one-off exception goes as the second argument: 
 + 
 +<code JavaScript>​ 
 +addInventoryItem('​map7',​ {icon: '​png',​ big: '​png'​});​ 
 +</​code>​ 
 + 
 +The extension is **not part of the key**: the key is the code that goes to the server. 
 + 
 +=== Item Images After Copying a Game === 
 + 
 +Item images are taken from the folder of the game whose number you passed. Copying a game does not copy its files, so in the copy the pictures are gone — unless ''​gameId''​ is named explicitly, or if it points at the copy itself. 
 + 
 +Bind the inventory to the game the pictures were uploaded to: 
 + 
 +<code JavaScript>​ 
 +showInventoryItems2({gameId:​ 3493}); 
 +</​code>​ 
 + 
 +The same number can be set once for the whole game, in the **"​Common HTML header"​** — it is copied together with the game: 
 + 
 +<code HTML> 
 +<​script>​ 
 +setInventoryGameId(3493);​ 
 +</​script>​ 
 +</​code>​ 
 + 
 +===== Zones of Application ===== 
 + 
 +A **zone** is a place on the level an item is applied to: a door, a lock, a merchant, a shelf slot. A zone has a code. 
 + 
 +When the player applies an item to a zone, the engine submits a **compound code: the zone's code plus the item's key**. A crowbar (''​crowbar''​) on the ''​lid''​ zone is the code ''​lidcrowbar''​. That is the code the bonus which opens the lid must have. 
 + 
 +{{ :​authors_main:​task_editor:​advanced:​inventory2_apply_en.gif |The crowbar on the lever: the code is the zone's name plus the item's key}} 
 + 
 +A zone with no code behind it simply answers "did not fit", and the item returns to the bag. There is no need to invent "​empty"​ zones: a zone with no matching item is harmless. 
 + 
 +Applying **without a zone** — "​use"​ next to the bag — submits a code equal to the item's key (the same as in the first version). 
 + 
 +> **The zone's code and the key are glued together with no separator**,​ so keep the names apart yourself: the zone ''​slot''​ with the item ''​1book''​ and the zone ''​slot1''​ with the item ''​book''​ give one and the same code. 
 + 
 +The engine does not check on the player'​s side which item fits which zone, and it will not: the right answer would then be lying in the page source. Any item on any zone is just a code, and it is the server that answers it. 
 + 
 +==== Declaring a Zone ==== 
 + 
 +Four ways, and all of them are about the same thing — a point and a radius around it. They differ only in where the point comes from. 
 + 
 +=== A Point on a Picture (Inline SVG) === 
 + 
 +The main way when a picture has several places of application. In the task text, in **"​Source"​** mode: 
 + 
 +<code HTML> 
 +<svg viewBox="​0 0 800 600">​ 
 +  <image href="​https://​cdn.qeng.org/​f/​g/​1234/​room.jpg"​ width="​800"​ height="​600"/>​ 
 +  <circle data-inventory-zone="​door"​ cx="​410"​ cy="​255"​ r="​40"​ fill="​none"/>​ 
 +  <circle data-inventory-zone="​chest"​ cx="​96"​ cy="​470"​ r="​55"​ fill="​none"/>​ 
 +</​svg>​ 
 +</​code>​ 
 + 
 +The coordinates live in the ''​viewBox''​ system, that is, they are tied to the picture rather than to the screen: phone, desktop, rotation, zoom — the point stays in the same place of the drawing. Nothing has to be recalculated. 
 + 
 +''​r''​ is the radius of application **in the picture'​s units**. On a small screen the picture shrinks, and the zone would shrink with it — so the engine raises the hit area to the size of a finger by itself. 
 + 
 +Not only ''​circle''​ works: any shape'​s bounding box is taken. 
 + 
 +> A picture inserted as ''<​img>'' ​cannot be marked up this way: an external svg has no content the engine can reach. For a picture in ''<​img>''​ use the percentages below. 
 + 
 +=== Percentages on a Picture === 
 + 
 +The cheapest way when you would rather not bother with svg: 
 + 
 +<code HTML> 
 +<img src="​..."​ data-inventory-zones="​door:​41,​42 chest:​12,​78,​6">​ 
 +</​code>​ 
 + 
 +After the colon — the zone's code, then percentages of the picture'​s own size: x, y and (optionally) the radius. One attribute can hold as many zones as you like, separated ​by spaces. 
 + 
 +An icon of its own cannot be set this way — only one common to every zone of that host. If each needs its own, use ''​data-inventory-zone''​ on separate elements or ''​addInventoryZone()''​. 
 + 
 +=== Any Element of the Page === 
 + 
 +<code HTML> 
 +<div data-inventory-zone="​npc">​The merchant</​div>​ 
 +<img src="​..."​ data-inventory-zone="​lock">​ 
 +</​code>​ 
 + 
 +The hit area is the element'​s whole rectangle. This is the answer for "the zone is a block of text, a whole picture, or a button"​. 
 + 
 +=== From a Script === 
 + 
 +A zone that is not on the level to begin with is declared by a call — in the task script or in a bonus's "Text after solution":​ 
 + 
 +<code JavaScript>​ 
 +addInventoryZone('​box',​ {el: '#​room',​ x: 120, y: 150, r: 45}); 
 +</​code>​ 
 + 
 +The first argument is the zone's code. Then: 
 + 
 +^ Field ^ What it means ^ 
 +| ''​el''​ | the host: a selector or an element. **Required** — it sets the coordinate system | 
 +| ''​x'',​ ''​y''​ | the position inside the host; without them — the element'​s centre, and the hit area is its whole rectangle | 
 +| ''​r''​ | the radius of application;​ without it — the size of the icon, raised to a finger | 
 +| ''​icon''​ | the zone's own icon; without it the common one | 
 + 
 +**The host decides the units**: an inline svg means its ''​viewBox''​ coordinates,​ any other element means percentages of its rectangle (the radius, percentages of the shorter side). The same ''​{x:​ 150, y: 262}''​ means different things on an svg and on a picture. There are no absolute screen coordinates on purpose: they would slide away on the very first different screen. 
 + 
 +Calling again with the same code does not start a second zone but **updates the existing one**. That also means a zone can be moved: recalculate the position and call again. 
 + 
 +To remove one — ''​removeInventoryZone('​door'​)''​. This call takes down a zone from the markup too, but only until the next auto update: the html comes from the server again, and the zone is back. Such a zone has to be removed ​where everything else on the level is removed — with a [[en:​authors_main:​task_editor:​advanced:​edit_templates|template on the server]]. 
 + 
 +A zone from a script **overrides** a zone from the markup with the same code. 
 + 
 +==== An Icon of Your Own ==== 
 + 
 +By default every zone gets one common semi-transparent icon. Your own is set with the ''​data-inventory-zone-icon''​ attribute in the markup or the ''​icon''​ field of ''​addInventoryZone()'',​ and its kind does not have to be declared — the engine tells them apart itself: 
 + 
 +<code HTML> 
 +<circle data-inventory-zone="​door"​ data-inventory-zone-icon="​fa-lock"​ .../> 
 +<circle data-inventory-zone="​gate"​ data-inventory-zone-icon="/​f/​g/​123/​door.png"​ .../> 
 +<circle data-inventory-zone="​safe"​ data-inventory-zone-icon="​🔑"​ .../> 
 +</​code>​ 
 + 
 +  * ''​fa-lock''​ — a font-awesome glyph; 
 +  * a path or an address — a picture of your own; 
 +  * anything else — just a character. 
 + 
 +==== Zones That Are Not There Yet ==== 
 + 
 +Which zones exist usually depends on what the player has already done: an opened crate has no zones, and the machine'​s slot appears together with the coin. The markup for that is convenient to assemble with [[en:​authors_main:​task_editor:​advanced:​edit_templates|templates]],​ ''​{% match bonus_N %}''​ — they are computed on the server, so the page source under the player holds only the layers and zones that are visible right now. 
 + 
 +Templates work the same way in the task script, but the template lines are commented out with a double slash — the engine strips it itself: 
 + 
 +<code JavaScript>​ 
 +// {% match bonus_2}:​{ 
 +//   {%= leverdncrowbar}:​{ 
 +addInventoryZone('​leverup',​ {el: '#​quest_room',​ x: 288, y: 305, r: 45}); 
 +//   %} 
 +//   {%= ?}:{ 
 +addInventoryZone('​leverdn',​ {el: '#​quest_room',​ x: 315, y: 112, r: 45}); 
 +//   %} 
 +// %} 
 +</​code>​ 
 + 
 +===== Worth Knowing ===== 
 + 
 +  * **An item's key is a code.** Dots, quotes, and extensions must not go into it. 
 +  * **Applying is always silent:** nothing appears in the answer form. All the feedback is on the item itself and on the zone. 
 +  * **The engine reads the verdict out of the "​Previous answers"​ block.** With monitoring switched off on the level the item simply returns to the bag in silence — no green, no red. 
 +  * **The same wrong code does not go to the server a second time.** The item comes back at once, with the same verdict as last time — no attempt is spent. 
 +  * **A hidden host is no zone.** A zone on an element inside a collapsed ''<​details>'',​ on ''​display:​ none'',​ or on a picture that has not loaded yet will not appear until the host shows itself. The engine recounts on its own: on a window resize, on a picture'​s load, and at the moment the player picks an item up. 
 +  * **While an item is in hand, a zone's icon lies over the page** and intercepts the click on whatever is beneath it — including your own button. 
 +  * **A bonus's synonyms are a state.** A bonus'​s value in a template is its **last** entered answer, and each new synonym replaces the previous one. That is why the lever goes back and forth and the book is put up and taken down: two synonyms on one bonus instead of two bonuses. 
 +  * **A bonus description marked ''​%hide%''​** is not in the page source until it is solved — the chain of applications cannot be peeked at. 
 +  * A bonus hidden by the engine is printed **without** the answer entered into it. If a script needs that answer, hide the bonuses block with a style rather than with the mark. 
 +  * The panel lives outside the auto-updating part of the page: somebody else's code or a new hint will not close it and will not take away the selected item. 
 +  * An open panel pushes the bottom of the page away. Your own block pinned to the bottom will argue with it. 
 + 
 +What the inventory does not have: **quantity** (an item is either there or not — "three coins" are made of three items or three codes), sorting, and dragging an item onto an item. 
 + 
 +===== Examples in the Game ===== 
 + 
 +  * [[https://​qeng.org/​game.php?​jump_to&​gid=3493&​task_id=83700|Inventory:​ the room]] — items are found and applied to points on a picture: a crowbar, a lever, a lock, a lid, a coin into a machine. Zones from a script, scene layers from templates. 
 +  * [[https://​qeng.org/​game.php?​jump_to&​gid=3493&​task_id=83701|Inventory:​ the shelf]] — items are not handed out by bonuses at all: what is in the bag and what stands on the shelf is worked out by the script from the entered codes every time. 
 + 
 +Both levels carry a "How is it done?" section inside them with a walkthrough. 
 + 
 +===== The First Version ===== 
 + 
 +It works the way it always worked. The difference is that the inventory is rendered inside a block the author sets aside, there are no zones, and a selected item has two buttons — the eye and "​use"​. 
 + 
 +{{ :​authors_main:​inventory_en.png?​800 |The first version'​s inventory: items arrive for solved bonuses}} 
 + 
 +{{ :​authors_main:​inventory_item_en.png?​800 |A selected item: the eye shows the picture full size, the tick uses the item}} 
 + 
 +  - In the task text (in **"​Source"​** mode) — a block where the list should be shown: 
 +<code HTML> 
 +<div id="​inventory_container">​Loading inventory...</div> 
 +</​code>​ 
 +  - In the task script — the call: 
 +<code JavaScript>​ 
 +showInventoryItems('​inventory_container'​);​ 
 +</​code>​ 
 + 
 +The second parameter is the same binding of pictures to another game: ''​showInventoryItems('​inventory_container',​ 1234)''​. 
 + 
 +Items are given and taken the same way, with ''​addInventoryItem()''​ and ''​removeInventoryItem()''​ in a bonus'​s "Text after solution"​. The "​use"​ button submits a code equal to the item's key. 
 + 
 +{{ :​authors_main:​inventory_use_en.gif |An item arrives for a bonus, and "​use"​ types its code for the player}} 
 + 
 +Example in the game: [[https://​qeng.org/​game.php?​jump_to&​gid=3493&​task_id=68367|Inventory]].
  
-==== The "​Use"​ Button ​==== +===== See Also =====
-When a player clicks the **"​Use"​** button next to an item in their inventory, the engine automatically attempts to submit a code matching the item's key. +
-* For example, using the item ''​hammer123''​ will automatically submit the code ''​hammer123''​ to the engine'​s answer field.+
  
-===== Demonstration ===== +  * [[en:​authors_main:​task_editor:​advanced:​edit_templates|Templates]] — ''​{% match bonus_N %}''​ for a scene and for zones that depend on the state of the level. 
-A live example of a level with inventory configuration can be viewed in the [[https://qeng.org/​game.php?​gid=3493&​tid=68367|Demo game for authors]].+  * [[en:authors_main:​task_editor:​advanced:​composite_codes|Composite codes]] — the level'​s final code, assembled from parts. 
 +  * [[en:​authors_main:​task_editor:​advanced:​author_scripts|Interactive elements (scripts)]] — the other functions available in the task script.
  
  • en/authors_main/task_editor/advanced/inventory.1781559088.txt.gz
  • Last modified: 2026/06/15 21:31
  • by 127.0.0.1