Any way to make dynamic lists/arrays?

I'm building a monster catching type of game, (which is going perfectly btw! probably more on that soon) except now that I need to save data I have a problem. I've been searching the forum and documentation for 2 days now but I don't really see an option to add lists or arrays and or something. Any workarounds for this that someone found? Any chance this will still be added?

I know pulpscript is supposed to be simple and stuff. But it would give lot's of options to builders.

1 Like

By save data do you mean to persistent storage with store and restore? If so you're limited to using variables, rather than trying to get creative like using a room as an array with tile names as values (which I think is possible!).

I have a pretty shaky implementation of an array using only variables and some game events as helper functions, with the caveat that you need to predefine a limit on the length of the array (with the amount of code increasing linearly it goes up).

Essentially it comes down to using one variable to store the length of the array and another variable for each positional value:

array_length = 3
array_0 = "foo"
array_1 = "bar"
array_2 = "baz"

I suppose the nice news is that you get to decide whether you want the array to be zero indexed or not :smile: As per any undeclared pulp script variable, any undeclared positional value will return 0.

Unfortunately we can't just loop over the length of the array like:

i=0
while i<array_length do
  // Do something
  i++
end

Because we can't dynamically reference variable names (afaik), so instead all our array events need to manually check which position they are affecting:

on appendToArray do
  array_length += 1
  if array_length==1 then
    array_0 = arg1
  elseif array_length==2 then
    array_1 = arg1
  elseif array_length==3 then
    array_2 = arg1
  elseif array_length==4 then
    array_3 = arg1
  end
end

Hence the length of the array being hardcoded and the code required increasing as you raise that limit.

Custom events can't take arguments (again afaik), so instead we have to use variables as arguments and set these before calling the event handler. If you use names like arg1, arg2 etc. I think it helps avoid any clashes in the global scope (at least for me!).

arg1 = "qux"
tell event.game to
  call "appendToArray"
end

If you want to use more than one array (very reasonable) you could also pass the array as an argument to a single set of generic helper functions, but because you would need to manually split out which array you are affecting with a conditional anyway, I think it's probably easier to duplicate the helper functions for each array you add.

Anyway, you probably want an array so you can do something useful and array-like with it. Judge this solution for yourself, your mileage may vary!

on valueInArray do
  if array_0 == arg1 then
    return = 0
  elseif array_1 == arg1 then
    return = 1
  elseif array_2 == arg1 then
    return = 2
  elseif array_3 == arg1 then
    return = 3
  else
    return = -1
  end
end

Similarly to using global variables in place of arguments, we have to use a global return variable in place of a return value.

arg1 = "baz"
tell event.game to
  call "valueInArray"
end
say "{return}"  // 2

You could then update that specific value in the array:

on updateArray do
  if arg1==0 then
    array_0 = arg2
  elseif arg1==1 then
    array_1 = arg2
  elseif arg1==2 then
    array_2 = arg2
  elseif arg1==3 then
    array_3 = arg2
  end
end
if return > -1 then
  arg1 = return
  arg2 = "xyzzy"
  tell event.game to
    call "updateArray"
  end
end

And we end up with an array like:

say "{array_length} {array_0} {array_1} {array_2} {array_3}"  // 4 foo bar xyzzy qux

We could do plenty more besides.

This probably isn't the best way of implementing arrays, I'm no language author and it's very manual, but it's what I figured out when I was playing around :sweat_smile:

I think the best case scenario, aside from native arrays, would be a feature in pulp script to allow indirect variable referencing. That is, something like this (inventing a new function called "indirect" - this isn't working Pulp Script code as the indirect function does not exist)

on load do
  _len=0
end

on getLen do
  // return the length on _ret
  _ret = _len
end

//-------------------------
// stack-like implementation
//
on push do
  // assume global var set as input: _val
  indirect "_array_val[{_len}]" = _val
  _len++
end

on pop do
  // return value will stored to: _ret
  // no way to delete a variable in pulpscript...? so at least set the popped var to a value of zero
  _ret = indirect "_array_val_[{_len}]"
  indirect "_array_val_[{_len}]" = 0
  _len--
end

//--------------------------
// array-like implementation using getters and setters
//
on set do
  // assume two global vars as inputs: _idx, _val
  indirect "_array_val_[{_idx}]" = _val
  // adjust the length; 
  // must be at least (_idx+1)
  if _idx >= _len then
    _len=_idx
    _ len++
  end
  // any unset array members are implicitly just zero-valued in pulpscript anyway, so this is safe
end

on get do
  // assume a global var input: _idx
  // return value on _ret
  // do not alter the storage location or array length (unlike pop, which implies the value is removed)
  _ret = indirect "_array_val_[{_idx}]"
end

I think it's reasonable to add indirect as a feature request. In my opinion the above code is "pulpy" or whatever label describes something that follows common pulp script design patterns.

I'd LOVE to also have the ability to create scoped variables... e.g. variables that are only valid within the given tile? so if i want three arrays i could just drop the above into a single tile definition, then drop 3 of those tiles into a room and refer to them by coordinates. boom: 3 separate arrays of arbitrary length.

would be awesome to have support for a row of tiles that are off-screen too, so the rendering engine knows we don't need to draw them, but they exist for references like this. perhaps just arbitrarily support negative coords as "off screen"... so a game script could create these array tiles and refer to them without imposing any limitations on the rooms being played in otherwise?

with all three of the above features we could create pretty much any data structure we want. it might not be the most efficient solution, but that's not the point of pulp script, right? it would, however, enable these more interesting data structures to be accessible enough so a game in pulp script that just needs one or two arrays could still be finished... and that's feel pretty "pulpy" to me :slight_smile:

2 Likes

I would love if tell, name, solid etc. supported the in "roomName" syntax, like goto already does, and then not only could events on one screen directly alter the layout of other screens (rather than having to set a variable then handle it during the enter event of the relevant screen), but you could create a screen that isn't playable but used for things like this. I think that'd fit slightly better than off-screen tiles because the syntax is already there with goto.

The suggested indirect function would definitely be useful too!

1 Like

Instead of generating a new numbered variable for every item in an array, what if you treated event handlers as objects in an array? (This approach won’t work for storing runtime values but is a pretty clean approach for fixed data like item prices and monster stats that are known at author time.) This example is for the player’s PulpScript for clarity of intention. You could add these event handlers to the game or individual rooms or tiles and use tell to call them.

on load do
  totalItems = 3
end

on item1 do
  itemName = "first item"
  itemValue = "an apple"
end

on item2 do
  itemName = "second item"
  itemValue = "an orange"
end

on item3 do
  itemName = "third and final item"
  itemValue = "a banana"
end

on confirm do
  i = 0
  while i<totalItems do
    i += 1
    call "item{i}"
    log "{i} {itemName}: {itemValue}"
  end
end

I’m not sure how well this will scale (rememeber, PulpScript really isn’t designed for heavy data manipulation, syntactically or in terms of runtime optimizations) but it minimizes impact on the global variable name space since it’s just changing the value of a few predefined variables instead of generating a new variable for every possible value.

5 Likes

that is nice for compile-time static lists... sadly not so much for the use cases i envision.

1 Like

This would be cool to have, yes. Indirect is something that a lot of people would benefit from.

Or maybe double braces.

Like

"{{varName}}"
2 Likes

This also a Nice workaround solution. Will probably use this for some data retrieval.

For small arrays of numbers, you can store them encoded inside a number variable in a certain base. For example, 54321 could be an array of integers from 0-9 where the first element is 5, the second is 4, etc. 5330 (210221023) could be an array of integers from 0-2 where the first element is 2, the second is 1, etc.

1 Like

For small arrays of numbers, you can store them encoded inside a number variable in a certain base.

Ease of read access is the biggest pain with this, no?