Random objects/encounters in different areas of the game

scrimshaw04

I'm attempting to create a set of objects/encounters that can randomly appear in some areas of my game.

Firstly, I'd like to create a set of possible encounters that can happen. Some encounters should be more likely than others to happen. I believe GetRandomInt would be useful for this, but how can I make certain integers more likely to be rolled than others?

Secondly, only certain encounters should be possible in certain areas of the game, and in some areas no encounters should be possible.

Thirdly, the encounters should be repeatable.

How could I go about approaching this?

Thanks in advance!


TinFoilMkIV

For the first question, instead of trying to make certain integers more likely than other's I'd simply roll a larger range of random integers then check for a specific range of numbers to equal a specific result
ie:
chance = GetRandomInt (0, 100)
if (chance <= 15){
//scenario #1
}
else if (15 < chance >= 50){
//scenario #2
}
else {
//scenario #3
}

So anything below 15 is scenario #1, 15-50 is scenario #2, and 51-100 is #3

You can use a similar technique if you want to create a chance to even have an encounter in any given area.

For area specific encounters you have a few options. Probably the most straightforwards would be to give the area itself an attribute list that holds the names of any encounter that can happen there. Then when an encounter is about to happen you pick a random item from that list then do that encounter. You can combine ListCount (list) with the GetRandomInt (integer min, integer max) to pick a number within the range of the list no matter it's size.
ie:
//select a random encounter
id = GetRandomInt (0, ListCount (area.encounters) -1)
//-1 because lists start at 0 so the max value is one below the item count
encounter = StringListItem (area.encounters, id)
do (area, encounter)

Making an encounter repeatable is pretty simple, tho depends how complex the encounters are. Key things are to remember not to create or destroy anything outside the encounter that shouldn't be repeated, and to reset any attributes that don't carry over after the encounter so that it works the same when it happens again.

EDIT: also for reference here's a link to the documentation which has lists and info about scripts, functions, and all kinds of other generally useful stuff http://docs.textadventures.co.uk/quest/functions/


DarkLizerd

OK... I program in BASIC, not Quest, but it sounds like you want something like this:
(This is a sample of my room map)
RE is what chance you meet a creature. IE: room #1 20% otherwise nothing
Dice(1,2) rolls a die (random number generator) , in this case, return a 1 or a 2...
CR is what creature you meet. IE:
#0 is no creature
#1 is a male creature
#2 is a female creature
#3 is a lizard

Room 1: RoomName = "Entrance Cave": RE = 20: CR = Dice(1, 2)
Room 2:RoomName= "Side Cave": RE = 30: CR = 3
Room 8: RoomName= " Middle of hallway ": RE = 25: CR = Dice(1, 2)
Room 9: RoomName = " South end of hallway ": RE = 0: CR = 0

For quest, I would think you would add the code in the "enter room" section for each room.
And you could (or should) be able to set it up for more than just 1 encounter event.
IE, a lizard and a male creature.
Another option, and I'm not sure how, You could run a start game script that set's up each encounter
for each room first so that when you play that run, nothing changes, but everything changes the next time you play.


hegemonkhan

just to add to Tin's post/content:

there's 4 built-in Randomization Functions:

GetRandomInt (min_value, max_value) // an algorithm selects (and returns) a number between and including the min and max values
// GetRandomInt (0,9) // selects and returns: 0 or 1 or 2 or 3 or 4 or 5 or 6 or 7 or 8 or 9

GetRandomDouble () // an algorithm selects and returns a decimal/fraction (between 0.0 and 1.0), no input by you required
// let's say the algorithm selects (and returns): 0.7

DiceRoll ("NUMBER_OF_DICEdNUMBER_OF_SIDES") // similuates rolling a dice or die of however many sides
// DiceRoll ("1d6") // a normal six-sided single dice: 1x(1 to 6), min roll: 1, max roll: 6
// DiceRoll ("3d12") /// rolling 3 die each with 12-sides: 1x(1 to 12) + 1x(1 to 12) + 1x(1 to 12), min roll: 3 (1+1+1), max roll: 36 (12+12+12)

RandomChance (VALUE_AS PERCENT: 0-100) // has a percent of your input of returning 'true'
// RandomChance (30) // there's a 30% percent chance it'll return 'true'


as Tin mentioned, you use the 'GetRandomChance' with a List Attribute // checking for a wide range of values probably produces more randomness than just the algorithm itself does.

and for an example, lets say we got a treasure chest or a drop from a monster....

let's say the choices for the drop/treasure chest are:

red, blue, yellow

we use the 'GetRandomInt' and a List Atribute to select the item:

color_list_variable = split ("red;blue;yellow", ";")
item_variable = StringListItem (color_list_variable, GetRandomInt (0, ListCount (color_list_variable) - 1))

now, we can use the RandomChance, as to whether you actually get that selected item or not (so the 'GetRandomInt' and 'RandomChance' go well together if you want this type of design in your game):

if (item_variable = "red" and RandomChance (70)) {
  player.item_string_attribute = item_variable
} else if (item_variable = "blue" and RandomChance (33)) {
  player.item_string_attribute = item_variable
} else if (item_variable = "yellow" and RandomChance (90)) {
  player.item_string_attribute = item_variable
} else {
  msg ("nothing")
}

each area (room), object (Object), or monster (Object) would have its own List Attribute of items/events/actions that can be selected.


scrimshaw04

Brilliant, that makes sense. Thanks so much for the help guys!

Now I'm stuck on trying to figure out how to create a catalogue of objects, which can then be cloned and sent to the player's location. I'm not sure how to refer to the cloned object in a script. If the original object was named 'object' then would the clone be named 'object_1'?

I think I'll also need to set up a turn counter so that if the player ignores them, they will disappear from that room after they leave.


hegemonkhan

unfortunately, in the GUI/Editor's: 'whatever' Object -> 'Attributes' Tab -> Attributes -> Add, you can't create an Object List/Dictionary Attribute, you'll have to do so in code and/or maybe via GUI/Editor scripting too.

in code:

<object name="example_object">
  <attr name="example_objectlist_attribute" type="objectlist">toy;ball;hat;shoe</attr>
</object>

<object name="toy">
</object>

<object name="ball">
</object>

<object name="hat">
</object>

<object name="shoe">
</object>

or via code's scripting:

objectlist_variable = split ("toy;shoe;ball;hat", ";") // maybe this doesn't work... (maybe it'll be a String List and not an Object List)

// or:

objectlist_variable = NewObjectList ()
list add (objectlist_variable, toy)
list add (objectlist_variable, ball)
list add (objectlist_variable, hat)
list add (objectlist_variable, shoe)

// the Objects have to actually exist of course (see the code box above -- too lazy to copy and paste it again, lol)

and/or maybe you can do so within the GUI/Editor's scripting via this way:

run as script -> add new script -> 'variables' category/section -> 'set a variable or attribute' Script -> (see below)

set variable example_objectlist_variable = [EXPRESSION] NewObjectList ()

and then whatever the path for the 'list add' in the GUI/Editor (I don't know the GUI/Editor stuff well), taking a guess (too lazy to open up quest to check)

run as script -> add new script -> 'object' category/section -> 'add an Object to an object list --- or something like this probably' Script


there's also many built-in Objectlist Attributes you can use too:

the various Scopes ( http://docs.textadventures.co.uk/quest/scopes.html )
GetDirectChildren ( http://docs.textadventures.co.uk/quest/functions/getdirectchildren.html )
GetAllChildObjects ( http://docs.textadventures.co.uk/quest/functions/getallchildobjects.html )
AllObjects ( http://docs.textadventures.co.uk/quest/functions/allobjects.html )


checking for what items are in your Lists:

Got ( http://docs.textadventures.co.uk/quest/functions/corelibrary/got.html ) // this checks, via the 'foreach', if the item is in your 'ScopeInventory' (which is a list of all the Object's contained within the currently controlled Player Object, 'game.pov', such as the default 'player' Player Object. This is also known as your 'inventory')
ListContains ( http://docs.textadventures.co.uk/quest/functions/listcontains.html )
DictionaryContains ( http://docs.textadventures.co.uk/quest/functions/dictionarycontains.html )


iterating/cycling through all items in a list (this is what makes lists so useful), and then doing some action with whatever ones you want:

foreach ( http://docs.textadventures.co.uk/quest/scripts/foreach.html )


http://docs.textadventures.co.uk/quest/guides/using_lists.html
http://docs.textadventures.co.uk/quest/using_dictionaries.html

http://textadventures.co.uk/forum/samples/topic/5137/list-and-dictionary-extensive-guide-by-hk
http://textadventures.co.uk/forum/samples/topic/5138/explore-and-travel-code-sample-by-hk


and there's also the built-in 'displayverbs' and 'inventoryverbs' Stringlist Attribute, which determines what Verbs are/are-shown as the buttons and hyperlinks:

http://textadventures.co.uk/forum/samples/topic/5023/how-to-use-display-verbs-and-inventory-verbs-effectively


hegemonkhan

as for the Cloned Objects, you need something to identify/indicate/flag, which there's a few various ways of doing so:

  1. using the original Object's 'name' (ID) String Attribute, via the 'String Manipulation' Functions:

http://docs.textadventures.co.uk/quest/functions/ (scroll down to the very botton)

this requires you to have a naming structure though, for example you can use:

http://docs.textadventures.co.uk/quest/functions/string/startswith.html

// original Object's 'name' (ID) String Attribute:

object

<object name="object">
</object>

and the cloning adds a number to the name (as the 'name' String Attribute is the ID for quest, and thus 'names' must be unique), I've not actually used cloning yet, so I'm not sure whether it does this: 'object_1' or 'object1', nor do I known what number it starts with, lol. But, this does not matter, as we can use the 'StartsWith' Function, so:

if (StartsWith (this.name, "object") {
  // script
}

and some other useful ones:

EndsWith, Mid

for example, of using 'EndsWith' (this can NOT be used with clones obviously as consequtive numbers are added to the end):

<object name="katana_sword">
</object>

<object name="claymore_sword">
</object>

<object name="short_sword_sword">
</object>

<object name="short_bow_bow">
</object>

<object name="long_bow_bow">
</object>

// scripting example (you'd want to do this example scripting at the start of the game, via the 'game' Game object's 'start' Script):

foreach (object_variable, AllObjects()) {
  if (EndsWith (object_variable.name, "sword") {
    list add (game.sword_objectlist_attribute, object_variable)
  } else if (EndsWith (object_variable.name, "bow") {
    list add (game.bow_objectlist_attribute, object_variable)
  }
}
  1. using another Attribute:

the simplist would be the built-in 'alias' String Attribute:

// pretend we cloned a single 'orc' Object, instead of creating multiple ones (this is essentially what the 'cloning' is doing though, meh) as I've done below, lol:

// cloning, includes that Object's Attributes, so all the clones of the 'orc' Object will get an 'alias' String Attribute with the Value of 'orc', on them

<object name="orc">
  <attr name="alias" type="string">orc</attr>
</object>

<object name="orc_1">
  <attr name="alias" type="string">orc</attr>
</object>

<object name="orc_2">
  <attr name="alias" type="string">orc</attr>
</object>

<object name="orc_3">
  <attr name="alias" type="string">orc</attr>
</object>

<object name="orc_99">
  <attr name="alias" type="string">orc</attr>
</object>

<object name="dragon">
  <attr name="alias" type="string">dragon</attr>
</object>

<object name="dragon_1">
  <attr name="alias" type="string">dragon</attr>
</object>

<object name="dragon_2">
  <attr name="alias" type="string">dragon</attr>
</object>

<object name="dragon_99">
  <attr name="alias" type="string">dragon</attr>
</object>

and then in/with scripting you can check that Attribute:

if (this.alias = "orc") {
  // scripting
} else if (this.alias = "dragon") {
  // scripting
}

but, you can use any Attribute you want (and any type of Attribute, it does NOT have to be a String Attribute --- Boolean Attributes are used by a lot of people as they're simple: true/false, for example, an 'undead' Boolean Attribute, a 'zombie' would have it's 'undead' Boolean Attribute's Value set as 'true', whereas an 'orc' would have its 'undead' Boolean Attribute's Value set as 'false'), for example (I use a String Attribute again though):

<object name="orc">
  <attr name="monster_type_string_attribute" type="string">orc</attr>
</object>

<object name="orc_1">
  <attr name="monster_type_string_attribute" type="string">orc</attr>
</object>

<object name="orc_2">
  <attr name="monster_type_string_attribute" type="string">orc</attr>
</object>

<object name="orc_3">
  <attr name="monster_type_string_attribute" type="string">orc</attr>
</object>

<object name="orc_99">
  <attr name="monster_type_string_attribute" type="string">orc</attr>
</object>

<object name="dragon">
  <attr name="monster_type_string_attribute" type="string">dragon</attr>
</object>

<object name="dragon_1">
  <attr name="alias" type="string">water dragon</attr>
  <attr name="monster_type_string_attribute" type="string">dragon</attr>
</object>

<object name="dragon_2">
  <attr name="alias" type="string">fire dragon</attr>
  <attr name="monster_type_string_attribute" type="string">dragon</attr>
</object>

<object name="dragon_99">
  <attr name="alias" type="string">Bahamut (King of Dragons)</attr>
  <attr name="monster_type_string_attribute" type="string">dragon</attr>
</object>

'this' is a special keyword/keycommand/keyfunction/whatever (lol), which refers to the containing Object of the scripting (such as an Object's Verbs / Script Attributes) you're using the 'this' within, so it's very useful to use.

for example:

<object name="orc">
  <attr name="fight" type="script">
    msg ("You fight the " + this.name + " and kill it")
    // outputs: You fight the orc and kill it
  </attr>
</object>

<object name="dragon">
  <attr name="fight" type="script">
    msg ("You fight the " + this.name + " and kill it")
    // outputs: You fight the dragon and kill it
  </attr>
</object>

<verb>
  <property>fight</property>
  <pattern>fight</pattern>
  <defaultexpression>You can't fight that!</defaultexpression>
</verb>

but 'this' is really useful for more advanced usages that need/use dynamic objects (many objects or no structured names to them), such as a lot of scripting uses with Objects lists and/or otherwise working with many Objects.


hegemonkhan

you can take a look at this thread for some examples/ideas, it might be of some help for you with some things you might want to do in your game:

http://textadventures.co.uk/forum/quest/topic/3348/noobie-hks-help-me-thread#22483

http://textadventures.co.uk/forum/quest/topic/3348/noobie-hks-help-me-thread#22486 (the key/legend for all of my abrevs, I've learned to never use abrevs ever again, lol --- I got the damage types and defense types mixed up in my code. this is really old code of mine when I was first learning to code --- now, instead of abrevs, I'm very descriptive with my 'names' of Objects, Attributes, Functions, etc, lol --- it let's me know exactly what I'm working with as well as ensuring it's unique, lol. For example, I can do/have this/these: player.strength_integer_attribute = 100, and: player.strength_string_attribute="strong" --- but this is my own naming/labeling convention/system)


Lists and Dictionaries are not easy to understand and use if you're new to coding, so let us know if you need help with them.

you can also take a look at this too, but it's a bit advanced usage with Lists/Dictionaries, for another example of what you can do with lists/dictionaries:

http://textadventures.co.uk/forum/samples/topic/5138/explore-and-travel-code-sample-by-hk


TinFoilMkIV

@scrimshaw04 - care to share details on what kind of stuff your encounters are going to include?

Unless you want multiple of the same enemies showing up in a group in a way they need to be separate objects, I'd personally avoid cloning them altogether. You can create an out of game storage area and keep an instance of every enemy there and move them to the players location as needed for combat, then when all.

At one point I had a game where there was only a single enemy object and it was pulling all of it's stats from a library after the encounter determined what the enemy would be, tho that method is quite a bit trickier to set up.


scrimshaw04

You guys are incredibly helpful, huge thanks to you all!

Unfortunately thanks to Christmas I won't have time to work on this project again for a bit over a week (I'm away from home and only have my phone), so I'll have to go through and parse all of these suggestions then.

For context I'm working on a prehistory themed adventure, with a hunting mechanic. I want the player to be able to traverse a number of different biomes, each with its own set of wildlife that have a chance to randomly appear. The player can choose to either capture the animal alive (which adds an 'alive' version of the animal to their inventory which is a distinct object from the object that appears in the room) or dead (ditto, but with a dead version). I'm not planning on including combat for now, but I feel like it might be necessary eventually if I want to include larger creatures such as wolves. I definitely will not be including a levelling system, but something akin to hit points might be necessary. I'm drawing a lot of inspiration from Zelda on the level design, so perhaps heart containers are a reasonable compromise.

Even within these biomes there will be specific areas where certain creatures that inhabit it will or will not appear. For example, rabbits might appear in every part of a temperate forests, but salmon will only appear in a stream.

I realise this is a pretty ambitious first project, so at the moment I'm working on a fairly self-contained prologue, and restricting the number of mechanics. Hunting is a secondary mechanic, but it's one of the main things I wanted to include in the game, and it provides additional options for the player to solve some of the puzzles.

Ideally I'd like to just be able to tick a box for each room quickly that 'this animal' and 'that animal' can spawn there, but I suspect it will be more complicated than that.


hegemonkhan

here's a quick example for you to look at, ask about anything you don't understand, or anything you need help with.

(the animals are not located within the biomes, they're held/stored within an "internal" --- the person playing the game doesn't know about / isn't aware of / can't goto/visit it --- storage/data Object, for easier management/better organization for the game maker)

(let me know if you need a design where the animals are located within the biomes)

<object name="forest_room_object">
  <inherit name="biome_type" />
  // this will over-ride the 'hunting_animals_objectlist_attribute' of the Inherited Attribute from the 'biome_type' Object Type:
  <attr name="hunting_animals_objectlist_attribute" type="objectlist">wolf;deer;boar;bear</attr>
</object>

<object name="jungle_room_object">
  <inherit name="biome_type" />
  // this will over-ride the 'hunting_animals_objectlist_attribute' of the Inherited Attribute from the 'biome_type' Object Type:
   <attr name="hunting_animals_objectlist_attribute" type="objectlist">blah1;blah2;blah3</attr>
</object>

<object name="hunting_animals_data_object">
  <object name="unknown">
  </object>
  <object name="wolf">
    <inherit name="hunting_animals_type" />
  </object>
  <object name="boar">
    <inherit name="hunting_animals_type" />
  </object>
  <object name="deer">
    <inherit name="hunting_animals_type" />
  </object>
  <object name="bear">
    <inherit name="hunting_animals_type" />
    // these will over-ride the Inherited Attributes from the 'hunting_animals_type' Object Type:
    <attr name="heart_containers_life_string_attribute" type="string">10/10</attr>
    <attr name="current_heart_containers_life_integer_attribute" type="int">10</attr>
    <attr name="maximum_heart_containers_life_integer_attribute" type="int">10</attr>
  </object>
  <object name="blah1">
  </object>
  <object name="blah2">
  </object>
  <object name="blah3">
  </object>
</object>

<type name="biome_type">
  <attr name="hunting_animals_objectlist_attribute" type="objectlist">unknown</attr>
  <attr name="displayverbs" type="listextend">hunt</attr>
  <attr name="hunt" type="script">
    randomly_selected_animal = ObjectListItem (this.hunting_animals_objectlist_attribute, GetRandomInt (0, ListCount (this.hunting_animals_objectlist_attribute) - 1))
   // combat Function (so you can loop it)
   ask ("Do you want to kill it? (otherwise, you'll capture it alive)") {
     if (result) {
       randomly_selected_animal.condition_string_attribute = "dead"
     }
     // we don't need an 'else' as the default/initial condition is already set as being: 'alive', and this is also why I ask if you want to kill it instead of capturing it, making it insignificantly more efficient lol, as you don't have a 'not' operation, lol
    /*
    if you want to see it asking about capturing, it'd be like this:
    ask ("Do you want to capture it alive? (otherwise, you'll kill it)") {
       // again, there's no action to do for choosing to capture it, as it's already initially set as being alive
       if (not result) {
         randomly_selected_animal.condition_string_attribute = "dead"
       }
     }
     */
   }
   MoveObject (randomly_selected_animal, player) // or (it's the same thing): randomly_selected_animal.parent = player
   // actually, I need to do the 'CloneAndMove', for this design to work, my bad
  </attr>
</type>

<type name="hunting_animals_type">
  <attr name="condition_string_attribute" type="string">alive</attr> // using a String Attribute, allows for more than just two/binary choices of Values. Boolean: true/false, whereas, a String: normal/alive, dead, unconscious, poisoned, stunned, paralyzed, etc. Using a Stringlist Attribute allows for multiple Values at the same time, ie 'poisoned and petrified'
  <attr name="heart_containers_life_string_attribute" type="string">1/1</attr>
  <attr name="current_heart_containers_life_integer_attribute" type="int">1</attr>
  <attr name="maximum_heart_containers_life_integer_attribute" type="int">1</attr>
  <attr name="changedcurrent_heart_containers_life_integer_attribute" type="script">
    this.heart_containers_life_string_attribute = this.current_heart_containers_life_integer_attribute + "/" + this.maximum_heart_containers_life_integer_attribute
  </attr>
  <attr name="changedmaximum_heart_containers_life_integer_attribute" type="script">
    this.heart_containers_life_string_attribute = this.current_heart_containers_life_integer_attribute + "/" + this.maximum_heart_containers_life_integer_attribute
  </attr>
</type>

<verb>
  <property>hunt</property>
  <pattern>hunt<pattern>
  <defaultexpression>You can't hunt that!</attr>
</verb>

I didn't implement anything for this yet:

"
Even within these biomes there will be specific areas where certain creatures that inhabit it will or will not appear. For example, rabbits might appear in every part of a temperate forests, but salmon will only appear in a stream.
(scrim)
"


The Pixie

Use clones; it is not that bad.

The only trick is to make sure scripts on the original do not refer to itself directly. If you have an object called "salmon" that you want to clone, then in the scripts, rather than using "salmon", use "this", which is a special Quest code for whatever the script belongs to. That way, the script will reference the clone, rather than the original, when it is on the clone.

I would give every original an alias, and then if you want, say, to check if something is a salmon, you can just see if the alias is "salmon" (the name will be unique for each clone).

Keep all the originals in a room inaccessible to the player, and create clones with the CloneObjectAndMove function.


scrimshaw04

Cool, I'm starting to understand this system a little more. I'll need to try it myself, but that's a lot for me to go on.

So as I understand it, I after I use 'this' to clone an object, henceforth 'this' will refer to the cloned object? What if I want to run a seperate script that also refers to the clone, in that case 'this' wouldn't work. I'm unclear on what you mean by 'when it is on the clone', because I'm unsure how to apply a script to the clone in the first place.

I'm currently using a setup like you suggested and cloning objects from an inaccessible room, but without knowing how to refer to the cloned specifically it has some problems, like not being able to remove cloned objects correctly.

Apologies if this is a basic question, I'm completely new to programming/scripting.

And thanks again!


scrimshaw04

Ah but I guess I can check for objects in the room that the player is in with the alias of a creature instead then. In that case, if I were to spawn multiple instances of a creature in a room for some reason then I wouldn't be able to refer to a specific one, because they would all have the same alias, right? That might not be a problem for now, but it could be in the future.

For now my plan for the random generation system is this (I'm not sure how to write it in the correct syntax):

  1. Assign every room in the game with Boolean attributes for every possible creature that could be spawned (I should be able to do this by modifying the template room object, I think). Booleans set to true mean that creature can spawn there.
  2. Run a script every time the player enters a room that checks for each of these attributes. The same script makes a dice roll out of 100 for each creature to see if it successfully spawns.
  3. Create some more Boolean attributes on the template room that trigger different ways the creatures can spawn. If multiple creatures are selected, the default will be to add them all to a list then randomly choose one creature from that list to spawn. Unfortunately this might skew the probabilities a little but I think I can live with that. Other attributes might say that a room has a chance to spawn 2 or 3 instances of a selected creature, or spawn multiple species.
  4. Every time the player enters a new room, check for any leftover objects with an alias that matches any creature, or maybe even a 'creature' attribute and remove them.

I can probably refine that a little more, but that's the gist so far. I don't expect anyone to write the code for me, writing down my thoughts just helps me to clarify my idea.

The player also needs to be able to release trapped creatures from their inventory, which means they are completely removed from the game. For some reason I haven't managed to get that to work yet, but I'm sure I can figure it out.


hegemonkhan

this is some of the more advanced programming concepts, so don't worry if this is difficult to understand/follow:

(if you've been understanding any of this, wow, as this should be really hard when you're totally new to programming/coding... you start off slow... learning 1 thing at a time, laughs. Oh well, go ahead and jump right into coding, best way to learn, hehe! If you want to teach someone to swim, throw them into the deep end of a pool! hehe :D I jumped right into programming too, I first started with the 'character creation' guide, hehe. Took me quite a while to learn it... though I'm stupid, and you're likely much more smart, so you'll get stuff faster than I did, wish I was smart/smarter, oh well, sighs)

( http://docs.textadventures.co.uk/quest/guides/character_creation.html )

and here's my own struggle with learning to code (via leanring to code with quest), when I found it 4 years ago:

http://textadventures.co.uk/forum/quest/topic/3348/noobie-hks-help-me-thread

(and now, all from/thanks to quest, I'm taking programming classes in college and been shocked at how well quest has prepared me for them!)


about using 'this', maybe this (sorry, a not-intended bad pun) will help:

(the 'orc_1' and 'orc_2' Objects, represent doing/using cloning)

conceptually, instead of doing this (ugh!), for example:

<object name="player">
  <attr name="damage_integer_attribute" type="int">10</attr>
  <attr name="current_life_integer_attribute" type="int">999</attr>
</object>

<object name="orc_1">
  <attr name="current_life_integer_attribute" type="int">100</attr>
  <attr name="condition_string_attribute" type="string">alive</attr>
  <attr name="damage_integer_attribute" type="int">5</attr>
  <attr name="displayverbs" type="listextend">fight</attr>
  <attr name="fight" type="script"><![CDATA[
    if (orc_1.condition_string_attribute = "alive") {
      orc_1.current_life_integer_attribute = orc_1.current_life_integer_attribute - player.damage_integer_attribute
      if (orc_1.current_life_intger_attribute < 1) {
        orc_1.condition_string_attribute = "dead"
      } else {
        player.current_life_integer_attribute = player.current_integer_attribute - orc_1.damage_integer_attribute
        if (player.current_life_integer_attribute < 1) {
          msg ("You were killed by the " + orc_1.name + ".")
          msg ("GAME OVER")
          finish
        }
      }
    } else {
      msg ("The " + orc_1.name + " is already dead, silly.")
    }
  ]]></attr>
</object>

<object name="orc_2">
  <attr name="current_life_integer_attribute" type="int">100</attr>
  <attr name="condition_string_attribute" type="string">alive</attr>
  <attr name="damage_integer_attribute" type="int">5</attr>
  <attr name="displayverbs" type="listextend">fight</attr>
  <attr name="fight" type="script"><![CDATA[
    if (orc_2.condition_string_attribute = "alive") {
      orc_2.current_life_integer_attribute = orc_2.current_life_integer_attribute - player.damage_integer_attribute
      if (orc_2.current_life_intger_attribute < 1) {
        orc_2.condition_string_attribute = "dead"
      } else {
        player.current_life_integer_attribute = player.current_integer_attribute - orc_2.damage_integer_attribute
        if (player.current_life_integer_attribute < 1) {
          msg ("You were killed by the " + orc_2.name + ".")
          msg ("GAME OVER")
          finish
        }
      }
    } else {
      msg ("The " + orc_2.name + " is already dead, silly.")
    }
  ]]></attr>
</object>

// imagine if we had 'orc_1' to 'orc_99' Objects.... that's a ton of code! YUCK! UGH!

// ---------------------------------------------------------

// conceptually, doing this is much better (but still MAJOR YUCK/UGH!):

<object name="player">
  <attr name="damage_integer_attribute" type="int">10</attr>
  <attr name="current_life_integer_attribute" type="int">999</attr>
</object>

<object name="orc_1">
  <attr name="current_life_integer_attribute" type="int">100</attr>
  <attr name="condition_string_attribute" type="string">alive</attr>
  <attr name="damage_integer_attribute" type="int">5</attr>
  <attr name="displayverbs" type="listextend">fight</attr>
  <attr name="fight" type="script"><![CDATA[
    if (this.condition_string_attribute = "alive") { // this 'this' is contained within the 'orc_1' Object, and thus the 'this' is an Object reference to the 'orc_1' Object (conceptually this will help you understand it I hope: quest will replace 'this' with 'orc_1')
      this.current_life_integer_attribute = this.current_life_integer_attribute - player.damage_integer_attribute // this 'this' is contained within the 'orc_1' Object, and thus the 'this' is an Object reference to the 'orc_1' Object (conceptually this will help you understand it I hope: quest will replace 'this' with 'orc_1')
      if (this.current_life_intger_attribute < 1) { // // this 'this' is contained within the 'orc_1' Object, and thus the 'this' is an Object reference to the 'orc_1' Object (conceptually this will help you understand it I hope: quest will replace 'this' with 'orc_1')
        this.condition_string_attribute = "dead" // this 'this' is contained within the 'orc_1' Object, and thus the 'this' is an Object reference to the 'orc_1' Object (conceptually this will help you understand it I hope: quest will replace 'this' with 'orc_1')
      } else {
        player.current_life_integer_attribute = player.current_integer_attribute - this.damage_integer_attribute // this 'this' is contained within the 'orc_1' Object, and thus the 'this' is an Object reference to the 'orc_1' Object (conceptually this will help you understand it I hope: quest will replace 'this' with 'orc_1')
        if (player.current_life_integer_attribute < 1) {
          msg ("You were killed by the " + this.name + ".") // this 'this' is contained within the 'orc_1' Object, and thus the 'this' is an Object reference to the 'orc_1' Object (conceptually this will help you understand it I hope: quest will replace 'this' with 'orc_1')
          msg ("GAME OVER")
          finish
        }
      }
    } else {
      msg ("The " + this.name + " is already dead, silly.") // this 'this' is contained within the 'orc_1' Object, and thus the 'this' is an Object reference to the 'orc_1' Object (conceptually this will help you understand it I hope: quest will replace 'this' with 'orc_1')
    }
  ]]></attr>
</object>

<object name="orc_2">
  <attr name="current_life_integer_attribute" type="int">100</attr>
  <attr name="condition_string_attribute" type="string">alive</attr>
  <attr name="damage_integer_attribute" type="int">5</attr>
  <attr name="displayverbs" type="listextend">fight</attr>
  <attr name="fight" type="script"><![CDATA[
    if (this.condition_string_attribute = "alive") { // this 'this' is contained within the 'orc_2' Object, and thus the 'this' is an Object reference to the 'orc_2' Object (conceptually this will help you understand it I hope: quest will replace 'this' with 'orc_2')
      this.current_life_integer_attribute = this.current_life_integer_attribute - player.damage_integer_attribute // this 'this' is contained within the 'orc_2' Object, and thus the 'this' is an Object reference to the 'orc_2' Object (conceptually this will help you understand it I hope: quest will replace 'this' with 'orc_2')
      if (this.current_life_intger_attribute < 1) { // this 'this' is contained within the 'orc_2' Object, and thus the 'this' is an Object reference to the 'orc_2' Object (conceptually this will help you understand it I hope: quest will replace 'this' with 'orc_2')
        this.condition_string_attribute = "dead" // this 'this' is contained within the 'orc_2' Object, and thus the 'this' is an Object reference to the 'orc_2' Object (conceptually this will help you understand it I hope: quest will replace 'this' with 'orc_2')
      } else {
        player.current_life_integer_attribute = player.current_integer_attribute - this.damage_integer_attribute // this 'this' is contained within the 'orc_2' Object, and thus the 'this' is an Object reference to the 'orc_2' Object (conceptually this will help you understand it I hope: quest will replace 'this' with 'orc_2')
        if (player.current_life_integer_attribute < 1) {
          msg ("You were killed by the " + this.name + ".") // this 'this' is contained within the 'orc_2' Object, and thus the 'this' is an Object reference to the 'orc_2' Object (conceptually this will help you understand it I hope: quest will replace 'this' with 'orc_2')
          msg ("GAME OVER")
          finish
        }
      }
    } else {
      msg ("The " + this.name + " is already dead, silly.") // this 'this' is contained within the 'orc_2' Object, and thus the 'this' is an Object reference to the 'orc_2' Object (conceptually this will help you understand it I hope: quest will replace 'this' with 'orc_2')
    }
  ]]></attr>
</object>

// -------------------------------------------------

// and this is even/extremely much better (though still not the best... but this is good enough for this concept lesson):

<object name="player">
  <inherit name="character_type" /> // Object Types ('Types') or 'groups/classes' will add their Attributes, as Inherited Attributes, to this 'player' Player Object, and thus if they use the 'this', that 'this' scripting, now added to this 'player' Player Object, will now be an Object reference to the 'player' Player Object (conceptually: quest will replace 'this' with 'player') 
  <attr name="damage_integer_attribute" type="int">10</attr>
  <attr name="current_life_integer_attribute" type="int">999</attr>
</object>

<object name="orc_1">
  <inherit name="orc_type" /> // Object Types ('Types') or 'groups/classes' will add their Attributes, as Inherited Attributes, to this 'orc_1' Object, and thus if they use the 'this', that 'this' scripting, now added to this 'orc_1' Object, will now be an Object reference to the 'orc_1' Object (quest will replace 'this' with 'orc_1') 
</object>

<object name="orc_2">
  <inherit name="orc_type" /> // Object Types ('Types') or 'groups/classes' will add their Attributes, as Inherited Attributes, to this 'orc_2' Object, and thus if they use the 'this', that 'this' scripting, now added to this 'orc_2' Object, will now be an Object reference to the 'orc_2' Object (quest will replace 'this' with 'orc_2') 
</object>

<type name="orc_type">
  <inherit name="monster_type" />
  <attr name="current_life_integer_attribute" type="int">100</attr>
  <attr name="damage_integer_attribute" type="int">5</attr>
</type>

<type name="monster_type">
  <inherit name="character_type" />
  <attr name="condition_string_attribute" type="string">alive</attr>
  <attr name="displayverbs" type="listextend">fight</attr>
  <attr name="fight" type="script"><![CDATA[
    if (this.condition_string_attribute = "alive") {
      this.current_life_integer_attribute = this.current_life_integer_attribute - player.damage_integer_attribute
      if (this.current_life_intger_attribute < 1) {
        this.condition_string_attribute = "dead"
      } else {
        player.current_life_integer_attribute = player.current_integer_attribute - this.damage_integer_attribute
        if (player.current_life_integer_attribute < 1) {
          msg ("You were killed by the " + this.name + ".")
          msg ("GAME OVER")
          finish
        }
      }
    } else {
      msg ("The " + this.name + " is already dead, silly.")
    }
  ]]></attr>
</type>

<type name="character_type">
  <attr name="damage_integer_attribute" type="int">1</attr>
  <attr name="current_life_integer_attribute" type="int">1</attr>
</type>

this example of mine above, uses predetermined/known (static usage) Objects, well, when we're (dynamic usage): randomly selecting who knows what Objects and want to apply the same stuff to any of them (such as via cloning), this is were the 'this' special keyword/keycommand/keyfunction/whatever-exactly-it-is-lol, really shines!


hegemonkhan

"
In that case, if I were to spawn multiple instances of a creature in a room for some reason then I wouldn't be able to refer to a specific one, because they would all have the same alias, right? That might not be a problem for now, but it could be in the future.
(scrim)
"


here's an example:

<object name="animal_room">
  <attr name="animal_objectlist_attribute" type="objectlist">bat;rat</attr>
</object>

<object name="bat_clone_1">
  <attr name="alias" type="string">fruit bat</attr>
</object>

<object name="bat_clone_2">
  <attr name="alias" type="string">vampire bat</attr>
</object>

<object name="rat_clone_1">
</object>

<object name="rat_clone_2">
</object>

// scripting example:

foreach (object_variable, animal_room.animal_objectlist_attribute) {
  if (StartsWith (object_variable.name, "bat") and object_variable.alias = "vampire bat") {
    msg ("We've specifically got-to the 'vampire bat' from our bat clones")
    // we'd do some action of course, which would be with/to our 'vampire bat', such as moving it within the 'player' Playe object:
    MoveObject (object_variable, player)
  }
}

hegemonkhan

Booleans are nice and have their uses, but take a look at this:

your usually 'status effects / conditions' in RPGs:

player.poisoned = false
player.asleep = false
player.petrified = false
player.confused = false
player.silenced = false
etc etc etc

YUCK! (N Attributes)

you get poisoned:

player.poisoned = true
player.asleep = false
player.petrified = false
player.confused = false
player.silenced = false
etc etc etc

you get silenced:

player.poisoned = false
player.asleep = false
player.petrified = false
player.confused = false
player.silenced = true
etc etc etc

you get poisoned and silenced (having multiple Boolean Attributes, DO allow for multiple conditions, a positive, whereas a single String Attribute does not, however, a single Stringlist Attribute does, and it, a single Stringlist Attribute, is (for really big/complex designs) better than multiple Boolean Attributes):

player.poisoned = true
player.asleep = false
player.petrified = false
player.confused = false
player.silenced = true
etc etc etc

// scripting example:

if (player.normal) {
  msg ("you're normal")
} else if (player.poisoned) {
  msg ("You're poisoned")
} else if (player.asleep) {
  msg ("You're asleep")
} else if (player.silenced) {
  msg ("You're silenced")
} else if (player.petrified) {
  msg ("You're petrified")
} else if (player.poisoned and player.confused) {
  msg ("You're poisoned and confused")
}
// etc 'else ifs'...

YUCK!


whereas, much better (a single Attribute):

player.condition_string_attribute = "normal"

you get poisoned:

player.condition_string_attribute = "poisoned"

you get silenced:

player.condition_string_attribute = "silenced"

we can't have two conditions at the same time with a single String Attribute, however.

// scripting example:

if (player.condition_string_attribute = "normal") {
  msg ("You're normal")
} else if (player.condition_string_attribute = "poisoned") {
  msg ("You're poisoned")
} else if (player.condition_string_attribute = "silenced") {
  msg ("You're silenced")
}
// etc 'else ifs'....

a bit better... unless you need/want multiple effects/conditions at the same time...


and (for a really big conplex game) really better (a single List Attribute):

player.condition_stringlist_attribute = NewStringList ()
list add (player.condition_stringlist_attribute, "normal")

you get poisoned (continuing from above state):

list add (player.condition_stringlist_attribute, "poisoned")
list remove (player.condition_stringlist_attribute, "normal")

you get silenced (continuing from above state):

list add (player.condition_stringlist_attribute, "silenced")
list remove (player.condition_stringlist_attribute, "poison")

you get poisoned and silenced (continuing from above state):

list add (player.condition_stringlist_attribute, "poisoned")

you get poisoned and silenced and confused (continuing from above state):

list add (player.condition_stringlist_attribute, "confused")

you get poisoned and silenced and confused (continuing from intial/default state: 'normal' only):

list add (player.condition_stringlist_attribute, "confused")
list add (player.condition_stringlist_attribute, "poisoned")
list add (player.condition_stringlist_attribute, "silenced")
list remove (player.condition_stringlist_attribute, "normal")

// scripting example:

<object name="player">
  // I don't know if the special 'changedXXX' Script Attribute can work with Lists... if not... than we'd have to use a Turnscript Element instead:
  <attr name="changedcondition_stringlist_attribute" type="script"><![CDATA[
    foreach (string_variable, player.condition_stringlist_attribute) {
      if (string_variable = "poisoned") {
        player.current_life_attribute = player.current_life_attribute - 50
      }
    }
 ]]></attr>
 <attr name="changedcurrent_life_attribute" type="script"><![CDATA[
   if (player.current_life_attribute < 1) {
     msg (You've died.")
     msg ("GAME OVER")
     finish
   }
 ]]></attr>
</object>

The Pixie

So as I understand it, I after I use 'this' to clone an object, henceforth 'this' will refer to the cloned object? What if I want to run a seperate script that also refers to the clone, in that case 'this' wouldn't work. I'm unclear on what you mean by 'when it is on the clone', because I'm unsure how to apply a script to the clone in the first place.

Scripts are all attached to something, whether that is a turn script, a command, a room or an object; the script is an attribute of the thing. The "this" refers to the thing the script is an attribute of.

When you create a clone, it gets all the attributes of the original, including the scripts. If the script attributes use "this", they now refer to the clone.

Ah but I guess I can check for objects in the room that the player is in with the alias of a creature instead then. In that case, if I were to spawn multiple instances of a creature in a room for some reason then I wouldn't be able to refer to a specific one, because they would all have the same alias, right? That might not be a problem for now, but it could be in the future.

You could have a script that searches the room for monsters with that alias. That is easier than searching for monsters that have a name that start with something, I would say. There is no trivial way to do this.

For now my plan for the random generation system is this (I'm not sure how to write it in the correct syntax):

I would create a type for each room-biome, and have the script in the type. The room with the forest_with_stream type will then produce creatures of the appropriate type (and as with cloning, use "this" to refer to the room itself).

If you run the script whenever the player enters the room, the player can get more to appear just by stepping into another room then stepping back into this room. You might want to think about a delay.


scrimshaw04

Oh that makes so much more sense! I can use a separate script to clone one of the template creatures, and them have a script using 'this' on the creature object that triggers when it's in the same room as the player.

And I see what you're saying about the booleans creating a tonne of unnecessary code. I don't really understand adding or removing things from lists yet because I haven't used them, but I'm sure I'll understand it more when I can actually put it into practice.

Thanks everyone, that's heaps for me to work on. I'll be back once I can get programming once more.

Also thanks hegemon. :) I work in motion graphics, which occasionally touches on some simple scripting (I believe After Effects uses JavaScript?), so maybe I can't say that I'm that new. AE also requires you to plan ahead and think about parent-child relationships, so I'm sure that helps me to understand the logic a little more. Games are my real passion, but normally I'm more of a visual person.


scrimshaw04

Sorry guys, I have a very simple question. In a script like this:

foreach (obj, AllObjects()) {
  if (DoesInherit(obj, "object_animal")) {
    RemoveObject (obj.this)
  }
  else {
  }
} 

What is the correct syntax for using 'this' after RemoveObject? The example I have here is obviously incorrect.

My goal is to check for any objects of that inherit the 'object_animal' type when the player enters a room and remove them.


Anonynn

RemoveObject (obj.this)

I believe this would be the best route. I think I have it correct.

RemoveObject (this)

Anonynn.


scrimshaw04

Hm, I've tried that and it doesn't seem to be working. Perhaps there's something else in my script that's wrong?


scrimshaw04

Another question: player.parent refers to the room which the player is currently in. So how do I refer to an attribute of player.parent in a script elsewhere?

For example, I want to run a script every time the player enters a room. The script only runs if the room inherits a certain type, or if a certain boolean is set to 'true'. What would be the correct syntax for checking one of these attributes?

Obviously something like player.parent (DoesInherit(obj, "object_spawn")) is incorrect, but I'm not sure how to write it correctly. Would I use 'for'? As in, for (player.parent) > (DoesInherit(obj, "object_spawn"))?


The Pixie

Not sure quite what you are trying t do, but this maybe:

RemoveObject (obj)

To access the attribute of an attribute, just use anther dot:

player.parent.attribute

But the code you have looks like you are doing something a bit different? Are you trying to find all the objects in the room? That would be different.


scrimshaw04

Ah, RemoveObject (obj) is close! However it seems to prevent any other objects of that type from spawning, unfortunately.

What I'm trying to do is make sure that any objects that have previously been spawned are removed before the player enters. Ideally the cloned objects would be removed as soon as the player enters another room, but I haven't figured out a solution for that yet. Perhaps running a check on all objects of a particular type and removing ones that don't share a parent with the player?

Here's the code I'm using currently

room = player.parent
foreach (obj, AllObjects()) {
  if (obj.parent = player.parent and DoesInherit(obj, "object_animal")) {
    RemoveObject (obj)
  }
  else {
  }
}
if (room.spawn = true) {
  if (room.spawn_rabbit = true) {
    chance_rabbit = GetRandomInt (0, 100)
    chance_rabbit_spawn = room.spawn_rabbit_chance
    if (chance_rabbit <= chance_rabbit_spawn) {
      CloneObjectAndMove (rabbit, room)
    }
  }
  if (room.spawn_doe = true) {
    chance_doe  = GetRandomInt (0, 100)
    chance_doe_spawn = room.spawn_doe_chance
    if (chance_doe <= chance_doe_spawn) {
      CloneObjectAndMove (doe, room)
    }
  }
  if (room.spawn_salmon = true) {
    chance_salmon = GetRandomInt (0, 100)
    chance_salmon_spawn = room.spawn_salmon_chance
    if (chance_salmon <= chance_salmon_spawn) {
      CloneObjectAndMove (salmon, room)
    }
  }
}
else {
}

The other unfortunate thing about this script is that it allows multiple animals to spawn at once. I haven't been able to figure out a way to arrange it so that only one animal will spawn at a time, without drastically compromising the probabilities of some of them. I haven't quite grasped lists yet, but I'm sure they'd be useful for this.

Thanks again!

EDIT: I'm also wrestling with trying to assign the animals a size integer when they're spawned. Each animal will have a range of sizes that it can spawn at that are represented numerically (a rabbit might be 4 - 20 for example). I'm trying to figure out if I can assign that integer to the clone and have it stay persistent for just that clone. Changedparent seems to work when the object is moved, but not when it's cloned.


TinFoilMkIV

For the first section of your code

foreach (obj, AllObjects()) {
  if (obj.parent = player.parent and DoesInherit(obj, "object_animal")) {
    RemoveObject (obj)
  }
  else {
  }
}

I'd suggest using http://docs.textadventures.co.uk/quest/functions/getdirectchildren.html in the future, helps keep the code a bit cleaner although your current version is functionally the same, just with an extra step.
ie:

foreach (obj, GetDirectChildren (player.parent)) {
  if (DoesInherit(obj, "object_animal")) {
    RemoveObject (obj)
  }
  else {
  }
}

As for making sure encounter objects get cleaned up, is there a scenario where you want the player to come back to a room and have those objects still be there? If not then I'd clean them up when the player is done with the encounter and/or leaves the room instead of waiting for them to re-enter.

As for how/when you'd clear something like that it seems like you're already aware of using changed attributes such as 'changedparent'. You can use this on the player to clear things as you leave a room and only have to check that room due to the changed attribute script having access to a temporary variable called 'oldvalue'. It stores the previous value of the attribute in question when the changed script triggers. In the case of 'changedparent', 'oldvalue' contains the previous parent, so you can check the previous rooms contents and clear them when the player's parent changes.

For your animal spawns you could either modify the original checks to prevent multiple spawns (probably more complicated) or add a check after the spawns are run and remove extras.

One question to answer there is do you want certain animals to have priority over others when spawned at the same time? If so the solution is relatively simple as you run an if check in the order of priority and remove all the extras when the highest priority animal is detected. Otherwise you'll probably want to build a list of all the animals that passed the spawn check then select one from that list at random to keep. You could either do a room check for animal objects again to build the list, or the way I'd do it would be to build a list when your running your spawn chances and not create any clones at that point. Then after all the spawn chances are checked you run a check on the spawn list, if empty, do nothing, if the list contains more than one item, select one at random and spawn that animal.

ie: check the room for multi spawns

//clear room spawnlist before running checks
room.spawnlist = NewObjectList ()
foreach (obj, GetDirectChildren (player.parent)){
 if(DoesInherit(obj, "object_animal")){
  //add animal objects to the spawn list
  list add (room.spawnlist, obj)
 }
}
if (ListCount (room.spawnlist) > 1) {
//more than one animal spawned, pick one at random
rand = GetRandomInt (0, ListCount (room.spawnlist) -1)
spawned = ObjectListItem (room.spawnlist, rand)
//
//animal selected, remove all others
 foreach (obj, room.spawnlist) {
   if (not obj = spawned) {
     RemoveObject (obj)
   }
 }
}
//other cases are a single spawn or no spawns, nothing special happens so no check required

ie: create a list before spawning

//clear room spawnlist before running checks
room.spawnlist = NewObjectList ()
//
//example of a spawn check
if (room.spawn = true) {
  if (room.spawn_rabbit = true) {
    chance_rabbit = GetRandomInt (0, 100)
    chance_rabbit_spawn = room.spawn_rabbit_chance
    if (chance_rabbit <= chance_rabbit_spawn) {
//**
//add item to list instead of cloning
      list add (room.spawnlist, rabbit)
    }
//...
//check list for spawn count
if (ListCount (room.spawnlist) = 1){
 //only one spawn so clone the object
 CloneObjectAndMove (ObjectListItem (room.spawn, 0), room)
}
else if (ListCount (room.spawnlist) > 1){
 //more than one spawned, select one at random
 rand = GetRandomInt (0, ListCount (room.spawnlist) -1)
 //clone selected
 CloneObjectAndMove (ObjectListItem (room.spawn, rand), room)
}
else{
 //only other possibility is no spawn this section isn't necessary as nothing happens.
}

And I do generally comment all my scripts almost this much so I can remember what parts do what when I inevitably have to go back and make changes to fix things I forgot to account for, heh.


hegemonkhan

this has surely already been explained (hadn't read the other posts yet), but just to give my own explanation anyways:

the 'foreach' Function creates/uses a temporary placeholder Variable:

foreach (NAME_OF_TEMPORARY_PLACEHOLDER_VARIABLE, NAME_OF_OBJECT.NAME_OF_LIST_ATTRIBUTE) { /* scripting */ }

the temporary placeholder variable stores, each of the items in the list individually one after the other, thus the 'each' in foreach, so it works like this, for an example:

team_object.team_list_attribute = split ("joe;jeff;jim", ";")

foreach (team_member, team_object.team_list_atribute) {
-> msg (team_member)
}

// effectively, what it does:

team_member = "joe"
msg (team_member)
team_member = "jeff"
msg (team_member)
team_member = "jim"
msg (team_member)

// output:
joe
jeff
jim

you can of course do more/various/different scripting with your temporary placeholder variable, whether it's value is an Object (from an Objectlist Attribute) or a String (from a Stringlist Attribute) --- you can even also effectively convert a String into an Object via, for example: GetObject (team_member_as_a_string_variable) --- , for example of using Objects and an Objectlist Attribute:

<object name="team_object">
  <attr name="team_objectlist_attribute" type="objectlist">joe;jeff;jim</attr>
</object>

<object name="joe">
  <attr name="run_laps" type="script">
    msg (this.name + " runs laps")
  </attr>
</object>

<object name="jeff">
  <attr name="run_laps" type="script">
    msg (this.name + " runs laps")
  </attr>
</object>

<object name="jim">
  <attr name="run_laps" type="script">
    msg (this.name + " runs laps")
  </attr>
</object>

// scripting:

foreach (team_member, team_object.team_objectlist_attribute) {
  invoke (team_member.run_laps)
}

// effectively, it's doing this:

team_member = joe
invoke (team_member.run_laps)
invoke (joe.run_laps)
team_member = jeff
invoke (team_member.run_laps)
invoke (jeff.run_laps)
team_member = jim
invoke (team_member.run_laps)
invoke (jim.run_laps)

// output:

joe runs laps
jeff runs laps
jim runs laps

scrimshaw04

Right, I think I get it. Invoke seems pretty handy, it might explain why the scripts that I've attached to the object_animal type aren't working.

Here's an example: I want to create a script attribute on all animals so that if they're particularly large or particularly small the script will change their alias to describe that. I have a size integer attribute which is randomised between a specified range.

So the script I threw together quickly is

size = this.animal_size
size_max = this.animal_size_max
ratio = size / size_max
if (ratio >= 0.8) {
  this.animal_size_adj = "Large"
}
else if (ratio >= 0.2) {
  this.animal_size_adj = "Small"
}
else {
  this.animal_size_adj = ""
}

And the alias is [#animal_size_adj] Rabbit, for example

It hasn't worked so far, but if I invoke (this.animal_size_adj_script) before the animal spawns, maybe that will work.

Also, for reference, here's the spawning script I have so far, which exists in the 'script when entering a room' field.

foreach (obj, GetDirectChildren (player.parent)) {
  if (DoesInherit(obj, "object_animal")) {
    destroy (obj)
  }
  else {
  }
}
if (room.spawn = true) {
  if (room.spawn_rabbit = true) {
    msg ("Spawn rabbit")
    chance_rabbit = GetRandomInt (0, 100)
    chance_rabbit_spawn = room.spawn_rabbit_chance
    if (chance_rabbit <= chance_rabbit_spawn) {
      ClonePrototype (rabbit, room)
    }
  }
  if (room.spawn_doe_whitetailed = true) {
    msg ("Spawn deer")
    chance_deer = GetRandomInt (0, 100)
    chance_deer_spawn = room.spawn_deer_chance
    if (chance_deer <= chance_deer_spawn) {
      ClonePrototype (deer, room)
    }
  }
  if (room.spawn_salmon = true) {
    msg ("Spawn Salmon")
    chance_salmon = GetRandomInt (0, 100)
    chance_salmon_spawn = room.spawn_salmon_chance
    if (chance_salmon <= chance_salmon_spawn) {
      ClonePrototype (salmon, room)
    }
  }
}
else {
}
room.spawnlist = NewObjectList ()
foreach (obj, GetDirectChildren (player.parent)) {
  if (DoesInherit(obj, "object_animal")) {
    // add animal objects to the spawn list
    list add (room.spawnlist, obj)
  }
}
if (ListCount (room.spawnlist) > 1) {
  // more than one animal spawned, pick one at random
  rand = GetRandomInt (0, ListCount (room.spawnlist) -1)
  spawned = ObjectListItem (room.spawnlist, rand)
  //
  // animal selected, remove all others
  foreach (obj, room.spawnlist) {
    if (not obj = spawned) {
      RemoveObject (obj)
    }
  }
}

I've also discovered Pixie's ClonePrototype function, which seems like it could be very handy if I want to, say, have different colours or descriptors on the animals that spawn. I don't really understand how functions work yet though, so that's something I'll need to look into further.

Unfortunately I'm still rather busy thanks to family/new years, but hopefully I'll be back into the swing of things soon.


hegemonkhan

Script Attributes (and the 'ScriptDictionaryItem(xxx)' Function for Scriptdictionary Attributes) require the use of 'do' ( http://docs.textadventures.co.uk/quest/scripts/do.html ) or 'invoke' ( http://docs.textadventures.co.uk/quest/scripts/invoke.html ) Script/Functions (the 'do' Function is more powerful/useful than the 'invoke' as it can concatenate, but for simple Script Attribute usage, invoke is fine).

A Script Attribute (+ Delegate usage) is just like a Function (+ Parameters/Arguments), so just as you have to 'call' a Function to activate/use/do it, so too do you have to 'invoke' or 'do' a Script Attribute to activate/use/do it.


hegemonkhan

"
size = this.animal_size
size_max = this.animal_size_max
ratio = size / size_max
if (ratio >= 0.8) {
this.animal_size_adj = "Large"
}
else if (ratio >= 0.2) {
this.animal_size_adj = "Small"
}
else {
this.animal_size_adj = ""
}
(scrim)
"


your code looks fine

to then change the 'alias', you can do something like this:

string concatenation, and using this:

http://docs.textadventures.co.uk/quest/functions/corelibrary/getdisplayalias.html

size = this.animal_size
size_max = this.animal_size_max
ratio = size / size_max
if (ratio >= 0.8) {
  this.animal_size_adj = "Large"
}
else if (ratio >= 0.2) {
  this.animal_size_adj = "Small"
}
else {
  this.animal_size_adj = ""
}
this.alias = this.animal_size_adj + " " + GetDisplayAlias (this)
msg (this.alias)

// for example output:
//
// large rabbit
// or
// small rabbit
// or
// (space) rabbit

though, you can tweak the code, to make it a bit better:

size = this.animal_size
size_max = this.animal_size_max
ratio = size / size_max
if (ratio >= 0.8) {
  this.animal_size_adj = "Large"
}
else if (ratio >= 0.2) {
  this.animal_size_adj = "Small"
}
else {
  this.animal_size_adj = null
}
if (not GetString (this, "animal_size_adj") = null) { // maybe you can just do this instead: if (HasString (this, "animal_size_adj")) {
  this.alias = this.animal_size_adj + " " + GetDisplayAlias (this)
}
msg (this.alias)

// for example output:
//
// large rabbit
// or
// small rabbit
// or
// rabbit

scrimshaw04

Great, thank you! I can invoke the script, but unfortunately it gives me the error: "Unknown object or variable 'this'".

Which seems strange to me, because if 'this' was going to be recognised anywhere I figured it would be when it was inside an attribute script. Obj doesn't work either.


The Pixie

You cannot use this if you call a script with invoke; use do instead.


scrimshaw04

Ah okay, I missed that. I should be able to test it out tomorrow, thanks!


hegemonkhan

ah, thanks Pixie, sorry about that, didn't realize 'this' couldn't be used with 'invoke', now 'do' is even more superior to 'invoke', lol


scrimshaw04

Success! It's finally working, thank a tonne everyone! If anyone is referencing this post in the future, the script I used to 'do' the script attribute on the spawns was

foreach (obj, GetDirectChildren (player.parent)) {
  if (DoesInherit(obj, "object_animal")) {
    do (obj, "animal_size_adj_script")
  }
  else {
  }
}

This topic is now closed. Topics are closed after 60 days of inactivity.

Support

Forums