Cloning an object with it's original attributes/adding attributes to a random clone?
Mochaaa
14 Jan 2020, 21:56Hi, complete Quest + coding noob here. For my game I wanted to set up a shop, but I wanted it to restock after a certain amount of time/on trigger. For now I've set the trigger for when the player leaves the shop, and I've made it clone a random object from a room and put it into the stock room. However, the value of the object doesn't follow the clone, and I don't know how to add value to a new object if the name of the new object is random. Sorry if this is worded weirdly, but can anyone help?

Io
14 Jan 2020, 22:40Try something like this. And pardon my psuecode:
switch case(ClonedObject.alias){
case("Cheap Thing"){
ClonedObject.value=5
}
case("Medium Thing"){
ClonedObject.value=15
}
case("Expensive Thing"){
ClonedObject.value=9999
}
}
Hope this helps!
mrangel
15 Jan 2020, 02:40the value of the object doesn't follow the clone
Why not? If the value is an attribute of the object, the clone will have the same value.
Is the value stored somewhere other than on the object?
I don't know how to add value to a new object if the name of the new object is random
If you're creating the object using the CloneObject functions, they return a reference to the new clone.
So to set an attribute on the new clone, you can do:
newClone = CloneObjectAndMove (Some Original Object, Stock Room)
newClone.value = 65
or if your shop has a dictionary holding a list of stock and their values (which is a weird thing to do, but it would explain why cloning the objects doesn't retain their value), it would be something like:
newClone = CloneObjectAndMove (Some Original Object, Stock Room)
dictionary add (Shop.pricelist, newObject.name, 35)
Mochaaa
15 Jan 2020, 03:03Sadly it returned with a
Error running script: Error compiling expression 'ClonedObject.alias': Unknown object or variable 'ClonedObject'
Do you know how to fix it?
Mochaaa
15 Jan 2020, 03:18"Why not? If the value is an attribute of the object, the clone will have the same value.
Is the value stored somewhere other than on the object?"
When I increase the price of the object in the inventory menu, object.price changes.
When I look in the debugger, object.price does follow through, but for some reason the value doesn't show up in game and I can't buy it.
I'm using
CloneObjectAndMove (PickOneChild (CompleteStockList), Stock1)
To make the clone and move it to the stock room.
mrangel
15 Jan 2020, 09:08
Error running script: Error compiling expression 'ClonedObject.alias': Unknown object or variable 'ClonedObject'
That piece of code assumes that you have a variable named ClonedObject to access the clone, and sets its price based on its alias. This is a really strange thing to do and I wouldn't recommend it.
If you have a clone object for which you want to find out the original's name, it's more usual to use object.prototype
.
When I look in the debugger, object.price does follow through, but for some reason the value doesn't show up in game and I can't buy it.
The clones will all have the same attributes as the original at the time they were created.
When you increase object.price
, are you increasing the price of the original, or a clone that's already been created?
Or do you want to increase the price of both clones and the original?
Mochaaa
16 Jan 2020, 00:57Okay, I'm going to start from the beginning here and try to find where the issue is.
I have three objects in a room that I want to be randomly cloned. These three objects have the name VibrantBlueRaincoat, CanaryYellowRaincoat, and ScuffedRedSneakers. The aliases are the same, but with spaces in between. Each of these originals have their .price set to 5, which was decided by me adjusting the value in the inventory tab. The shop system I'm using is the standard one described in the quest documentation, with a stock room devoid of anything but the stuff that I'm immediately selling.
I used CloneObjectAndMove (PickOneChild (CompleteStockList), Stock1)
to clone one original object with the .price attribute of 5. When I go ingame and trigger the script, the cloned object appears in the correct place, but no price is shown in the UI and the item cannot be bought.
See here: https://imgur.com/LpO7t5B and https://imgur.com/NjzGQvQ for debugger looking at a cloned object.
mrangel
16 Jan 2020, 02:01Did you run the SetUpMerchandise
function on the cloned objects?
I'm not sure if this should be necessary for clones or not; but it's the first thing that jumps out at me.
It's hard to spot a problem when I can't see your code, but that's the first thing I'd try to check.
Mochaaa
16 Jan 2020, 05:22The only modification I made to the Shop functions was in BuyObject
and I changed it so that it wouldn't immediately clone and replace with exactly the same copy. Also, how do I run SetUpMerchandise
on an object that doesn't exist yet?
mrangel
16 Jan 2020, 10:26That makes sense, then.
The SetUpShop
function calls SetUpMerchandise
on all the objects in the stock room. This modifies the object's listalias so that it shows the price, and adds the "buy" verb.
The player buys the object, which removes the price from the alias, and removes the buy/steal scripts so that the player can drop and pick it up again. Because you've disabled cloning, it makes these changes to the original object.
Then you clone the object. Cloning copies the object's current values, so as far is the shop is concerned the object has already been sold.
If you want to make the object sellable again, you need to run SetUpMerchandise
on the clone.
For example, change CloneObjectAndMove (PickOneChild (CompleteStockList), Stock1)
to SetUpMerchandise (CloneObjectAndMove (PickOneChild (CompleteStockList), Stock1))
hegemonkhan
17 Jan 2020, 14:58mrangel has already covered/helped with all of this, but just here's a hopefully simple explanation of cloning:
let's say we create an Object:
<object name="ball">
<inherit name="editor_object" />
<attr name="alias" type="string">ball</attr>
<attr name="shape" type="string">sphere</attr>
<attr name="color" type="string">red</attr>
<attr name="radius" type="int">2</attr>
<attr name="deflated" type="boolean">false</attr>
<attr name="kick" type="script">
msg ("you kick the ball")
</attr>
<displayverbs type="stringlist">
<value>kick</value>
</displayverbs>
<inventoryverbs type="stringlist">
<value>kick</value>
</inventoryverbs>
</object>
<verb>
<property>kick</property>
<pattern>kick</pattern>
<defaultexpression>You can't kick that!</defaultexpression>
</verb>
then, when you clone it, another Object is created, with everything (all attributes) the same, except for its 'name' String Attribute, as this has to be unique (it adds an increasing number to the end of the name, each time you clone), due to the 'name' String Attribute, being its ID for the quest engine:
// original 'ball' Object:
<object name="ball">
<inherit name="editor_object" />
<attr name="alias" type="string">ball</attr>
<attr name="shape" type="string">sphere</attr>
<attr name="color" type="string">red</attr>
<attr name="radius" type="int">2</attr>
<attr name="deflated" type="boolean">false</attr>
<attr name="kick" type="script">
msg ("you kick the ball")
</attr>
<displayverbs type="stringlist">
<value>kick</value>
</displayverbs>
<inventoryverbs type="stringlist">
<value>kick</value>
</inventoryverbs>
</object>
-----------------------------------------------------------
// clones (3 of them in this example) of the 'ball' Object:
<object name="ball1">
<inherit name="editor_object" />
<attr name="alias" type="string">ball</attr>
<attr name="shape" type="string">sphere</attr>
<attr name="color" type="string">red</attr>
<attr name="radius" type="int">2</attr>
<attr name="deflated" type="boolean">false</attr>
<attr name="kick" type="script">
msg ("you kick the ball")
</attr>
<displayverbs type="stringlist">
<value>kick</value>
</displayverbs>
<inventoryverbs type="stringlist">
<value>kick</value>
</inventoryverbs>
</object>
<object name="ball2">
<inherit name="editor_object" />
<attr name="alias" type="string">ball</attr>
<attr name="shape" type="string">sphere</attr>
<attr name="color" type="string">red</attr>
<attr name="radius" type="int">2</attr>
<attr name="deflated" type="boolean">false</attr>
<attr name="kick" type="script">
msg ("you kick the ball")
</attr>
<displayverbs type="stringlist">
<value>kick</value>
</displayverbs>
<inventoryverbs type="stringlist">
<value>kick</value>
</inventoryverbs>
</object>
<object name="ball3">
<inherit name="editor_object" />
<attr name="alias" type="string">ball</attr>
<attr name="shape" type="string">sphere</attr>
<attr name="color" type="string">red</attr>
<attr name="radius" type="int">2</attr>
<attr name="deflated" type="boolean">false</attr>
<attr name="kick" type="script">
msg ("you kick the ball")
</attr>
<displayverbs type="stringlist">
<value>kick</value>
</displayverbs>
<inventoryverbs type="stringlist">
<value>kick</value>
</inventoryverbs>
</object>
-----------------
<verb>
<property>kick</property>
<pattern>kick</pattern>
<defaultexpression>You can't kick that!</defaultexpression>
</verb>
so, if doing totally random/dynamic, the only way to access a specific clone is to store it's address/memory location within an Attribute, upon the clone's creation:
this is known as an Object (reference/pointer) Attribute, an example:
create ("katana") // scripting that creates a 'katana' Object
player.weapon = katana // storing the memory address/location of the 'katana' Object into the Object (reference/pointer) custom (my own named/created in this example) 'player.weapon' Object (reference/pointer) Attribute
so, now, whenever we use 'player.weapon', we are using the 'katana'
for an example:
create ("katana") // scripting that creates a 'katana' Object
create ("short_sword") // scripting that creates a 'short_sword' Object
katana.damage = 50 // setting/creating a custom 'damage' Integer Attribute on the 'katana' Object
short_sword.damage = 10 // setting/creating a custom 'damage' Integer Attribute on the 'short_sword' Object
player.weapon = katana
orc.life = orc.life - player.weapon.damage
// the orc's life will be reduced by the 'damage' (50) of the 'katana' Object
player.weapon = short_sword
orc.life = orc.life - player.weapon.damage
// the orc's life will be reduced by the 'damage' (10) of the 'short_sword' Object
now, for random/dynamic usage, we need to store it (a Clone of the 'ball' Object in this example) immediately upon/along with its creation, an example:
create ("ball")
create ("example_object")
example_object.clone_1 = CloneObject (ball) // 'ball1' clone object
example_object.clone_2 = CloneObject (ball) // 'ball2' clone object
example_object.clone_3 = CloneObject (ball) // 'ball3' clone object
and now, we can access the clones of the 'ball' Object, via using: 'example_object.clone_1', 'example_object.clone_2', 'example_object.clone_3'
an example:
msg ("original object: " + ball.name)
msg ("clone object 1: " + example_object.clone_1.name)
msg ("clone object 2: " + example_object.clone_2.name)
msg ("clone object 3: " + example_object.clone_3.name)
// output/display:
original object: ball
clone object 1: ball1
clone object 2: ball2
clone object 3: ball3
all Objects can be referenced/accessed via their 'names' (ID) String Attributes, their other Attributes (but if you got Objects with the same Attributes and/or Values, then you can NOT reference/access a specific Object, obviously), and/or their Object Types / Types / Inherited Attributes (but if you got Objects with the same Object Types / Types / Inherited Attributes, then you can NOT reference/access a specific Object, obviously)
(also, with Strings, there's a lot of cool/neat string manipulation stuff, such as using concatenation as one example, that you can do with them, for getting/doing dynamic matching)
( the string manipulation functions: https://docs.textadventures.co.uk/quest/functions/#string )
some examples:
(most of my examples only show checking for String Attributes, but you can check for the other types of Attributes too: Integers/ints, Doubles, Booleans, Scripts, Lists, Dictionaries, etc)
foreach (object_variable, AllObjects ()) {
if (object_variable.name = "WHATEVER") {
} else if (object_variable.alias = "WHATEVER") {
} else if (HasString (object_variable, "color")) {
} else if (GetString (object_variable, "color") = "red") {
// yes, I know that this script ('GetString') won't ever be checked (due to my setup example in the 'HasString' script before it), as I'm just giving examples of various ways of referencing/accessing an Object, so I don't care about this incorrect scripting ordering logic
} else if (DoesInherit (object_variable, "ball_type")) {
} else if (ListContains (object_variable.color_stringlist, "red")) {
} else if (Contains (object_variable, water)) {
}
}
hegemonkhan
17 Jan 2020, 15:06PS
forgot to explain this, you can change a clone's Attribute's values (but NOT if its an Inherited Attribute, unless you recreate the Attribute on the clone Object), by storing it's address/memory location within an Object (reference/pointer) Attribute
for example:
create ("example_object")
create ("katana")
katana.damage = 10
msg (katana.damage)
// output: 10
example_object.clone = CloneObject (katana)
msg (example_object.clone.damage)
// output: 10
example_object.clone.damage = 50
msg (example_object.clone.damage)
// output: 50
hegemonkhan
17 Jan 2020, 15:26PSS
the recent version of quest has changed the clone feature a bit, so that when you clone something, it's original Object's address/memory location gets stored into the 'prototype' Object (reference/pointer) Attribute, for all clones being made, which is extremely useful
// original objects:
<object name="ball">
</object>
<object name="food">
</object>
---------------
// clones
<object name="ball1">
<attr name="prototype" type="object">ball</attr>
</object>
<object name="ball2">
<attr name="prototype" type="object">ball</attr>
</object>
<object name="ball3">
<attr name="prototype" type="object">ball</attr>
</object>
<object name="food1">
<attr name="prototype" type="object">food</attr>
</object>
<object name="food2">
<attr name="prototype" type="object">food</attr>
</object>
<object name="food3">
<attr name="prototype" type="object">food</attr>
</object>
mrangel
17 Jan 2020, 15:32Edit: Ah, you beat me to it.
when you clone it, another Object is created, with everything (all attributes) the same, except for its 'name' String Attribute
I would advise against using Clone
or ShallowClone
directly unless you've got a specific reason for doing so.
For a new user, unless there's some specific reason not to, you should be using CloneObject
, CloneObjectAndMove
, or CloneObjectAndMoveHere
depending where you want the clone to be. (There's also a CloneObjectAndInitialise
, which has a few rather specialised uses)
There may be 3 attributes different from the original when you use these clone functions (which makes them a lot easier to use):
- The
name
attribute of each clone will be different, usually adding a number to the end. You shouldn't use the name in any case. - The
alias
attribute will be set to the original object'salias
if it had one, and the original object'sname
otherwise. This means that if you clone an object named "ball" which has no alias, the player will see the alias "ball" instead of the name "ball2" for clones. - The
prototype
attribute will be set to the original object'sprototype
if it has one, or the original object otherwise. This means that you have an attribute which lets you find the original object a clone was created from, even if it's a clone of a clone of a clone.
So, while "everything except for its name" may be true for objects cloned with Clone
, it's probably better to be aware of the other changes made by the CloneObject
functions.
prototype especially is very useful. It lets you use expressions like FilterByAttribute (AllObjects(), "prototype", ball)
which returns an objectlist containing all the clones of ball, or if (object.prototype = ball) {
which lets you find out quickly if an object is a ball.
mrangel
17 Jan 2020, 15:53It's worth pointing out that Clone()
and ShallowClone()
don't set prototype
by default. It only exists in the CloneObject
functions.
Also that as of Quest 5.8, cloned children are handled differently. If I remember correctly:
- If you clone an object using
Clone
, it will be created in the same location as the original. Any children it has will be created inside it.- If you clone an object with children (for example a bag of apples) using
Clone
, the children are also cloned. The cloned apple is created inside the cloned bag.
- If you clone an object with children (for example a bag of apples) using
- If you clone an object using
ShallowClone
, its children are not cloned, so the cloned bag will be empty.- As far as I know, this is new in Quest 5.8
- If you're using a stacking system, this could cause unexpected behaviour
- If you clone an object using
CloneObject
, the object will be cloned usingShallowClone
, and its alias set, and then its children are cloned and moved.- This means that the cloned apples are created inside the original bag, and then moved to the cloned bag. The apples'
changedparent
script will be run, which didn't happen in Quest 5.7 - This means that the children of a cloned object will now have their
hasbeenmoved
flag set to true even if the object has never been moved - This might cause some stacking libraries to behave unexpectedly
- This means that the cloned apples are created inside the original bag, and then moved to the cloned bag. The apples'
- If you clone an object with
CloneObjectAndMove
orCloneObjectAndMoveHere
, thechangedparent
scripts will be run for the object and its children.- I think this is different from Quest 5.7, which would only run
changedparent
for the top cloned object.
- I think this is different from Quest 5.7, which would only run
This mostly doesn't matter. But some stacking libraries, or (for example) a bag whose alias changes to "empty bag", "bag", or "overloaded bag" depending on the weight of stuff inside it, may behave differently from how they did in Quest 5.8
(I'm not sure, but I think cloning a limited container which contains another container might now have a bug. I'll take a look at the code again at some point)

onimike
25 Jan 2020, 18:50What I do with my clones is make a random chance changing the value before placing or taking a clone.

onimike
25 Jan 2020, 18:50What I do with my clones is make a random chance changing the value before placing or taking a clone.
Edit: Sorry don't know why it posted twice don't know how to delete lol
mrangel
25 Jan 2020, 21:23What I do with my clones is make a random chance changing the value before placing or taking a clone.
Did the suggestion I posted earlier solve your problem?