dynamic character array, dictionary questions

Started by brewton, Fri 19/02/2021 02:04:53

Previous topic - Next topic

brewton

Hi, I'm not sure how to ask this. It might be a two-part problem. If anything I'm just articulating the problem so I can understand it better myself.

First, here's a link to my project on GitHub so you can see what I'm trying to do.

I'm working on something that requires a dynamic array of dummy characters that I can reuse with different names and sprites and properties.
I haven't worked much with these concepts so please let me know if I am mistaken in how they function. I've read the manual and searched the forum but I think I'm kind of lost.

So far with my three test characters it works, but these are not dummy characters and are hard-assigned to inventory items.
The dummy characters are for visible inventory items in a scene, so all use the same view and are locked to a frame according to a custom property CharID assigned to inventory items.
Similarly, characters are assigned a custom property

This is the code I would like to replace.

Code: ags

  void drop_held_item()
  {
        InventoryItem* Item = player.ActiveInventory;                          // initialize the held item
        int CharID = Item.GetProperty("CharID");                               // what's the character?
        character[CharID].ChangeRoom(player.Room, mouse.x, mouse.y   offset);  // move the character to where the mouse was clicked - 'dropping' it.
        RemoveWalkableArea(2);
        character[CharID].PlaceOnWalkableArea();                               // stops the object being stuck on a wall or something        
        
        int InvFrame = Item.GetProperty("InvFrame");                           // what's the inventory frame ID of the item?      
        character[CharID].LockViewFrame(1, 0, InvFrame);                       // locks it to the item's sprite            
        
        RestoreWalkableArea(2);
        player.LoseInventory(Item);
        player.ActiveInventory = null;
  }

  void pick_up_item(InventoryItem* item, Character* itemChar)
  {
      itemChar.UnlockView();                          // no longer locked to the item sprite
      player.AddInventory(item, invSlots);            // places it in the cursor
      player.ActiveInventory = item;                  // makes it active
      itemChar.ChangeRoom(0);                         // sends it to the inventory room      
  }


I haven't worked with dynamic arrays or dictionary lists before but I think they could provide a solution.

What I want to do is write a function that handles:

Part 1: When an item is dropped on the screen:
- create a new dummy character
- create a dictionary key with the character's array value (ex cDummyItem[22])
- add values:
  . Inventory item number
  . Inventory item name
  . inventory item sprite number
  . inUse bool = true **
- use LockViewFrame to lock cDummyItem[22] to the sprite number assigned in its dictionary key
- assign the inventory item name to the character name

Part 2: When an item (character) is clicked
- retrieve the inventory item number and return the value
- reset the character instance
- reset the character dictionary key

So my initial code would become something like (not tested)
Code: ags

  void instance_dummy(InventoryItem* Item)
  {
      // Part 1 pseudocode from above
  }

  function destroy_dummy(Character* dummy)
  {
      // Part 2 pseudocode from above
      // returns inventory item      
  }

  void drop_held_item()
  {
        InventoryItem* Item = player.ActiveInventory;                          // initialize the held item
        instance_dummy(Item);
        player.LoseInventory(Item);
        player.ActiveInventory = null;
  }

  void pick_up_item(Character* itemChar)
  {
      *InventoryItem Item = destroy_dummy(itemChar);
      player.AddInventory(Item, invSlots);         // places it in the cursor
      player.ActiveInventory = Item;                  // makes it active
  }


I guess my questions are: would this work and is this a smart way to do this?
Also, would it be better to just keep creating new characters, or is it better to search through the array for the next available "inUse == false" character and reinitialize them with new values?

Crimson Wizard

#1
Ah, Kyrandia template. We had a discussion about this couple of years ago, in this rather long thread. We discussed dropping items and painting trees in random locations, and many other things. I don't know whether person's work ended in success or not, as he was not on since.


There are several issues touched in your post, but most important thing first, you cannot create game objects at runtime in AGS (with exception of Overlays, and some utility types like arrays, dynamic sprites and dictionary).
Sorry if you already knew this, but it was not clear from your post.
So you would have to use "object pool" method:

- Create as many dummy characters in the editor as you may need displayed at the same time;
- In script have these characters in some array, which does not have to be dynamic, could be static too, as you might know their total number at the time of compilation;
- In fact, you may not use array of characters, but maybe array of structs containing 2 variables, first is char ID, and next is boolean "in use".
- When you need a new dummy character you get a free one according to this list and mark as used. When you no longer need this character you mark it as not used and hide them.

You know maximal available characters, you know which of them are in use, and you can put characters into use or "release" them from use.
Released characters may be hidden in the non-existant room (e.g. ChangeRoom(-1) works for non-player characters).


In the past I wrote one very basic module to simplify this kind of logic. It helps to track a pool of dummy objects with integer IDs (does not matter what kind of objects these are, so long as they have IDs).
Never posted it properly on forum here, but I quickly uploaded the script, so you may take a look if you like:
https://github.com/ivan-mogilko/ags-script-modules/blob/master/scripts/_dump/ObjectPool.ash
https://github.com/ivan-mogilko/ags-script-modules/blob/master/scripts/_dump/ObjectPool.asc
Spoiler

I used this to write arcade sequences for couple of games (like this), maybe I could disclose parts of that script too later, or maybe it's not needed for this case.
The use is rather simple.
Code: ags

// create an instance of the ObjectPool
ObjectPool Pool;

function game_start()
{
     // setup which game objects could be used in the pool
     Pool.AddObjects(50, 100); // add IDs from 50 to 100
}

// How to of use, in general
function AddNew()
{
    int id = Pool.Acquire(); // get next free one
    if (id < 0) return error // pseudocode
    // id is the real game object's id
    // could be used as
    // object[id] - to get room objects
    // character[id] - to get characters, and so on
}

function RemoveOne(int id)
{
    Pool.Release(id); // mark as unused
}

[close]

brewton

Thanks, CW!
I didn't know that about the run-time object creation, so that's the missing piece in my logic.
Your solution is a lot more elegant. I guess for some reason I was thinking of using dictionaries as structs.

SMF spam blocked by CleanTalk