Random objects/encounters in different areas of the game

hegemonkhan
22 Apr 2018, 16:23

this involves using the 'list/dictionary' Attribute (+ GetRandomInt/GetRandomDouble/RandomChance/DiceRoll for the randomness), if you need help with using lists/dictionaries, let me know.


the 'GetRandomInt' and 'RandomChance' work especially well together too:

for example (an item) drop system:

the 'GetRandomInt' selects the (type of) item

and the 'RandomChance' determines whether that selected item, is actually dropped (successful) or not (failed)

create ("example_object")

create ("sword")
create ("candy")
create ("axe")
create ("chocolate")

example_object.example_objectlist_attribute = NewObjectList ()
list add (example_object.example_objectlist_attribute, sword)
list add (example_object.example_objectlist_attribute, candy)
list add (example_object.example_objectlist_attribute, axe)
list add (example_object.example_objectlist_attribute, chocolate)

list_count_integer_variable = ListCount (example_object.example_objectlist_attribute)

last_index_number_integer_variable = list_count_integer_variable - 1

viable_randomly_selected_index_number_integer_variable = GetRandomInt (0, last_index_number_integer_variable)

viable_randomly_selected_list_item_object_variable = ObjectListItem (example_object.example_objectlist_attribute, viable_randomly_selected_index_number_integer_variable)

// you can use 'if' too, instead of the 'switch' below, as 'if' and 'switch' are functionally the same, though the syntax for them is a bit different, of course

// though, if you got a lot of items in your list, then it'd be better to use a Script Dictionary, or to have each item Object have the 'drop' script on it, and call/do/use that 'drop' script, but this is a bit more advanced design... so not showing it here... showing the 'switch' instead as seen below

switch (viable_randomly_selected_list_item_object_variable) {
  case (sword) {
    if (RandomChance (10)) {
      CloneObjectAndMove (sword, player)
    }
  }
  case (candy) {
    if (RandomChance (90)) { // 90% (a high) chance of dropping (getting) it // common item drop
      CloneObjectAndMove (candy, player)
    }
  }
  case (axe) {
    if (RandomChance (10)) { // 10% (a low) chance of dropping (getting) it // rare item drop
      CloneObjectAndMove (axe, player)
    }
  }
  case (chocolate) {
    if (RandomChance (50)) { // 50% (1/2) chance of dropping (getting) it // a 1/2 (which is still quite good odds, lol) item drop
      CloneObjectAndMove (chocolate, player)
    }
  }
}

as for controlled randomness (lol), as far as I know, the only way to enforce a probability is to directly control it:

for example, if you want a '1/6' odd/chance/probability, then you got to literally have it happen on the 6th time, which means a 'counter' to track it:

create ("example_object")

example_object.counter_integer_attribute = 0

example_object.flag_boolean_attribute = false

// ---------

// if the 'example_object.counter_integer_attribute = 0' causes the 'changed' script to fire, then you need this code:

example_object.changedcounter_integer_attribute => {
  if (example_object.counter_integer_attribute = 6) {
    example_object.flag_boolean_attribute = true // this is needed to happen before this 'changed' script fires again by the 'example_object.counter_integer_attribute = 0' line below, lol
    msg ("BLAH ACTION/EVENT") // and this too, as well, lol
    example_object.counter_integer_attribute = 0
  } else if (example_object.flag_boolean_attribute) {
    example_object.flag_boolean_attribute = false
  } else {
    example_object.counter_integer_attribute = example_object.counter_integer_attribute + 1
  }
}

// otherwise / if-not, you can just use this code:

example_object.changedcounter_integer_attribute => {
  if (example_object.counter_integer_attribute = 6) {
    example_object.counter_integer_attribute = 0
    msg ("BLAH ACTION/EVENT")
  } else {
    example_object.counter_integer_attribute = example_object.counter_integer_attribute + 1
  }
}

hegemonkhan
22 Apr 2018, 17:13

if you want all items to be tried (if the first item fails, try for the next item, and so forth)... the best design (if you got a lot of items) would be recursion usage (see diablo 2's recursion, item drop / treasure class, system)


DarkLizerd
22 Apr 2018, 17:35

HK: "as for controlled randomness (lol), as far as I know, the only way to enforce a probability is to directly control it:
for example, if you want a '1/6' odd/chance/probability, then you got to literally have it happen on the 6th time, which means a 'counter' to track it:"

Nope... just go with a GetRandomInt (0, 6) to make it random...
For you randomness...
Do you want a selected random items in the room? Or scattered throughout your maze of rooms?
For the first:
You could add a bit of code in the "on first enter":
Room_01
// no creature
// no items

Room_02
Switch(GetRandomInt (0, 6))
Case 1,2,3: no creature
case 4: Bat
case 5: lost explorer
case 6: Giant spider
switch (0,3)
case 1: Bag of coins
case 2: rusty sword
case 3: Heal potion

Room_03
anything else
...


mrangel
22 Apr 2018, 21:03

I'd previously considered making a collection of containers for the various events/encounters. This system would allow moving monsters or items to the player's location, or running a script.

These are basically unreachable rooms, each representing a random encounter. Any objects inside the 'encounter' are moved to the player's location when it is triggered. It also can have attributes encounter_script (a script that is run when the encounter triggers), encounter_probability (an int; higher numbers are more likely), encounter_enabled (boolean), encounter_repeats (boolean), encounter_locations (objectlist of rooms where where the encounter can happen), and encounter_test (script that can modify the values above, in case it's more likely in some places than others for example).

A script something like:

possible_encounters = NewObjectList()
total_probability = 0
foreach (enc, GetDirectChildren(ENCOUNTERS)) {
  if (HasScript(enc, "encounter_test")) {
    do (enc, "encounter_test")
  }
  if (GetBoolean(enc, "encounter_enabled")) {
    found_room = true
    if (HasAtribute (enc, "encounter_locations")) {
      found_room = false
      foreach (room, enc.encounter_locations) {
        if (Contains (room, game.pov)) {
          found_room = true
        }
      }
    }
    if (found_room) {
      if (not HasInt (enc, "encounter_probability")) {
        enc.encounter_probability = 10
      }
      list add (possible_encounters, enc)
      total_probability = total_probability + enc.encounter_probability
    }
  }
}
if (total_probability < 100) {
  total_probability = 100
}
chosen = GetRandomInt (1, total_probability)
while (ListCount(possible_encounters) > 0) {
  encounter = ListItem (possible_encounters, 0)
  list remove (possible_encounters, encounter)
  chosen = chosen - encounter.encounter_probability
  if (chosen < 0) {
    possible_encounters = NewObjectList()
    encounter_children = NewObjectList()
    foreach (o, GetDirectChildren (encounter)) {
      if (GetBoolean (encounter, "encounter_repeats")) {
        list add (encounter_children, CloneObjectAndMove (o, game.pov.parent))
      }
      else {
        list add (encounter_children, o)
        o.parent = game.pov.parent
      }
    }
    if (HasScript (encounter, "encounter_script")) {
      // the "room" and "children" variables give the encounter script an easy way to find the
      //   room it's happening in, and the objects moved/cloned there (for example monsters)
      params = NewDictionary()
      dictionary add (params, "room", game.pov.parent)
      dictionary add (params, "children", encounter_children)
      do (encounter, "encounter_script", params)
    }
    if (not GetBoolean (encounter, "encounter_repeats")) {
      RemoveObject (encounter)
    }
  }
}

You could run that from the enterroom script, or when visiting a new room for the first time, or something like that. Depending if you want random encounters to only occur in new rooms.