How do I store several tags in a variable?

From OHRRPGCE-Wiki
Jump to navigation Jump to search

If you're not aware, a bit is the smallest possible amount of information that a computer can store. The famous zeros and ones that computers use for everything are bits. One and zero can also be thought of as on and off, true and false, yes and no, good and evil, Honoka and Nagisa, day and night, fuschia and not fuschia, or any other type of situation where there are exactly two possible values you want to keep track of. If that sounds familiar, it's because that's exactly how tags work. A tag is nothing more than a bit that the OHRRPGCE keeps track of for you.

Since variables are just information on your computer, they're made out of bits, too. Specifically, variables in HamsterSpeak are 32-bit signed integers, so they're each made out of 32 bits. So if you have a way to mess with those bits one at a time, you can use a variable as 32 tags.

# return whether one particular bit is true or false
script, get variable bit, value=1, which=1, begin
  variable (bit)
  # the 32nd bit is the minus sign, so it has to be treated differently
  if (which == 32) then, begin
    bit := -2147483648
    # the computer thinks of -2147483648 as 10000000000000000000000000000000b
  end
  else, begin
    # this turns the numbers 1, 2, 3, 4... into the numbers 1, 2, 4, 8...
    # why these numbers? because if you convert them to the bits the computer
    # uses to represent them, you get 00000000000000000000000000000001b,
    # 00000000000000000000000000000010b, 00000000000000000000000000000100b,
    # 00000000000000000000000000001000b...
    # in other words, it gives us a number where all the bits are turned off
    # except the one we're interested in, which is true
    bit := 2 ^ (which -- 1)
  end
  
  # return true if there are any bits that are turned on in both value and bit
  # since bit has only one bit turned on, this is the same as returning true
  # if that bit is turned on
  return ((value, and, bit) <> 0)
end

# change the value to have the bit in the requested state, and return the result
script, set variable bit, value=1, which=1, state=true, begin
  variable (bit)
  if (which == 32) then, begin
    bit := -2147483648
  end
  else, begin
    bit := 2 ^ (which -- 1)
  end
  
  if (state) then, begin
    # if we're trying to turn the bit on, then start a new number. go through it
    # one bit at a time, and if either value or bit has that bit turned on, turn it
    # on in the new number, too, and return that.
    return ((value, or, bit))
  end
  else, begin
    # this is more complicated. -1 looks like 11111111111111111111111111111111b to the
    # computer, so (-1 -- bit) equals the opposite of bit: all the bits are on, except
    # that the one that's turned on in bit is turned off. next, go through every bit, 
    # and if a bit is turned on in BOTH value AND (-1 -- bit), then turn that bit on.
    # this will turn on everything that's turned on in value, except that the bit that's
    # turned off in (-1 -- bit) and on in bit will be turned off.
    return ((value, and, (-1 -- bit)))
  end
end

(If you read the explanations, you might be thinking "wait, that's not what I thought -1 or -2147483648 would look like in binary!" That's because binary negative numbers are represented using something called two's complement, which looks a little weird to humans but makes perfect sense to computers.)

That's all really complicated, but you don't necessarily need to know how it works in order to use it, which is done like so:

variable (a)
variable (b)

a := 0
# 00000000000000000000000000000000b

a := set variable bit (a, 5, true)
# 00000000000000000000000000010000b

if (get variable bit (a, 5)) then, begin
  show text box (1)
  wait for text box
end
#true

b := -1
# 11111111111111111111111111111111b

b := set variable bit (b, 5, false)
# 11111111111111111111111111101111b

if (get variable bit (b, 5)) then, begin
  show text box (1)
  wait for text box
end
#false

Okay, but why?[edit]

You might be wondering why you would even want to do this. And the answer might be that you wouldn't! Your game has a total of 15998 tags you can use to store simple true/false values, and even if your game is really, really complicated, you probably won't ever need more than that. But there are a few situations where this can be helpful:

  • There are some things you can't do with tags, only variables. For instance, you might have some true/false values you want to read from a saved game without loading the entire save. In that case, you can store those values as a global variable, and use import globals to read that variable.
  • If you have a script that needs to know a lot of true/false values, you can put them all in a single integer and pass them as one argument instead of a long list of them. This can be helpful if you're calling the script from an NPC or a menu, where you can only pass along a handful of arguments.
  • You can still treat the variable as one big number, so you can set or check all the bits at once. In the example above you can see how we set a and b to 0 and -1 to turn all the bits off or on at once. This also works with other numbers: if you have a list of 12 conditions you want to check, you can assign each of them to the first 12 bits of a global variable, and then use a calculator to figure out what number 00000000000000000000111111111111b is (it's 4095) and have your script check whether your variable equals that instead of checking twelve different tags.