Regenerating Taken Objects

Enpherdaen
05 Oct 2017, 02:01

My game is scripted so objects randomly spawn throughout different rooms. Objects like sticks, stones, Aloe Vera plants, etc. I am wondering how to make it so that for objects that regrow such as different types of plants, 50 turns later (might change the amount whatever), the object(s) that the player has taken will re-spawn.

I wish to tell Quest that in the room I am pasting the function in, if the the object(s) in that room has been moved to the player, then 50 turns later the object(s) will be cloned and moved back into the room. I also need the same thing to apply to the duplicated clone(s).


Quest Forum Questers
05 Oct 2017, 02:16

Question first...

Can the player take multiples of the same object? Example, if the player is carrying sticks, and 50 turns later the sticks show up again, can the player then take more sticks?


hegemonkhan
05 Oct 2017, 05:16

there's two main ways of doing it:

  1. set/create a counter (Integer Attribute with a Value of 0 and increase it up to 50 each turn or a value of 50 and decrease it to 0) within a Turnscript (as you need it to be increased/decreased each turn) or as a 'changedparent' Script Attribute if you want 'player room movement' as your 'in game turns', for an alternative 'turn' example.

  2. have a normal game 'turn' counter (Integer Attribute) and check it if its a multiple of 50, via using the modulus operator/operation, for example: if (game.turns % 50 = 0) { / scripting to 'grow/spawn back' /}, and you'd need for it to be checked every turn. Though, you might not want this method, as you likely want the count to start when you take/deplete the item, so it's 50 turns later from that point, and not merely replenishing every 50 game turns


actually... this would be messy... as you don't want to have a Turnscript for every item.... hmm... not sure of a good design for doing this (the other users, who're programmers can probably help you better with a good design for doing it) ... hmm...

The best I can come up with is to use Booleans (or a List):

have a single global Turnscript, and have it check these Booleans, as to whether to increase the counters for each type of item, as this way, you don't have multiple Turnscripts, at least.


K.V.
05 Oct 2017, 05:48

  


K.V.
05 Oct 2017, 06:13

 


mrangel
05 Oct 2017, 10:20

I'd suggest something like KV's answer. However, I'm thinking that if the item is consumable, you could get odd errors (trying to clone an object that's been destroyed). I'd change the 'take' script to something like:

msg ("You pluck the fart blossom from the branch.")
newblossom = CloneObjectAndMove (this, game.pov)
newblossom.take = true
this.visible = false
SetTurnTimeout (6) {
  fart blossom.visible = true
  if (game.pov.parent = fart blossom.parent) {
    msg ("A new fart blossom blossoms.")
  }
}

This puts a clone in your inventory, and makes the original invisible for 6 turns (including this one). It also can be used in cases where the branch is strong enough to put items on. KV's version checks if the blossom is on the branch in order to decide whether to clone it or pick it up normally. Mine just sets newblossom.take = true, which removes this script from the clone and causes it to follow the default item-that-can-be-taken behaviour.

(I'm using this method in my woodlands; except the cloned object isn't always the same. I've got an object for a flower growing in the ground, but its take script gives you a clone of a picked_flower, with a different description because it's been picked)


Enpherdaen
05 Oct 2017, 16:57

Actually I just realized how I could do it, but I am still facing issues. If I script the "take"option so it re-spawns the object after 50 turns, then if the player already has a stick for example and drops it then takes it again, it was also re-spawn 50 turns later.


K.V.
05 Oct 2017, 17:20

   


Enpherdaen
05 Oct 2017, 17:52

K.V I'm not sure if I understand you. Anyway, what I've done is went to the "take" option in Aloe Vera(which I will do with the other regenerative objects) and set it so after 50 turns, the "Aloe Vera" object will be cloned and added to "player.parent". The issue is that "player.parent" will likely not be the same room that the player was in when they initially took the object. Is there a way to tell Quest that, or do I have to ditch this approach entirely?


K.V.
05 Oct 2017, 18:12

 


K.V.
05 Oct 2017, 18:16

 


Enpherdaen
05 Oct 2017, 18:23

K.V. But keep in mind, it's not just one "Eucalyptus Plant", as there would be a bunch of them randomly spawned all over the map. So referring to the specific room that the leaf was taken from is an issue.

Also I don't understand anything after sticks kicking your butt. Lol. You kinda have to use baby language with me, I'm not that familiar with everything yet. For what I can understand about it, I don't know why I would be doing that.


K.V.
05 Oct 2017, 18:29

 


Enpherdaen
05 Oct 2017, 18:33

The player will need numerous to build things. Same thing applies to other objects. The fact that my game is almost only clones makes it so hard to script, especially as I'm a newbie at scripting games.

Also where is the "after taking" option?


K.V.
05 Oct 2017, 18:38

 


K.V.
05 Oct 2017, 18:38

 


Enpherdaen
05 Oct 2017, 18:41

Online, although the Desktop editor works fine for me as well and I literally have it open right now.


Enpherdaen
05 Oct 2017, 18:44

Oh wait, it wasn't there because I did not enable taking yet.


K.V.
05 Oct 2017, 18:46

 


K.V.
05 Oct 2017, 18:47

 


jmnevil54
05 Oct 2017, 18:49

I would rather do.....

Stick = player.stick.....

If player.stick = 2 and player.fuel = 1...
  Remove stick/all objects...
  Add Torch to inventory

Or something.

Edit:
I found this. http://docs.textadventures.co.uk/quest/tutorial/using_timers_and_turn_scripts.html


K.V.
05 Oct 2017, 18:53

 


Enpherdaen
05 Oct 2017, 18:54

Jmnevil54 what?


jmnevil54
05 Oct 2017, 18:56

I forgot where the turn script was.

So in my game, I have have it so that after every time the player does a turn, A REAL TURN, the game prints "You did something."

Unfortunately, in my efforts to show you where it was, I have lost it myself............

I use the web editor, so I can't use.....
wait....
I have it! Watch this video! https://www.youtube.com/watch?v=vdk0KuLjnqM
(See link up there too...)


jmnevil54
05 Oct 2017, 19:02

Okay, so I've seen another crafting thread like this.....

Stick = player.stick

Fuel = player.fuel

You'll have to use some sort of foreach script to figure out the rest.


Enpherdaen
05 Oct 2017, 19:02

Jmnevil54 that's a day and night cycle which I already have.


jmnevil54
05 Oct 2017, 19:06

Okay... so what don't you have?

I found the foreach stick thread.

http://textadventures.co.uk/forum/quest/topic/_58m4h1ljuixmcvog__jcg/checking-for-object-and-amounts

It basically goes like this...

  small_stick_count = 0
  large_stick_count = 0
  foreach (o, GetAllChildObjects(player)) {
    if (StartsWith(o.name, "small_stick") and LengthOf (o.name) > LengthOf ("small_stick")) {
      small_stick_count = small_stick_count + 1
    }
    else if (StartsWith(o.name, "large_stick") and LengthOf (o.name) > LengthOf ("small_stick")) {
      large_stick_count = large_stick_count + 1
    }
  }
  if (small_stick_count > 3) {
    if (large_stick_count > 3) {
      msg ("SUCCESS!!!")
      msg ("")
      msg ("You have enough to construct this fire/shelter/trap! And proceed to do so.")
    }
    else {
      msg ("Unfortunately, you don't have enough large sticks for this project.")
    }
  }
  else {
    msg ("Not enough small sticks.")
  }

But that isn't the full script...

Ask Anonynn...


K.V.
05 Oct 2017, 19:07

 


jmnevil54
05 Oct 2017, 19:14

No, no, I wasn't talking about SetTurnTimeout... I think...
I still can't find it....


jmnevil54
05 Oct 2017, 19:15

What do you call it when you print "You did something" every turn? ....The feature, I mean?


K.V.
05 Oct 2017, 19:16

 


K.V.
05 Oct 2017, 19:18

 


Enpherdaen
05 Oct 2017, 19:21

What if I go into the object take section, and script it so when the player takes Aloe Vera, a clone is made and moved into player.parent in the same turn, AND said clone is invisible for 50 turns. Or just visible after 50 turns.


K.V.
05 Oct 2017, 19:23

 


Enpherdaen
05 Oct 2017, 19:27

"I think it would be easier to just move the stick back to where it came from upon use, rather than removing it from the game".

What do you mean about moving the stick back to where it came from upon use?

Also I'm not removing it from the game, I'm making it invisible for 50 turns.

I think the issue with every approach that I try is telling Quest which clone I am referring to, which as of now seems Inevitable.


jmnevil54
05 Oct 2017, 19:28

*Downloads game and searches

Oh. It turns out I do have a Turn Script named Turn1.

Use a turn script!

BTW This guy makes tutorials. He has a crafting/survival tutorial. https://www.youtube.com/channel/UCGSCL0CED0oIG8J1mKyDoMw/videos


Enpherdaen
05 Oct 2017, 19:35

Would I be able to type out: player.parent.Aloe Vera, referring to the Aloe Vera that is in the room that parents the player?


K.V.
05 Oct 2017, 20:19

 


K.V.
05 Oct 2017, 20:22

 


Enpherdaen
05 Oct 2017, 20:26

My Gosh my brain hates me for thinking about all this so hard.


Enpherdaen
05 Oct 2017, 20:31

I don't understand all of the x value = this and that. It'd be impossible to use that in order to achieve the original question.

:(


jmnevil54
05 Oct 2017, 20:37

X= is the same as any attribute.

If it makes you feel better, just do game.x!


jmnevil54
05 Oct 2017, 20:38

isOG = false

Someone explain this.


Enpherdaen
05 Oct 2017, 20:48

I just gotta know how to make the plants the player has taken regenerate after x amount of turns. I seriously need a step by step explanation at this point.


K.V.
05 Oct 2017, 20:48

 


Enpherdaen
05 Oct 2017, 20:52

Yay


K.V.
05 Oct 2017, 20:57

 


Enpherdaen
05 Oct 2017, 21:03

But you can't do the same thing with multiple clones in different rooms.


jmnevil54
05 Oct 2017, 21:17

Why do you need clones again?


K.V.
05 Oct 2017, 21:18

 


K.V.
05 Oct 2017, 21:31

 


Enpherdaen
05 Oct 2017, 21:38

Yes K.V. those 3 steps are exactly what I would like. So many different examples were just kinda confusing me.


Enpherdaen
05 Oct 2017, 21:48

If it's possible, it'd be much simpler to me not involving chasing after individual clones. As you showed in the "for Enpherdaen" example. But do you know how to do it without?


K.V.
05 Oct 2017, 21:50

 


K.V.
05 Oct 2017, 22:04

 


hegemonkhan
05 Oct 2017, 22:06

when you create a clone, you can set/create a String Attribute that has the original Object's name as it's stored/set Value, and you can store the clones into an Objectlist, which you can use to get a specific clone or all clones

(various examples, the design is poor/lame/stupid/meaningless... just to show the different examples of what can be done)

@ KV:

you should be able to make sense of these various methods, which you can use/adapt for helping enderph (sorry on the spelling) for what he wants done for his game

<game name="example_game">
  <attr name="start" type="script">
    data_object.clone_objectlist_attribute = NewObjectList ()
  </attr>
</game>

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

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

<object name="data_object">
  <attr name="specific_clone_string_attribute" type="string">unknown</attr>
</object>

<object name="original_object">
  <attr name="parent" type="object">room</attr>
  <attr name="example_clone_script_attribute" type="script">
    clone_object_variable = CloneObject (this)
    clone_object_variable.name_of_original_object_string_attribute = this.name
    clone_object_variable.parent = room2
    msg (cloned_object_variable.name)
    msg (cloned_object_variable.name_of_original_object_string_attribute)
    if (cloned_object_variable.name = "original_object100") {
      data_object.specific_clone_string_attribute = cloned_object_variable.name
    }
    list add (data_object.clone_objectlist_attribute, cloned_object_variable)
    foreach (obj, data_object.clone_objectlist_attribute) {
      msg (obj.name)
      msg (obj.name_of_original_object_string_attribute)
      if (obj.name = data_object.specific_clone_string_attribute) {
        msg ("this is clone 100 (original_object100)")
      }
    }
  </attr>
</object>

Enpherdaen
05 Oct 2017, 22:20
  1. I'm thinking of making 100 or less total rooms. Each room has a 50% chance of spawning one in.
  2. I do not understand what "in play means". Does not matter to me though how many sticks are in the game at one time.
  3. For now, yes.
  4. Answered in question 1.
  5. When it comes to sticks, it doesn't matter because sticks are so common anyway. However with rarer objects like Aloa Vera, the object will stay where it was dropped and have no effect on the room it was dropped in.
  6. No.
  7. The sticks need to be on the player. But if it's easier the other way, then I don't really mind that much.
  8. I haven't gotten to the crafting part of the game yet. I intend it to be if you you want to build a camp fire (15 sticks, 10 stones), your inventory must at least look like this: Sticks: 15 Stones: 10. Unless they don't need to be in your inventory, in which case same thing applies to the room. Also yes, I would like to stack them.

jmnevil54
05 Oct 2017, 22:23
I haven't gotten to the crafting part of the game yet. I intend it to be if you you want to build a camp fire (15 sticks, 10 stones), your inventory must at least look like this: Sticks: 15 Stones: 10. Unless they don't need to be in your inventory, in which case same thing applies to the room. Also yes, I would like to stack them.

Stacking doesn't work too well...


Enpherdaen
05 Oct 2017, 22:26

Jmnevil54 we'll see about that!


K.V.
05 Oct 2017, 22:27

 


Enpherdaen
05 Oct 2017, 22:32

K.V. what?


mrangel
05 Oct 2017, 22:39

I think that the easiest way to do it if you want to have multiple plants, and have all of them respawn an item after some number of turns, then the easiest way is going to be having a factory object. This could be a bush that regrows leaves, or it could be an invisible object.

For an example, I'll give you a bush that regrows flowers.

The bush's initialisation script [I'm using the web version. If you're on desktop, you could probably do this more easily in the attributes tab]:

this.regrows_after_turns = 15
this.hasflower = true
this.regrow => {
  this.hasflower = true
  this.look = "It's a bush, with a red flower growing on it"
}

And the bush's "take" script:

if (this.hasflower) {
  msg ("You pick a flower from the bush")
  this.look = "It's a bush, but you picked the flower now"
  this.hasflower = false
  this.turns_since_picked = 0
  AddToInventory (CloneObject (red flower))
}
else {
  msg ("There's no flower to pick. You might need to wait for another one to grow")
}

I'm assuming there is an object named "red flower"; the original is probably somewhere the player can't access, but it doesn't matter as long as it doesn't get destroyed.

And then to make this work, you have a turnscript. You can put this in a turnscript on its own, or include it within your existing time system, to be run every time a real turn has passed.

foreach (object, AllObjects()) {
  if (HasInt(object, "turns_since_picked")) {
    object.turns_since_picked = object.turns_since_picked + 1
    if (object.turns_since_picked = object.regrows_after_turns) {
      do (object, "regrow")
    }
  }
}

Then you have it; a bush that regrows after 15 turns. You can clone the bush in different rooms, and each one will keep its own timer to decide when to grow a new flower. You can use the same script on different bushes, and just change the type of flower grown.

Then if you want some object (a stone on the ground, maybe?) that reappears without growing on a bush, you could add it like this. Its initialisation script:

this.regrows_after_turns = 22
this.regrow => {
  if (HasObject(this, "original_location")) {
    newstone = CloneObjectAndMove (this, this.original_location)
    newstone.turns_since_picked = null
  }
}

And its 'take' script:

AddToInventory(this)
msg ("You pick up the stone.")

// If it doesn't already have a turns_since_picked, this is the first time so we start counting
// ... and remember its original_location so we know where to put the clone.
if (not HasInt(this, "turns_since_picked")) {
  this.turns_since_picked = 0
  this.original_location = this.parent
}

Hope that's clear enough. From your posts, I think that should do everything you want. You just need one turnscript to keep it ticking over; and you can have as many bushes or stones as you need, or different types of items using the same script.


mrangel
05 Oct 2017, 22:42

Sorry, lots of posts while I was typing that. Just ignore me.


K.V.
05 Oct 2017, 22:50

 


K.V.
05 Oct 2017, 22:54

 


Enpherdaen
05 Oct 2017, 23:02

K.V yes I have already thought of making multiple items in the player's inventory one item with it's own value to change.

But how does that solve the issue of respawning plants?


jmnevil54
05 Oct 2017, 23:10

MAKE. PLANTS. RESPAWN.
DO. YOU. WANT. OBJECTS. OR. ATTRIBUTES.


Enpherdaen
05 Oct 2017, 23:18

Jmnevil54 attributes.


jmnevil54
05 Oct 2017, 23:21

...
Create a turn script. It's different than a timer. I put a link on it already.
(Sorry, I had to edit this.)
Paste this.

game.turn = game.turn + 50

This would either be in another turncript, or the same one.

if (room.plant < 0) {
  if (game.turn >= 50) {
    room.plant = room.plant + 1
  }
}

Tell me, if it's an attribute, how does a player pick it up?

Also, if aloe vera is rare, why are the sticks objects, and aloe vera an attribute?


jmnevil54
05 Oct 2017, 23:31

I edited it.


mrangel
05 Oct 2017, 23:36

(I realise that in my example, the stone is actually simpler than the bush. So you could use the stone code for a flower or whatever too, and just put it on a bush. But I left the bush code in, because it might be useful if you want to have a bush that sometimes grows a flower and sometimes grows a twig; or a tree that grows a flower and then turns into a fruit if you leave it 20 turns without picking it. Or a tree that stops creating fruit if you chop it down for timber)

What was the issue with the sticks? I see a lot of talking about sticks, but I haven't been able to figure out what the problem was.


Enpherdaen
05 Oct 2017, 23:53

As far as I know there was no issue with sticks, K.V. just really likes sticks.

Jmnevil54 I didn't really know what you were talking about so I just went along with it. All objects are objects.


K.V.
06 Oct 2017, 00:59

 


Enpherdaen
06 Oct 2017, 01:10

This issue doesn't apply to sticks. Sticks don't regrow lol.

So K.V. what do you recommend I do to fix the issue?


K.V.
06 Oct 2017, 01:12

 


Enpherdaen
06 Oct 2017, 01:26

Does his just make it so every 15 turns an item automatically respawns?


K.V.
06 Oct 2017, 01:50

 


Enpherdaen
06 Oct 2017, 01:52

I wasn't worried about the amount of turns. More so, the timer only starting when you take it.


Enpherdaen
06 Oct 2017, 02:05

I know what line to change K.V.

What in the blazes is an "initialisation script"?


K.V.
06 Oct 2017, 02:09

 


K.V.
06 Oct 2017, 02:14

 


Enpherdaen
06 Oct 2017, 02:15

The timer should start when you take it every time.

What's an initialisation script and where do I find it?

EDIT: Oh okay I see now. Can't believe I missed that.


Enpherdaen
06 Oct 2017, 02:21

Well I'm grateful for finally getting it working. Now I'm wondering how the hell it works lol


hegemonkhan
06 Oct 2017, 02:51

It seems to be like the 'start' Script Attribute of the 'game' Game Settings Object (do you have this in the online version?), except it's for that specific Object.

if this is correct, than all it does: is to do what you put (scripts you add to it) in it, immediately at game start (once/one-time), for that specific Object.

(since its for that specific Object: basically it acts as a way to set up whatever you want for that Object to, have: attributes, or do: scripts)


Enpherdaen
06 Oct 2017, 02:55

I don't understand.

But then again I don't understand much.


K.V.
06 Oct 2017, 03:12
Information regarding initialisation scripts

To set an initialisation script for the game:

You can go to the Features tab on the game object, and check the option to show advanced options for the game object:

image


Then, go to the new tab that appears: Advanced Scripts:

image


This will load at start (before the start script) AND every time a saved game is loaded. (You need this for CSS settings to behave correctly, among other things like extra inventory objects.)

It will NOT understand this in a script! This is the script for the entire game.


To set an initialisation script on an object, which is what mrangel is doing (this script WILL understand this)

image

image


The order in which the scripts load:

  1. User interface initialisation script (if existing) [this is set on the game object]
  2. Start script [this is set on the game object]
  3. Object initialisation scripts (if existing)

EDIT:

REMEMBER: The User interface initialisation script loads on start AND each time a saved game is loaded, but the object initialisation scripts only load on start (not when loading saved games).



Resource:

https://github.com/ThePix/quest/wiki/UI-Part-05:-Where-and-When-To-Do-Stuff


Enpherdaen
06 Oct 2017, 03:15

K.V. I said I already found it.


mrangel
06 Oct 2017, 09:01

If you're using the desktop editor, I think you can do it using the 'Attributes' tab.

People like me using the web editor don't have that. So I got into the habit of using the "Initialisation script" to set any attributes that I want to set at the start.
One advantage of doing it this way is that if you ever want to reset an object to its initial state, you can can re-run the initialisation script with do(object, "_initialise_") … though there are probably quite few situations where this is useful.

I should point out that if you're using my script and cloning the object into different rooms, it will work fine as long as you clone it before it's been picked up.
This is why I find it useful to have a room the player can't access which contains all the "original" objects, so the player only deals with clones.


mrangel
06 Oct 2017, 09:14

KV:

REMEMBER: The initialisation scripts load on start AND each time a saved game is loaded.

I'm pretty sure this is only true for the UI Initialisation script; object initialisation scripts only get run once. (In some languages they would be run again on a cloned object, but this doesn't seem to be the case in Quest).

I've spent a lot of time recently messing about with bugs that turn out to be the result of initialisation scripts for different objects not running in the order I expected (it seems that the script for a container will usually – but not always – run before the scripts for its contents).


The Pixie
06 Oct 2017, 09:56

I'm pretty sure this is only true for the UI Initialisation script; object initialisation scripts only get run once.

Correct.

(In some languages they would be run again on a cloned object, but this doesn't seem to be the case in Quest).

Perhaps a CloneObjectAndInitialise function would be useful. I would be reluctant to change the current behaviour in case objects are cloned after they have changed somehow.

I've spent a lot of time recently messing about with bugs that turn out to be the result of initialisation scripts for different objects not running in the order I expected (it seems that the script for a container will usually – but not always – run before the scripts for its contents).

They run in the order the objects appear in the left pane.


K.V.
06 Oct 2017, 13:00

object initialisation scripts only get run once

Whoops! I edited that bit in that post. Thanks!


I also erased the rest of my posts in this thread to avoid confusion. (I misunderstood what the OP was wanting, and I had the thread ALL jacked up.)


Have a nice day!


Enpherdaen
06 Oct 2017, 13:07

Mrangel I have been using hidden objects in invisible rooms to clone as you said.


Enpherdaen
06 Oct 2017, 20:26

It appears Mrangel's script does not work, or at least doesn't do what I needed it to do. I can't 100% understand the scripting, which means I can't 100% tell what is going on.

Mrangel could you please explain how the last 2 scripts in your post (those were the ones I used) work step by step?


mrangel
06 Oct 2017, 21:06

Did you include the turnscript?
The first two scripts in that post were for a bush that grows flowers. The last two for an object that respawns itself. And the one in the middle is the Turnscript that powers both of them. You should be able to put that in a turnscript on its own, or you can incorporate it into your existing timing system if necessary.

The turnscript increases the "turns_since_picked" attribute of every object that has one; and if any of them have reached the same object's "regrows_after_turns" attribute, the objects "regrow" script is run.

... yes, I made a dumb mistake.
The take script should be:

if (not HasInt(this, "turns_since_picked")) {
  this.turns_since_picked = 0
  this.original_location = this.parent
}
AddToInventory(this)
msg ("You pick up the stone.")

This checks if the object already has a "turns_since_picked" attribute (in which case it's already been picked up). If not, it sets turns_since_picked to zero, starting the counter, and sets an "original_location" attribute so that it knows where to put the clone.
After it's done this, it lets the player actually pick up the stone. (by putting AddToInventory() before the other part, I had it setting "original_location" to the inventory, which isn't what you want.

The initialisation script just sets up a "regrow" script; which clones the object and puts the clone in the place indicated by its "original_location" attribute. The clone's "turns_since_picked" is set to null so that the game doesn't think it's already been picked up.

(sorry, I'm not so good at explaining things)


Enpherdaen
06 Oct 2017, 23:00

How did you get Quest to know which clone you were talking about?

You see when I try picking up Aloe Vera (which I applied the code to), it tells me I pick it up, but it is still there. Referring to "this" is referring to the actual object in a hidden room rather than the clone in the room with the player.


mrangel
07 Oct 2017, 08:51

this is a special variable that nearly always refers to the clone you want. If a script is run because of the player typing the name of an object, this should be the object they typed.

Can you show me what you've got so far, and I'll see if I can see where the problem's coming in?


Enpherdaen
07 Oct 2017, 15:24

Take script for Aloe Vera:

if (not HasInt(this, "turns_since_picked")) {
  this.turns_since_picked = 0
  this.original_location = this.parent
}
AddToInventory (this)
msg ("You pick up some Aloe Vera.")

Initialisation Script for Aloe Vera:

this.regrows_after_turns = 50
this.regrow => {
  if (HasObject(this, "original_location")) {
    newstone = CloneObjectAndMove (this, this.original_location)
    newstone.turns_since_picked = null
  }
}

How does it know what clone "this" is though? Does "this" tell Quest that you are referring to the clone in reach of the player?


mrangel
07 Oct 2017, 16:19

In the case of the 'take' script, this is the one in the player's room. (If there's more than one, the player will get a menu asking them to pick one, and this will be the one they chose).

In the case of the 'regrow' script, this is the one that was passed to the command do (object, "regrow") in the turnscript; so the one that's just had its counter increased.

(In the turnscript, this would be the turnscript itself; which you probably have no use for. That's why the turnscript looks through AllObjects(), searching for the object we want)

I've looked over those scripts another couple of times, walking through the code in my head, and I can't see where it's failing. I feel like I must be missing something obvious.
Is the turnscript just running as a normal turnscript, or is there other code around it that could be affecting it?

I assume you haven't actually got objects named "object" or "newstone" ... I have no idea how Quest would deal with it if you did. (If you do, then change all instances of "object" in the turnscript or "newstone" in the initialisation script to a name that isn't shared by anything else)


Enpherdaen
07 Oct 2017, 22:15

Ugh... I don't really get what you want me to do. I only need the take script and initialisation script, right? I don't have any turn scripts with it.

Also how does typing "this" tell Quest which clone you are referring to?


The Pixie
08 Oct 2017, 08:30

"this" indicates the object the script is attached to. For your clone, it will get a copy of the script on the prototype. When you run the script, "this" will refer to the clone.


mrangel
08 Oct 2017, 09:22

I only need the take script and initialisation script, right? I don't have any turn scripts with it.

You need the turnscript to make it work. Without that it will just let you pick up the object normally, and not clone it.

This turnscript, from the post up ↑ there:

foreach (object, AllObjects()) {
  if (HasInt(object, "turns_since_picked")) {
    object.turns_since_picked = object.turns_since_picked + 1
    if (object.turns_since_picked = object.regrows_after_turns) {
      do (object, "regrow")
    }
  }
}

The turnscript adds 1 to "turns_since_picked" for all objects that are counting it, and calls the 'regrow' script if any of them have reached the target number.


Enpherdaen
08 Oct 2017, 13:43

The Pixie oh I see now. I wish I knew of this "this" earlier (pun not intended).

Mrangel that'd explain why it wasn't working. Lemme try it out now.


Enpherdaen
10 Oct 2017, 18:48

Mrangel it still does not work. I'll paste out all of the script that has to do with it so you can find out what is wrong.

Intialisation script:

this.regrows_after_turns = 3
this.regrow => {
  if (HasObject(this, "original_location")) {
    newstone = CloneObjectAndMove (this, this.original_location)
    newstone.turns_since_picked = null
  }
}

Take script:

if (not HasInt(this, "turns_since_picked")) {
  this.turns_since_picked = 0
  this.original_location = this.parent
}
AddToInventory (this)
msg ("You pick up some Aloe Vera.")

Turn script: (which is it's own, separate turn script)

object.turns_since_picked = object.turns_since_picked + 1
if (object.turns_since_picked = object.regrows_after_turns) {
  do (object, "regrow")
}

K.V.
10 Oct 2017, 18:57

Quest doesn't recognize object in your turn script because it is missing the line that sets the variable:

foreach (object, AllObjects()) {


This is mrangel's turn script:

foreach (object, AllObjects()) {
  if (HasInt(object, "turns_since_picked")) {
    object.turns_since_picked = object.turns_since_picked + 1
    if (object.turns_since_picked = object.regrows_after_turns) {
      do (object, "regrow")
    }
  }
}

http://textadventures.co.uk/forum/quest/topic/ejfkn0tkdewpb9wphl5tqg/regenerating-taken-objects#8451555b-7a62-4c8f-af56-d4c51ef427d5


mrangel
10 Oct 2017, 19:01

Did you include the rest of the turn script? looks like you're missing a couple of lines.


Enpherdaen
10 Oct 2017, 20:43

What rest of the turn script?


K.V.
10 Oct 2017, 22:18

What rest of the turn script?

Two posts up.

Five posts up.


mrangel
10 Oct 2017, 22:37

An extra thought at this point is that if you've got the "original" items somewhere the player can't touch them, then your object initialisation script could be slightly rearranged (both versions should work):

this.regrows_after_turns = 3
this.original_object = this
this.regrow => {
  if (HasObject(this, "original_location")) {
    CloneObjectAndMove (this.original_object, this.original_location)
  }
}

(If you clone the original item rather than the one the player is carrying, you don't need the extra line to reset it to its "not been picked yet" state. Arranging it this way will make things like a crafting system easier to code in future)


Enpherdaen
10 Oct 2017, 22:55

I pasted that code where the Initialisation script is and it still does not work.


mrangel
10 Oct 2017, 23:10

Sorry, I should leave improvements until you've got it working.

KV's probably going to be more help than me at this point, I've got a lot of other stuff to deal with that's reducing my concentration. Hope it doesn't take too long to get it working.


mrangel
10 Oct 2017, 23:16

Looking at your code posted in the other thread, you have the turnscript right. There were just a couple of lines missing when you pasted it in this thread.

But it looks to me like the turnscript isn't enabled; maybe you should check that.


K.V.
10 Oct 2017, 23:25

I'm sorry. I was only looking at the code you posted last. You do have mrangel's entire script in the game.

As mrangel says, you only need to enable his turn script.

image


Enpherdaen
11 Oct 2017, 00:26

OH! Another small, silly mistake annoying the hell out of me for hours. Such a simple fix.


Enpherdaen
11 Oct 2017, 00:29

Now I'm getting errors. At first it says it doesn't understand what "object" is so I change all "object"s in the turn script to Aloe Vera, but then I get another error: Error running script: Error compiling expression 'Aloe Vera.turns_since_picked + 1': ArithmeticElement: Operation 'Add' is not defined for types 'Object' and 'Int32'.


K.V.
11 Oct 2017, 01:03

Have you actually taken the Aloe Vera at this point? (I'm thinking Aloe Vera.turns_since_picked doesn't exist when Quest tries to see what its value is in order to add 1 to it.)


K.V.
11 Oct 2017, 01:11

This is the turn script I have in the version of your game that I have (I'm pretty sure it is mrangel's unaltered script):

foreach (object, AllObjects()) {
  if (HasInt(object, "turns_since_picked")) {
    object.turns_since_picked = object.turns_since_picked + 1
    if (object.turns_since_picked = object.regrows_after_turns) {
      do (object, "regrow")
    }
  }
}

The turn script is enabled.


I can't get it to recreate that error.

> l
Parachute Landing
You can see: Trees and Aloe Vera.
You can go east.

> get aloe
You pick up some Aloe Vera.

> l
Parachute Landing
You can see: Trees.
You can go east.

> l
Parachute Landing
You can see: Trees.
You can go east.

> l
Parachute Landing
You can see: Trees and Aloe Vera.
You can go east.


(NOTE: I picked up the stone. That's why you don't see it listed.)


K.V.
11 Oct 2017, 01:15

Fret not, Enpherdaen.

Those small, silly mistakes happen to the best of them!


Can you flip to code view, copy the entire section of your turn script, and paste it here?

Here's the one in the version of the game I currently have:

  <turnscript name="Regenerative">
    <script>
      foreach (object, AllObjects()) {
        if (HasInt(object, "turns_since_picked")) {
          object.turns_since_picked = object.turns_since_picked + 1
          if (object.turns_since_picked = object.regrows_after_turns) {
            do (object, "regrow")
          }
        }
      }
    </script>
    <enabled />
  </turnscript>

mrangel
11 Oct 2017, 09:04

Now I'm getting errors. At first it says it doesn't understand what "object" is

'object' is defined by the line foreach (object, AllObjects()) {, which tells it to repeat the code once for each object in the game. It should be fine.

so I change all "object"s in the turn script to Aloe Vera,

I wouldn't expect that to work, but I just tested it and it does. It's confusing though, because in that script you'd be using the name "Aloe Vera" to refer to the collection of all objects in the game.

but then I get another error: Error running script: Error compiling expression 'Aloe Vera.turns_since_picked + 1': ArithmeticElement: Operation 'Add' is not defined for types 'Object' and 'Int32'.

That's what happens if you try to add one to an attribute that doesn't exist. This shouldn't be possible, because the previous line if (HasInt(object, "turns_since_picked")) { should make sure the code is only run for objects that have that attribute. From the error, I'd guess that when you changed the name of "object" you missed the one in the foreach or if statement.


Enpherdaen
11 Oct 2017, 14:52

K.V. what's weird about each turn you took is that I see no stones, sticks and other things that would be there.

I did paste the entire turn script code!

Mrangel why is the not-identifying "object" popping up then?


K.V.
11 Oct 2017, 15:03

You set it up to randomly generate objects.


On one play, I get this:

> d

Parachute Landing
You can see: Aloe Vera, Trees, Stones and a Sap Tree.
You can go east.
You un-clip yourself from the parachute and land on the thick shrubs below. Smoke is visible in the distance above the trees.


On the next, I get this:

> d

Parachute Landing
You can see: Aloe Vera and Trees.
You can go east.
You un-clip yourself from the parachute and land on the thick shrubs below. Smoke is visible in the distance above the trees.


One more, just for fun:

> d

Parachute Landing
You can see: Aloe Vera, Trees and Stones.
You can go east.
You un-clip yourself from the parachute and land on the thick shrubs below. Smoke is visible in the distance above the trees.


mrangel
11 Oct 2017, 15:04

I don't know. Can you show the full error message for not recognising "object"? Does it include a little bit of code to let you know which line it's finding a problem with?


Enpherdaen
11 Oct 2017, 15:20

K.V. It's just odd that the amount of things being spawned is so few. For me in almost every room there are more than 1 sticks and more than 1 stones.

Mrangel here: Error running script: Error compiling expression 'object.turns_since_picked + 1': Unknown object or variable 'object'


K.V.
11 Oct 2017, 15:34

It's just odd that the amount of things being spawned is so few. For me in almost every room there are more than 1 sticks and more than 1 stones.

Sounds like I don't need to play poker with you! You lucky dog!


If you have, let's say four sticks, that means you said, "I'm flipping this coin four times in a row, and it will be HEADS every time!" Then, it was heads every time.


mrangel
11 Oct 2017, 15:50

Error running script: Error compiling expression 'object.turns_since_picked + 1': Unknown object or variable 'object'

That's strange. It uses "object" successfully on two consecutive lines, but has an error on the third.
The only thing I can think of that could cause that is if "object" is spelled wrong in the first 2 lines of the turnscript;

    foreach (object, AllObjects()) {
        if (HasInt(object, "turns_since_picked")) {

I think they were correct previously, but I've seen errors suddenly appear if I've leaned on the keyboard by accident and inserted an extra letter without noticing. So it could be worth checking.

If that doesn't help, you could paste the whole code like you did in the other thread, so we can look over it for anything we haven't thought about yet.


Enpherdaen
11 Oct 2017, 16:06
<!--Saved by Quest 5.7.6404.16853-->
<asl version="550">
  <include ref="English.aslx" />
  <include ref="Core.aslx" />
  <game name="A Break From Bloodshed">
    <inherit name="theme_novella" />
    <gameid>4996880d-97f4-46da-b9f4-6e0d3539c85f</gameid>
    <version>1.0</version>
    <firstpublished>2017</firstpublished>
    <author>Brandon Michon-Cave</author>
    <category>Simulation</category>
    <description>An allied fighter pilot must survive on a South Pacific island after being shot down by a Japanese zero fighter during WWII.</description>
    <defaultfont>'Times New Roman', Times, serif</defaultfont>
    <menufont>Georgia, serif</menufont>
    <attr name="autodescription_youarein_useprefix" type="boolean">false</attr>
    <showborder type="boolean">false</showborder>
    <menuhoverbackground>LightSlateGray</menuhoverbackground>
    <setcustompadding />
    <autodisplayverbs type="boolean">false</autodisplayverbs>
    <autodescription />
    <echohyperlinks type="boolean">false</echohyperlinks>
    <showlocation type="boolean">false</showlocation>
    <appendobjectdescription />
    <enablehyperlinks type="boolean">false</enablehyperlinks>
    <attr name="autodescription_youcansee" type="int">3</attr>
    <attr name="autodescription_youcango" type="int">4</attr>
    <attr name="autodescription_description" type="int">2</attr>
    <showscore />
    <showhealth />
    <feature_limitinventory />
    <feature_lightdark />
    <feature_advancedscripts />
    <start type="script">
      set (Time, "minutes", 540)
      set (Cold, "value", 0)
      set (Hunger, "value", 0)
      set (Thirst, "value", 0)
      set (Sleep, "value", 0)
    </start>
    <unresolvedcommandhandler type="script">
    </unresolvedcommandhandler>
    <onhealthzero type="script">
      finish
      msg ("You have died.")
    </onhealthzero>
  </game>
  <verb>
    <property>exit</property>
    <pattern>exit</pattern>
    <defaultexpression>"You can't exit " + object.article + "."</defaultexpression>
  </verb>
  <verb>
    <property>follow</property>
    <pattern>follow</pattern>
    <defaultexpression>"You can't follow " + object.article + "."</defaultexpression>
  </verb>
  <verb>
    <property>enter</property>
    <pattern>enter</pattern>
    <defaultexpression>"You can't enter " + object.article + "."</defaultexpression>
  </verb>
  <object name="Time">
    <inherit name="editor_object" />
    <visible type="boolean">false</visible>
    <attr name="feature_startscript" type="boolean">false</attr>
    <usedefaultprefix type="boolean">false</usedefaultprefix>
    <drop type="boolean">false</drop>
  </object>
  <turnscript name="Time Movement">
    <enabled />
    <script>
      Time.minutes = Time.minutes + 12
      if (Time.minutes = 1440) {
        Time.minutes = 0
      }
    </script>
  </turnscript>
  <turnscript name="Hazard Effects">
    <enabled />
    <script>
      if (Cold.value = 100) {
        SetTurnTimeout (1) {
          Hunger.value = Hunger.value - 5
        }
      }
      if (Hunger.value = 100) {
        SetTurnTimeout (1) {
          DecreaseHealth (5)
        }
      }
      if (Thirst.value = 100) {
        SetTurnTimeout (1) {
          DecreaseHealth (5)
        }
      }
      if (Sleep.value = 100) {
        Time.minutes = Time.minutes + 240
        Sleep.value = Sleep.value - 50
        msg ("You fell asleep from exhaustion. You wake up some hours later still feeling tired.")
      }
    </script>
  </turnscript>
  <object name="ROOMS">
    <inherit name="editor_room" />
    <usedefaultprefix type="boolean">false</usedefaultprefix>
    <object name="Mountain Base">
      <inherit name="editor_room" />
      <alias>Mountain Base</alias>
      <usedefaultprefix type="boolean">false</usedefaultprefix>
      <objectslistprefix>You can see:</objectslistprefix>
      <firstenter type="script">
        msg ("You stop at the base of a mountain. Way upon it was where the smoke came from.")
      </firstenter>
      <beforefirstenter type="script">
        RandomObjectChance
      </beforefirstenter>
      <exit alias="west" to="Jungle">
        <inherit name="westdirection" />
      </exit>
    </object>
    <object name="Parachute Landing">
      <inherit name="editor_room" />
      <usedefaultprefix type="boolean">false</usedefaultprefix>
      <alias>Parachute Landing</alias>
      <objectslistprefix>You can see:</objectslistprefix>
      <firstenter type="script">
        msg ("You un-clip yourself from the parachute and land on the thick shrubs below. Smoke is visible in the distance above the trees.")
      </firstenter>
      <beforefirstenter type="script">
        RandomObjectChance
      </beforefirstenter>
      <object name="Caught Parachute">
        <alias>Caught Parachute</alias>
        <usedefaultprefix type="boolean">false</usedefaultprefix>
        <scenery />
        <description>You hear the sounds of lush forest life making a variety of noises. You open your eyes to see you have survived being shot down by a Japanese zero fighter. Your parachute hangs a few feet off the ground and sways from your movement.</description>
        <firstenter type="script">
        </firstenter>
        <exit alias="down" to="Parachute Landing">
          <inherit name="downdirection" />
          <visible />
          <scenery type="boolean">false</scenery>
        </exit>
        <object name="player">
          <inherit name="editor_object" />
          <inherit name="editor_player" />
        </object>
      </object>
      <object name="Smoke">
        <inherit name="editor_object" />
        <alias>Smoke</alias>
        <scenery />
        <drop type="boolean">false</drop>
        <follow type="script">
          MoveObject (player, Jungle)
        </follow>
      </object>
      <exit alias="east" to="Jungle">
        <inherit name="eastdirection" />
      </exit>
    </object>
    <object name="Crash Sight">
      <inherit name="editor_room" />
      <usedefaultprefix type="boolean">false</usedefaultprefix>
      <alias>Crash Sight</alias>
      <description type="string"></description>
      <objectslistprefix>You can see:</objectslistprefix>
      <firstenter type="script">
        msg ("The trees behind the plane are broken and clipped. One of the wings has been torn off and sits beside the plane, a few feet back.")
      </firstenter>
      <object name="P15D Mustang">
        <description type="string"></description>
        <usedefaultprefix type="boolean">false</usedefaultprefix>
        <scenery />
        <objectslistprefix>You can see:</objectslistprefix>
        <firstenter type="script">
          msg ("The windows are shattered into oblivion and pieces of metal fragmentation from the control panel lie about.")
        </firstenter>
        <exit alias="out" to="Crash Sight">
          <inherit name="outdirection" />
        </exit>
      </object>
      <exit alias="in" to="P15D Mustang">
        <inherit name="indirection" />
      </exit>
      <command name="Enter Mustang">
        <pattern>enter mustang; go in mustang; enter plane; go in plane; enter p15d; go in p15d; enter p15d mustang; go in p15d mustang</pattern>
        <script>
          MoveObject (player, P15D Mustang)
        </script>
      </command>
    </object>
    <object name="Jungle">
      <inherit name="editor_room" />
      <usedefaultprefix type="boolean">false</usedefaultprefix>
      <alias>Jungle</alias>
      <firstenter type="script">
        msg ("After 12 minutes of walking, the smoke subsides.Thankfully you took note of it's general location, way up on a mountain.")
      </firstenter>
      <beforefirstenter type="script">
        RandomObjectChance
      </beforefirstenter>
      <exit alias="west" to="Parachute Landing">
        <inherit name="westdirection" />
      </exit>
      <exit alias="east" to="Mountain Base">
        <inherit name="eastdirection" />
      </exit>
    </object>
  </object>
  <object name="OBJECTS">
    <inherit name="editor_room" />
    <object name="Sticks">
      <inherit name="editor_object" />
      <take />
      <attr name="feature_usegive" type="boolean">false</attr>
      <alt type="stringlist">
        <value>Stick</value>
      </alt>
      <usedefaultprefix type="boolean">false</usedefaultprefix>
    </object>
    <object name="Stones">
      <inherit name="editor_object" />
      <take />
      <alt type="stringlist">
        <value>Stone</value>
      </alt>
      <usedefaultprefix type="boolean">false</usedefaultprefix>
    </object>
    <object name="Shelter">
      <inherit name="editor_object" />
    </object>
    <object name="Cordage">
      <inherit name="editor_object" />
      <take />
      <usedefaultprefix type="boolean">false</usedefaultprefix>
    </object>
    <object name="Fire Pit">
      <inherit name="editor_object" />
    </object>
    <object name="Hatchet">
      <inherit name="editor_object" />
      <take />
    </object>
    <object name="Edged Stone">
      <inherit name="editor_object" />
      <take />
      <usedefaultprefix type="boolean">false</usedefaultprefix>
      <prefix>an</prefix>
    </object>
    <object name="Spear">
      <inherit name="editor_object" />
      <take />
    </object>
    <object name="Torch">
      <inherit name="editor_object" />
      <take />
    </object>
    <object name="Bow">
      <inherit name="editor_object" />
      <take />
      <attr name="feature_usegive" type="boolean">false</attr>
      <alias>Bow</alias>
      <feature_startscript />
      <use type="script">
        if () {
        }
      </use>
      <attr name="_initialise_" type="script">
        Bow.arrows = 0
      </attr>
    </object>
    <object name="Arrow">
      <inherit name="editor_object" />
      <take />
      <usedefaultprefix type="boolean">false</usedefaultprefix>
      <attr name="feature_usegive" type="boolean">false</attr>
      <prefix>an</prefix>
    </object>
    <object name="Backpack">
      <inherit name="editor_object" />
      <take />
    </object>
    <object name="Fishing Rod">
      <inherit name="editor_object" />
      <take />
    </object>
    <object name="Water Bucket">
      <inherit name="editor_object" />
      <take />
    </object>
    <object name="Water Collector">
      <inherit name="editor_object" />
    </object>
    <object name="Poncho">
      <inherit name="editor_object" />
      <inherit name="wearable" />
      <take />
      <feature_wearable />
      <attr name="wear_layer" type="int">1</attr>
      <wearmsg>You are now wearing the poncho.</wearmsg>
      <removemsg>You have taken off the poncho.</removemsg>
      <wear_slots type="stringlist">
        <value>Torso</value>
      </wear_slots>
    </object>
    <object name="Wrapping">
      <inherit name="editor_object" />
      <take />
    </object>
    <object name="Bandage">
      <inherit name="editor_object" />
      <take />
    </object>
    <object name="Noose Trap">
      <inherit name="editor_object" />
    </object>
    <object name="Cambium">
      <inherit name="editor_object" />
      <take />
      <usedefaultprefix type="boolean">false</usedefaultprefix>
    </object>
    <object name="Boar">
      <inherit name="editor_object" />
      <object name="Animal Skin">
        <inherit name="editor_object" />
      </object>
      <object name="Raw Boar Meat">
        <inherit name="editor_object" />
      </object>
    </object>
    <object name="Hide">
      <inherit name="editor_object" />
      <take />
      <usedefaultprefix type="boolean">false</usedefaultprefix>
    </object>
    <object name="Trees">
      <inherit name="editor_object" />
      <inherit name="surface" />
      <alt type="stringlist">
        <value>Tree</value>
      </alt>
      <usedefaultprefix type="boolean">false</usedefaultprefix>
      <feature_container />
      <hidechildren />
      <listchildren />
      <attr name="feature_startscript" type="boolean">false</attr>
      <take type="boolean">false</take>
      <attr name="_initialise_" type="script"><![CDATA[
        this.regrows_after_turns = 50
        this.regrow => {
          if (HasObject(this, "original_location")) {
            newstone = CloneObjectAndMove (this, this.original_location)
            newstone.turns_since_picked = null
          }
        }
      ]]></attr>
      <object name="Bark">
        <inherit name="editor_object" />
        <take />
        <usedefaultprefix type="boolean">false</usedefaultprefix>
      </object>
      <object name="Large Leaves">
        <inherit name="editor_object" />
        <take />
        <alt type="stringlist">
          <value>Large Leaf</value>
          <value>Leaf</value>
        </alt>
        <usedefaultprefix type="boolean">false</usedefaultprefix>
      </object>
      <object name="TreeLog">
        <inherit name="editor_object" />
        <alias>Log</alias>
        <usedefaultprefix />
      </object>
    </object>
    <object name="Sap Tree">
      <inherit name="editor_object" />
      <object name="Sap">
        <inherit name="editor_object" />
        <usedefaultprefix type="boolean">false</usedefaultprefix>
      </object>
    </object>
    <object name="Aloe Vera">
      <inherit name="editor_object" />
      <usedefaultprefix type="boolean">false</usedefaultprefix>
      <attr name="feature_usegive" type="boolean">false</attr>
      <feature_startscript />
      <take type="script">
        if (not HasInt(this, "turns_since_picked")) {
          this.turns_since_picked = 0
          this.original_location = this.parent
        }
        AddToInventory (this)
        msg ("You pick up some Aloe Vera.")
      </take>
      <ontake type="script">
      </ontake>
      <attr name="_initialise_" type="script"><![CDATA[
        this.regrows_after_turns = 3
        this.original_object = this
        this.regrow => {
          if (HasObject(this, "original_location")) {
            CloneObjectAndMove (this.original_object, this.original_location)
          }
        }
      ]]></attr>
    </object>
    <object name="Worm">
      <inherit name="editor_object" />
      <usedefaultprefix />
      <scenery />
      <feature_startscript />
      <take type="script">
        AddToInventory (this)
        msg ("You pick up some Aloe Vera.")
        if (not HasInt(this, "turns_since_picked")) {
          this.turns_since_picked = 0
          this.original_location = this.parent
        }
      </take>
      <attr name="_initialise_" type="script"><![CDATA[
        this.regrows_after_turns = 50
        this.regrow => {
          if (HasObject(this, "original_location")) {
            newstone = CloneObjectAndMove (this, this.original_location)
            newstone.turns_since_picked = null
          }
        }
      ]]></attr>
    </object>
    <object name="Thorn Bush">
      <inherit name="editor_object" />
      <take />
      <object name="Thorn">
        <inherit name="editor_object" />
        <take />
      </object>
    </object>
    <object name="Cooked Boar Meat">
      <inherit name="editor_object" />
    </object>
  </object>
  <object name="Cold">
    <inherit name="editor_object" />
  </object>
  <object name="Hunger">
    <inherit name="editor_object" />
  </object>
  <object name="Thirst">
    <inherit name="editor_object" />
  </object>
  <object name="Sleep">
    <inherit name="editor_object" />
  </object>
  <turnscript name="Hazards">
    <enabled />
    <script><![CDATA[
      if (Time.minutes > 1259) {
        if (Time.minutes < 540) {
          Cold.value = Cold.value + 5
        }
      }
      Hunger.value = Hunger.value + 0.5
      Thirst.value = Thirst.value + 1
      Sleep.value = Sleep.value + 1.3
    ]]></script>
  </turnscript>
  <command name="Kill Boar">
    <pattern>kill boar, attack boar, hunt boar, chase boar</pattern>
    <script><![CDATA[
      if (ListContains(ScopeReachable(), Boar)) {
        if (Got(Bow)) {
          if (Bow.arrows > 0) {
          }
        }
      }
    ]]></script>
  </command>
  <command name="Craft Arrow">
    <pattern>Craft Arrow</pattern>
    <script><![CDATA[
      Bow.arrows = Bow.arrows + 1
      if (Bow.arrows = 1) {
        msg ("You now have 1 arrow.")
      }
      if (Bow.arrows > 1) {
        msg ("You now have " + Bow.arrows + " arrows.")
      }
    ]]></script>
  </command>
  <command name="Craft Bow">
    <pattern>Craft Bow</pattern>
    <script>
      CloneObjectAndMove (Bow, player)
      msg ("You have crafted a bow.")
    </script>
  </command>
  <turnscript name="Regenerative">
    <enabled />
    <script>
      object.turns_since_picked = object.turns_since_picked + 1
      if (object.turns_since_picked = object.regrows_after_turns) {
        do (object, "regrow")
      }
    </script>
  </turnscript>
  <function name="RandomObjectChance">
    while (RandomChance(50)) {
      CloneObjectAndMove (Large Leaves, player.parent)
    }
    while (RandomChance(50)) {
      CloneObjectAndMove (Stones, player.parent)
    }
    while (RandomChance(50)) {
      CloneObjectAndMove (Sticks, player.parent)
    }
    while (RandomChance(50)) {
      CloneObjectAndMove (Aloe Vera, player.parent)
    }
    while (RandomChance(10)) {
      CloneObjectAndMove (Sap Tree, player.parent)
    }
    if (RandomChance(10)) {
      CloneObjectAndMove (Boar, player.parent)
    }
    CloneObjectAndMove (Trees, player.parent)
    while (RandomChance(50)) {
      CloneObjectAndMove (Worm, player.parent)
    }
    while (RandomChance(10)) {
      CloneObjectAndMove (Thorn Bush, player.parent)
    }
  </function>
  <function name="ChanceToKillBoar">
  </function>
</asl>

hegemonkhan
11 Oct 2017, 17:18

in your 'Kill Boar' Command, it's the semicolon that is used by quest to separate its patterns, so in/for your Command's 'patterns' box/String-Attribute, you need to change your commas into semicolons:

from:

  <command name="Kill Boar">
    <pattern>kill boar, attack boar, hunt boar, chase boar</pattern>
    <script><![CDATA[
      if (ListContains(ScopeReachable(), Boar)) {
        if (Got(Bow)) {
          if (Bow.arrows > 0) {
          }
        }
      }
    ]]></script>
  </command>

to:

  <command name="Kill Boar">
    <pattern>kill boar; attack boar; hunt boar; chase boar</pattern>
    <script><![CDATA[
      if (ListContains(ScopeReachable(), Boar)) {
        if (Got(Bow)) {
          if (Bow.arrows > 0) {
          }
        }
      }
    ]]></script>
  </command>

Your 'Regenerative' Turnscript has a Variable (the 'object' Variable in it) that isn't storing a Value in it (hence an ERROR):

(mrangel gave the list, so I'm fixing/edit my code for it below)

  <turnscript name="Regenerative">
    <enabled />
    <script>
      object.turns_since_picked = object.turns_since_picked + 1
      if (object.turns_since_picked = object.regrows_after_turns) {
        do (object, "regrow")
      }
    </script>
  </turnscript>

to fix it:

(you need to replace my all upper-case stuff below, as I'm not sure what Object and what List Attribute it is suppose to be, probably the others can help you, if you don't know which it is to be)

(I got a few more checks in my scripting than 'mrangel' does, as I'm not clear on exactly your entire/whole design, so I put them in to be safe)

  <turnscript name="Regenerative">
    <enabled />
    <script>
      foreach (object, AllObjects ()) {
        if (HasInt (object, "turns_since_picked")) {
          object.turns_since_picked = object.turns_since_picked + 1
          if (HasInt (object, "regrows_after_turns")) {
            if (object.turns_since_picked = object.regrows_after_turns) {
              if (HasScript (object, "regrow")) {
                do (object, "regrow")
              } else {
                msg ("ERROR: this object: " + object.name + ", in your game, doesn't have the 'regrow' Script Attribute")
              }
            }
          } else {
            msg ("ERROR: this object: " + object.name + ", in your game, doesn't have the 'regrows_after_turns' Integer Attribute")
          }
        } else {
          msg ("ERROR: this object: " + object.name + ", in your game, doesn't have the 'turns_since_picked' Integer Attribute")
        }
      }
    </script>
  </turnscript>

Enpherdaen
11 Oct 2017, 17:24

HK oops I always make that comma/semicolan mistake.

As for the rest I don't know what you are saying to me.


hegemonkhan
11 Oct 2017, 17:27

also, not sure if you intended some of these things in this:

  <function name="RandomObjectChance">
    while (RandomChance(50)) {
      CloneObjectAndMove (Large Leaves, player.parent)
    }
    while (RandomChance(50)) {
      CloneObjectAndMove (Stones, player.parent)
    }
    while (RandomChance(50)) {
      CloneObjectAndMove (Sticks, player.parent)
    }
    while (RandomChance(50)) {
      CloneObjectAndMove (Aloe Vera, player.parent)
    }
    while (RandomChance(10)) {
      CloneObjectAndMove (Sap Tree, player.parent)
    }
    if (RandomChance(10)) {
      CloneObjectAndMove (Boar, player.parent)
    }
    CloneObjectAndMove (Trees, player.parent)
    while (RandomChance(50)) {
      CloneObjectAndMove (Worm, player.parent)
    }
    while (RandomChance(10)) {
      CloneObjectAndMove (Thorn Bush, player.parent)
    }
  </function>

I'm just going to re-order it, for better organization, so you can see what you've got, and if you need to change anything with it (if it's not what you wanted):

  <function name="RandomObjectChance">

    // your individual and specific Objects' 'while' Functions:

    while (RandomChance(50)) {
      CloneObjectAndMove (Large Leaves, player.parent)
    }

    while (RandomChance(50)) {
      CloneObjectAndMove (Stones, player.parent)
    }

    while (RandomChance(50)) {
      CloneObjectAndMove (Sticks, player.parent)
    }

    while (RandomChance(50)) {
      CloneObjectAndMove (Aloe Vera, player.parent)
    }

    while (RandomChance(10)) {
      CloneObjectAndMove (Sap Tree, player.parent)
    }

    while (RandomChance(50)) {
      CloneObjectAndMove (Worm, player.parent)
    }

    while (RandomChance(10)) {
      CloneObjectAndMove (Thorn Bush, player.parent)
    }

   // your 'if' Function (I'm presuming this is intended, as you only want a single chance of cloning and moving a single boar, right? if you use the 'while' you have chance of cloning and moving multiple boars):

    if (RandomChance(10)) {
      CloneObjectAndMove (Boar, player.parent)
    }

   // this will ALWAYS clone and move 'Trees' Object into the room you're in, aka: this Object's clone will always be added to the room you're in (is this intended by you or not?):

    CloneObjectAndMove (Trees, player.parent)

  </function>

mrangel
11 Oct 2017, 17:48

Your 'Regenerative' turnscript has some lines missing.

It should be

     foreach (object, AllObjects()) {
        if (HasInt(object, "turns_since_picked")) {
          object.turns_since_picked = object.turns_since_picked + 1
          if (object.turns_since_picked = object.regrows_after_turns) {
            do (object, "regrow")
          }
        }
      }

but you just have:

      object.turns_since_picked = object.turns_since_picked + 1
      if (object.turns_since_picked = object.regrows_after_turns) {
        do (object, "regrow")
      }

I think maybe you deleted these lines by accident, because when you pasted the code in the other thread it was correct.


hegemonkhan
11 Oct 2017, 17:49

(filler to getting my edited post, updated/posted)


(ya, I too have a hard time remembering whether whatever is suppose to be a comma or semicolon too, lol. Like for a Function's parameters, you separate them with commas, but a Command's pattern, you separate the patterns with a semicolon, and for a List, you separate them with semicolons, laughs. I get confused all of the time)


let me explain how the 'foreach' Function works, so you can better understand what we're doing with our/the code, in trying to help you:

<object name="team">

  <attr name="team_member_objectlist_attribute" type="objectlist">joe; jim; jon</attr>

  <object name="joe">
    <inherit name="team_type" />
  </object>

  <object name="jim">
    <inherit name="team_type" />
  </object>

  <object name="jon">
    <inherit name="team_type" />
  </object>

</object>

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

// this scripting is a way of also creating the above:

(I don't think the 'team_type' Type/Object-Type, can be created through scripting... someone can correct me if I'm wrong)

create ("team")

create ("joe", "team_type")
joe.parent = team // or: MoveObject (joe, team)

create ("jim", "team_type")
jim.parent = team // or: MoveObject (jim, team)

create ("jon", "team_type")
jon.parent = team // or: MoveObject (jon, team)

team.team_member_objectlist_attribute = NewObjectList ()
list add (team.team_member_objectlist_attribute, joe)
list add (team.team_member_objectlist_attribute, jim)
list add (team.team_member_objectlist_attribute, jon)

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

// and now to explain how the 'foreach' Function works:

foreach (team_member_object_variable, team.team_member_objectlist_attribute) {
  do (team_member_object_variable, "run_laps")
}

// this is how foreach works conceptively:

foreach (team_member_object_variable, team.team_member_objectlist_attribute) {

  // each item (joe, jim, jon) in the list (team.team_member_objectlist_attribute) is stored into the 'team_member_object_variable' Variable VARIABLE, one after the other, and does the scripting for each of them:

  team_member_object_variable = joe
  do (team_member_object_variable, "run_laps")
  do (joe, "run_laps")

  team_member_object_variable = jim
  do (team_member_object_variable, "run_laps")
  do (jim, "run_laps")

  team_member_object_variable = jon
  do (team_member_object_variable, "run_laps")
  do (jon, "run_laps")

}

hegemonkhan
11 Oct 2017, 18:00

here's mine (it got more checks and error messages for you to trouble shoot it, as I don't know your game/code design at all and thus put them in just to be safe and help out with trouble shooting any errors, than 'mrangel' does, so you can use either one: his or mine):

  <turnscript name="Regenerative">
    <enabled />
    <script>
      foreach (object, AllObjects ()) {
        if (HasInt (object, "turns_since_picked")) {
          object.turns_since_picked = object.turns_since_picked + 1
          if (HasInt (object, "regrows_after_turns")) {
            if (object.turns_since_picked = object.regrows_after_turns) {
              if (HasScript (object, "regrow")) {
                do (object, "regrow")
              } else {
                msg ("ERROR: this object: " + object.name + ", in your game, doesn't have the 'regrow' Script Attribute")
              }
            }
          } else {
            msg ("ERROR: this object: " + object.name + ", in your game, doesn't have the 'regrows_after_turns' Integer Attribute")
          }
        } else {
          msg ("ERROR: this object: " + object.name + ", in your game, doesn't have the 'turns_since_picked' Integer Attribute")
        }
      }
    </script>
  </turnscript>

K.V.
11 Oct 2017, 20:11

This is your Regenerative turn script (which is not the entire script posted by mrangel):

  <turnscript name="Regenerative">
    <enabled />
    <script>
      object.turns_since_picked = object.turns_since_picked + 1
      if (object.turns_since_picked = object.regrows_after_turns) {
        do (object, "regrow")
      }
    </script>
  </turnscript>

It is missing the first two lines.


This is mrangel's turn script, which is what you need:

  <turnscript name="Regenerative">
    <script>
      foreach (object, AllObjects()) {
        if (HasInt(object, "turns_since_picked")) {
          object.turns_since_picked = object.turns_since_picked + 1
          if (object.turns_since_picked = object.regrows_after_turns) {
            do (object, "regrow")
          }
        }
      }
    </script>
    <enabled />
  </turnscript>

The first line defines the variable: object.

I shall break that line down.

foreach (object, AllObjects()) {

foreach will perform whatever operation you have in this script for each thing you specify later in this line.

object is the variable foreachwill use in each script.

AllObjects() returns a list of every object in the game.

So foreach (object, AllObjects()) {, basically says this in English:

For each object in the entire game:

    Do stuff to the object.

if (HasInt(object, "turns_since_picked")) {

This simply checks whether the object being ran in the script has an integer value for the attribute "turns_since_picked".

This will not work with having declared the variable: object. Therefore, it follows the foreach line of the script.


Enpherdaen
11 Oct 2017, 20:40

Yay it finally works and has no errors! Only took 131 messages ahaha.

I have no clue how it works though. I hate not knowing.


mrangel
11 Oct 2017, 21:24

I can try to explain it a bit better; but I'm rushing against work deadlines right now, so will come back and edit this later if nobody beats me to it.


K.V.
11 Oct 2017, 21:42

Yay!

Go, Enpherdaen!

Here's the documentation concerning foreach. (I meant to link to it earlier.)

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


I'll be watching for your post in Game Announcements so I can sign on to be one of the beta-testers once it's completed.

From what I've seen so far, it will be good!


hegemonkhan
12 Oct 2017, 01:11

(filler for getting my edited post, updated/posted)
(again, filler for getting my edited post, updated/posted)
(again, filler for getting my edited post, updated/posted)


(taken from one of my posts, from a few posts up)

I already try to explain how the 'foreach' Function works:

(I took out the 'team_type' to make it less confusing, and changed some other stuff up, to make it work better as an example for you to learn from)

<object name="team">

  <attr name="team_member_objectlist_attribute" type="objectlist">joe; jim; jan</attr>

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

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

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

</object>

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

// this scripting (see here) is a way of also creating the tag stuff (seen above):

create ("team")

script_variable => { msg (this.name + " runs laps") }

create ("joe")
set (joe, "run_laps_script_attribute", script_variable)
set (joe, "parent", team)

create ("jim")
set (jim, "run_laps_script_attribute", script_variable)
set (jim, "parent", team)

create ("jan", "team_type")
set (jan, "run_laps_script_attribute", script_variable)
set (jan, "parent", team)

team.team_member_objectlist_attribute = NewObjectList ()
list add (team.team_member_objectlist_attribute, joe)
list add (team.team_member_objectlist_attribute, jim)
list add (team.team_member_objectlist_attribute, jan)

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

// and now to explain how the 'foreach' Function works:

foreach (team_member_object_variable, team.team_member_objectlist_attribute) {
  do (team_member_object_variable, "run_laps_script_attribute")
}

// this is how foreach works conceptively:

foreach (team_member_object_variable, team.team_member_objectlist_attribute) {

  // each item (joe, jim, jon) in the list (team.team_member_objectlist_attribute) is stored into the 'team_member_object_variable' Variable VARIABLE, one after the other, and does the scripting for each of them:

  team_member_object_variable = joe
  do (team_member_object_variable, "run_laps_script_attribute")
  do (joe, "run_laps_script_attribute")

  team_member_object_variable = jim
  do (team_member_object_variable, "run_laps_script_attribute")
  do (jim, "run_laps_script_attribute")

  team_member_object_variable = jan
  do (team_member_object_variable, "run_laps_script_attribute")
  do (jan, "run_laps_script_attribute")

}

// results/output:

joe runs laps
jim runs laps
jan runs laps

the difference is instead of a custom object list (my example), you're using instead a built-in objectlist of EVERY/ALL Objects in the entire game via using this: 'AllObjects ()'


K.V.
12 Oct 2017, 01:35

Sorry about that, HK! I didn't even see your post concerning foreach!

Sometimes the page is on my screen for an hour or more while I flip back and forth between different things I've got going on.

I now realize it would be more efficient to look at the thread in a new tab before writing/continuing to write/editing my post during those instances.

(I'm easily distracted. I minimize Chrome after reading a thread to test something in Quest, then I end up doing something else in a totally different game while I've got Quest open, then I look up something about that... You get the picture, I'm sure.)


Enpherdaen
12 Oct 2017, 01:35

I must understand why every section of the 3 codes related to object respawning is as it is.


Enpherdaen
12 Oct 2017, 02:05

K.V. Do not get your hopes up about me finishing this game, it seems every problem I run into is a result only of there being random clones everywhere and trying to figure out how to refer to specific, hypothetical clones.


K.V.
12 Oct 2017, 02:55

I have that same problem with clones.

mrangel has a much better approach to them than I could ever come up with. (I learn at least one thing from mrangel every time he posts something. The same goes for HK, Pixie, and a few other people whose names are currently escaping me.)

I bet you get it, though.

One little trick (edited):

foreach (variable, ScopeInventory()) {  //For each thing (which we'll call 'variable') in the player's inventory...
  if (variable.alias = "Aloe Vera")) {  //If the alias of this 'variable' is "Aloe Vera"...
    msg ("You are carrying an Aloe Vera which is named: " + variable.name + ".")
   }
}

Here's an insignificant example game you can play with (changing things around and adding things and such), which will probably help:

CLICK HERE TO VIEW THE EXAMPLE CODE (EDITED).
<!--Saved by Quest 5.7.6494.25078-->
<asl version="550">
  <include ref="English.aslx" />
  <include ref="Core.aslx" />
  <game name="aloe clones">
    <gameid>9fb198ae-6fd9-4e5a-947a-f0aedc22bf56</gameid>
    <version>1.0</version>
    <firstpublished>2017</firstpublished>
  </game>
  <object name="room">
    <inherit name="editor_room" />
    <beforeenter type="script"><![CDATA[
      variable = 10
      while (variable > 0) {
        CloneObjectAndMoveHere (Aloe Vera)
        variable = variable - 1
      }
    ]]></beforeenter>
    <object name="player">
      <inherit name="editor_object" />
      <inherit name="editor_player" />
    </object>
  </object>
  <object name="Aloe Vera">
    <inherit name="editor_object" />
    <take />
  </object>
  <turnscript name="gotAloeVera">
    <enabled />
    <script>
      foreach (variable, ScopeInventory()) {
        if (variable.alias = "Aloe Vera") {
          msg ("You are carrying an Aloe Vera which is named: " + variable.name + ".")
        }
      }
    </script>
  </turnscript>
</asl>

mrangel
12 Oct 2017, 09:10

OK ... here's an attempt at a line-by-line explanation. Maybe that will help

First, the take script. This is run whenever the player tries to take the object, or a clone of the object. In the 'take' script, the special variable this refers to the clone that the player is trying to take.

  • if (not HasInt(this, "turns_since_picked")) { - if we're not already counting how many turns since the object was picked up. This ensures that the following two lines don't run again if the player drops the item somewhere else and then picks it up.
    • this.turns_since_picked = 0 - set the attribute "turns_since_picked" to zero. As soon as this is a number, the turnscript knows to start increasing it every turn.
    • this.original_location = this.parent - Create an attribute "original_location", remembering where the object was before it was picked up. We'll need this later.
  • }
  • AddToInventory(this) - The 'take' script is run instead of adding an object to the player's inventory. So once we've done the special stuff, we need to add it ourselves.
  • msg ("You pick up "+GetDisplayName(this)) - And tell the player that their action succeeded

Then the turnscript. This is run once every turn, after the player's action has been dealt with.

  • foreach (object, AllObjects()) { - Repeat the following code once for every object in the game, setting the variable 'object' to the one we're currently looking at.
    • if (HasInt(object, "turns_since_picked")) { - We're only interested in doing things to the objects that have a "turns_since_picked" attribute that is a number. That's the ones for which the 'take' script above has already run.
      • object.turns_since_picked = object.turns_since_picked + 1 - Set the attribute 'turns_since_picked' to 1 more than it was before; just counting the number of turns since it was picked
      • if (object.turns_since_picked = object.regrows_after_turns) { - If this object has reached the number of turns at which it needs to regrow
        • do (object, "regrow") - Run the object's "regrow" script. Within the regrow script, we will be able to use this to refer to the object that is object here.
      • }
    • }
  • }

And finally, the initialisation script. This is run at the very start of the game; this refers to the original object, before it is cloned.

  • this.regrows_after_turns = 50 - Creates an attribute to set how many turns between this object being picked, and its regrow script being called. If you wanted to, you could use different numbers for different regrowable objects
  • this.original_object = this - Allow the object to keep a record of which object it is a clone of. You could change the script to work without this, but it is very useful. (in fact, I'd recommend putting this line in the initialisation script of every object that is likely to be cloned, because it makes a lot of scripting easier)
  • this.regrow => { - Create a script called 'regrow' for this object. Within the script, this means the clone that was picked a certain number of turns ago.
    • if (HasObject(this, "original_location")) { - This shouldn't be necessary, but it does no harm. Makes sure that this clone has an attribute referring to the room where it was picked.
      • CloneObjectAndMove (this.original_object, this.original_location) - Make a new clone of 'original_object' (the object that this is a clone of), and put it in 'original_location' (the place this was first taken from)
    • }
  • }

Hope that makes sense.


mrangel
12 Oct 2017, 09:54

Extra advice on dealing with clones:
(edited because I'd carelessly typed my own function name RemoveItem, rather than the core function RemoveObject)

I'll add that KV's example for looking at how many clones of something you have is pretty good. But in this case, because of the this.original_object = this line in the initialisation script, it's quicker to use that.

You can use if (StartsWith(variable.name, "Aloe Vera")) { to check if an object is a clone of Aloe Vera; but this can lead to hard-to-spot errors sometimes. For example, if a game has both a Cat and a Catapult, you might find that a clone called Catapult12 does have a name that starts with "Cat", so it would set off any scripts that are intended to check if there's a cat in the room.

So I'd recommend if (variable.original_object = Aloe Vera) { - to directly test which object this is a clone of.

You could also do:
avlist = FilterByAttribute(ScopeInventory(), "original_object", Aloe Vera)
which makes avlist a list of all the Aloe Vera clones the player is currently carrying.

This is one of the reasons I'd recommend putting this.original_object in the initialisation script for all the cloneable objects. Or to make it a little more automated, in the game start script:

foreach (object, AllObjects()) {
  object.original_object = object
}

(this would make sure that every object has an "original_object" attribute, rather than having to put it in all their initialisation scripts)

In a crafting system, you might then do something like…

sticks = FilterByAttribute (ScopeInventory(), "original_object", Stick)
stones = FilterByAttribute (ScopeInventory(), "original_object", Stone)
if ((ListCount(sticks) < 4) or (ListCount(stones) < 2)) {
  msg ("You need at least 4 sticks and 2 stones to make a campfire")
}
else {
  for (i, 0, 3) {
    RemoveObject (ListItem(sticks, i))
  }
  for (i, 0, 1) {
    RemoveObject (ListItem(stones, i))
  }
  CloneItemAndMoveHere (Campfire)
  msg ("You build a fire!")
}

K.V.
12 Oct 2017, 10:41

That original_object attribute is pretty slick, mrangel!


mrangel
12 Oct 2017, 14:33

Thanks. In mine the attribute is named "prototype"; but I figured that's likely to be unintuitive to anyone else reading my code.

While the mood struck me, I've written up a more comprehensive version of that crafting system. Not sure if I'll end up using it, but it gives my mind something to do when I'm failing at getting actual books written.


Enpherdaen
13 Oct 2017, 15:20

Great job at explaining guys! (Don't worry, that's not sarcasm). I'm understanding a lot more now.


mrangel
13 Oct 2017, 15:41

(I can't believe that worked, just writing code off the top of my head… three missing brackets, one dictionary incorrectly declared as an objectdictionary, and then it works as intended. I now have a crafting system that allows ingredients that you need a specified number of, ingredients that aren't consumed (like the knife), and ingredients that don't need to be picked up (like the campfire); if you need an ingredient you're not carrying it will try to pick it up; it behaves sanely for objects whose 'take' script hands you a clone or similar, including not consuming any items if you fail to pick up an item for some reason; and if you have enough ingredients to make more than one of an item it prompts you for a number; or allows you to enter "craft 4 meals")

Why have I invested like 2 hours coding time on this, when my current game doesn't really need it?


jmnevil54
13 Oct 2017, 16:35

Wow, Enpherdaen can read that, when that is all gibberish to me...


mrangel
16 Oct 2017, 15:49

Well, playing around with cloned objects inspired me to make a crafting system and a half-assed survival game.

Not on the same scale as yours, but I'd be happy to share the scripts if you think any of the code in this would be useful to you.

http://textadventures.co.uk/games/view/luz7uwwsuuqfmk-pmtnx8w/disposable-game


jmnevil54
16 Oct 2017, 19:14

mrangel, I play the game and I can't kill the chicken. You also didn't put any story in it. Also, why is there a Bible, a green bible, a blue-green Bible, and a pink magic tome?


K.V.
16 Oct 2017, 19:33

Very nice, mrangel!


Enpherdaen
16 Oct 2017, 20:14

That is a weird, weird game. I also cannot craft anything.


mrangel
16 Oct 2017, 20:35

mrangel, I play the game and I can't kill the chicken.

No, you can't. The chicken was there to test a bug.

You also didn't put any story in it.

Yep; it's more a test/demo of the systems

Also, why is there a Bible, a green bible, a blue-green Bible, and a pink magic tome?

Because it assigns random adjectives to the books, to give them all different names.

I also cannot craft anything.

Why not? Does it give some error?


Enpherdaen
16 Oct 2017, 21:45

Because anything I try to craft, it says "you don't know how to craft (object)"


jmnevil54
16 Oct 2017, 21:56

@Enpherdaen You use the survivor manual.


mrangel
16 Oct 2017, 21:59

Because anything I try to craft, it says "you don't know how to craft (object)"

Try just "craft" on its own to get a list of known recipes. I know I should make that a bit more obvious in the game somehow; but I'm not sure how.

(if you're short of materials, a new item will spawn in the starting room every time you enter. A bit surreal; but then this was designed just to test the crafting system, rather than as a real game)


Enpherdaen
16 Oct 2017, 22:02

Now I already have 2 more issues with clones, and the rest of my issues with probably relate to clones anyway.

Issue 1:
In my "kill boar" command, if the player possesses the prequisites, then to boar "dies" and the player can then access it's body. Problem is telling Quest which boar to "kill". I can't use "this" because it's a command, not a script within an object. I suppose I could go into the object verbs and make the command, but that doesn't seem right.

Issue 2: I plan on having the player's inventory work by having 1 object have a increasable and decreasable value rather than actually stack object. So if the player picks up 2 sticks, the player will recieve 1 stick clone with a value of 2. However, I once again do not know how tot ell Quest which stick to increase the value of.


mrangel
16 Oct 2017, 22:45

In my "kill boar" command, if the player possesses the prequisites, then to boar "dies" and the player can then access it's body. Problem is telling Quest which boar to "kill". I can't use "this" because it's a command, not a script within an object. I suppose I could go into the object verbs and make the command, but that doesn't seem right.

I noticed that when looking at your code previously. Was going to suggest something, but didn't want to distract in the middle of working on the previous issues.

The pattern kill boar, attack boar, hunt boar, chase boar would probably be better as kill #object#;attack #object#;hunt #object#;chase #object#.

That way, if the player types "kill boar", you get a variable object which refers to the boar in the room with them. However, it does mean that they can also enter "kill stick" or something equally silly. So you start the command script by testing if (object.original_object = boar) { to check that the object they're trying to kill is a boar, and display some kind of error message otherwise.

(In case you're wondering, within a command script this refers to the command itself. There are a couple of rare cases where it's actually useful)

I plan on having the player's inventory work by having 1 object have a increasable and decreasable value rather than actually stack object. So if the player picks up 2 sticks, the player will recieve 1 stick clone with a value of 2. However, I once again do not know how tot ell Quest which stick to increase the value of.

The way I did this was using a take script for stackable object, arranged as:

  • Get a list of sticks currently in the player's inventory (using the original_object method I mentioned earlier)
  • If the size of this list is 0
    • Clone a new stick into the inventory, and set its count to zero. Set a variable destination_stack to be the new clone.
  • else
    • Set destination_stack to be the first (presumably only) object in the list
  • Then we know that this is the one they're trying to pick up, and destination_stack is the one in their inventory
  • Decrease this.count, and increase destination_stack.count by the same number.

Then I gave it a script changedcount, which is automatically called whenever the attribute count changes.

  • If this.count is zero, RemoveObject(this)
  • if this.count is one, set this.alias to its singular form ("stick", for example)
  • If this.count is more than one, set this.alias to something like "Sticks (3)"

So the changedcount script on the one on the ground removes it because it's a stack that's been reduced to zero, and the changedcount of the one the player is carrying changes its name to singular or plural appropriately. (My script was set up so that all objects are stacks, so you could have "Pile of sticks (5)" on the ground, as well as "Sticks (2)" in your inventory, and move objects between them)

It'll vary depending whether you want the player to pick up one at a time, or all the ones they can see. And if you want objects on the ground to be stacked into a single item, or to appear separately in the list.

But however you set it up, you'll want a way to find the one the player is holding. If your stick has the this.original_object = this in its initialisation script (or you used the little script I mentioned earlier in the game start script), you'll probably end up with something like:

carrying = FilterByAttribute(ScopeInventory(), "original_object", Stick)   // or whatever object it is
if (ListCount(carrying) = 0) {
  // The player isn't carrying any Sticks
  //   so give them one, whether by moving `this` or cloning a new one
}
else {
  carrying = ListItem(carrying, 0)
  // The variable `carrying` is now the object the player's holding
  //    so code goes here to increase its value
}

Enpherdaen
17 Oct 2017, 20:10

Mrangel I'm a newbie at coding and can barely udnerstand all that lol.


mrangel
17 Oct 2017, 23:21

Sorry :S I find it hard to explain things, because my brain is messed up.

(I had a neurologist do these stupid scans and conclude that I don't use the part of the brain normally responsible for language processing; that I deal with all languages in the parts of the brain normally only used for translating foreign language. I've got a chunk of brain damage in the areas normally used for "natural" language processing; and what remains of those areas has somehow been taken over for programming; possibly because I was learning Perl at about the same time the white matter recovered about as far as it ever will)


jmnevil54
18 Oct 2017, 00:26

I can relate somewhat mrangel. My autism interferes with my language skills a lot. I find it hard to process sentences, it's hard to learn new words grammar, it's technically a reading disability.

Although, technically I think you're just assuming Enpherdaen (and me) are smarter than we really are.

I read this story about a man near an irradiated area, drank some water in a local stream, and then when he died soon after (supposedly of natural causes, not because of the radiation poisoning), the found that his brain used the Radioactive carbon 14 molecules to repair his brain. Of course, for this to work as a viable treatment, you'd need to find an irradiated body of water, then live there for however long it takes to heal/grow back your brain. ...yeah... Getting cancer for the sake of brain repair... well, it works in small amounts, as it did with that guy....


mrangel
18 Oct 2017, 01:18

Not so much 'smarter'. Just I think more easily in code; so for me, explaining it is the hard part.

Maybe a direct example will make it easier to understand how I'm thinking?

In the last version I've seen, you had this function:

<command name="Kill Boar">
    <pattern>kill boar, attack boar, hunt boar, chase boar</pattern>
    <script><![CDATA[
      if (ListContains(ScopeReachable(), Boar)) {
        if (Got(Bow)) {
          if (Bow.arrows > 0) {
          }
        }
      }
    ]]></script>
  </command>

If I was working on this game, I'd probably change that to something like:

<command name="Kill">
    <pattern>kill #object#;attack #object#;hunt #object#;chase #object#</pattern>
    <script><![CDATA[
      if (Equal(object.original_object, Boar)) {
        if (Got(Bow)) {
          if (Bow.arrows > 0) {
            // Code to kill the boar goes here
            // you can use 'object' to refer to the boar
          }
        }
      }
      else {
        // If this executes, it means the player has typed "kill thorn bush", or some other
        //   object that isn't a boar. So we show an error.
        msg ("You can't see a reason to fight the "+GetDisplayName(object)+".")
      }
    ]]></script>
  </command>

(to make that work, you would need to give the boar an initialisation script, this.original_object = this; or in the game start script put 3 lines foreach (object, AllObjects()) { / object.original_object = object / } ... most of my clone-handling methods use the same original_object attribute so that every clone knows which object it is a clone of)


Enpherdaen
18 Oct 2017, 02:47

"you can use 'object' to refer to the boar"

What?

So what goes in it's initialisation script?


jmnevil54
18 Oct 2017, 03:01

"
boar an initialisation script, this.original_object = this.
; or in the game start script put 3 lines foreach (object, AllObjects()) { / object.original_object = object / } ... most of my clone-handling methods use the same original_object attribute so that every clone knows which object it is a clone of)
"


jmnevil54
18 Oct 2017, 03:29

"
So you start the command script by testing if (object.original_object = boar) { to check that the object they're trying to kill is a boar, and display some kind of error message otherwise.
"


mrangel
18 Oct 2017, 08:38

Most of the scripts I use to deal with clones depend on an attribute to refer to the original object. There are two ways to set that up. Either way works:

  1. In the initialisation scripts for all objects that are likely to be cloned
this.original_object = this

or

  1. In the game's start script
foreach (object, AllObjects()) {
  object.original_object = object
}

This just makes an easier way for a script to look at some variable (usually object) and check which object it's a clone of.


Enpherdaen
18 Oct 2017, 16:05

"That way, if the player types "kill boar", you get a variable object which refers to the boar in the room with them".

What do you mean by this, and why do I need it?

"So you start the command script by testing if (object.original_object = boar) { to check that the object they're trying to kill is a boar, and display some kind of error message otherwise".

Why do I need it to "check" if the object they are trying to kill is a boar when I will tell Quest in the killing the boar part of the script that it is the boar which will be "killed"?

Sorry I'm just very confused with this type of approach in general. I don't get why I gotta switch boar to object and get a variable object etc. By the way, this is how my command looks like currently (it is unfinished as I need to figure out telling Quest which boar to "kill"):

if (ListContains(ScopeReachable(), Boar)) {
  if (Got(Bow)) {
    if (Bow.arrows > 0) {
      Bow.arrows = Bow.arrows - 1
    }
  }
}

mrangel
18 Oct 2017, 21:12

(Edited: Ooops, had an unnecessary line accidentally pasted in the first script)

Why do I need it to "check" if the object they are trying to kill is a boar when I will tell Quest in the killing the boar part of the script that it is the boar which will be "killed"?

I try to make code that behaves sensibly even if the player does something unexpected. If they've noticed that they can type "get sti" to pick up a stick, then they might be surprised to find that they have to type "kill boar" in full.

it is unfinished as I need to figure out telling Quest which boar to "kill"

Letting the player pick an object and then checking if it's a boar will probably be easier than getting Quest to figure out which boar they want to kill.
Both are relatively simple; so it's only a small difference really.

Two options:

  1. Your preferred method
<command name="Kill Boar">
  <pattern>kill boar, attack boar, hunt boar, chase boar</pattern>
  <script><![CDATA[
    if (Got(Bow)) {
      if (Bow.arrows > 0) {
        boarlist = FilterByAttribute (ScopeReachableNotHeld(), "original_object", Boar)
        if (ListCount(boarlist) > 0) {
          thisboar = ListItem (boarlist, 0)
          Bow.arrows = Bow.arrows - 1
          // Code to kill 'thisboar'
        }
        else {
          msg ("There isn't a boar here.")
        }
      }
      else {
        msg ("You don't have any arrows.")
      }
    }
    else {
      msg ("You don't have a bow.")
    }
  }
]]></script>
</command>

or

  1. The method I'd use
<command name="Kill">
  <pattern>kill #object#;attack #object#;hunt #object#;chase #object#</pattern>
  <script><![CDATA[
    if (Equal(object.original_object, Boar)) {
      if (Got(Bow)) {
        if (Bow.arrows > 0) {
          Bow.arrows = Bow.arrows - 1
          // Code to kill 'object' here
        }
        else {
          msg ("You don't have any arrows.")
        }
      }
      else {
        msg ("You don't have a bow.")
      }
    }
    else {
      msg ("You can't see a reason to fight the "+GetDisplayName(object)+".")
    }
  ]]></script>
</command>

I think realistically, those are about the same length. I just prefer the version with #object# because it will behave more like other commands.

With kill #object#, you know that the player has typed the name of something in the room with them, but you need to check it's a boar.

With kill boar, you know that the player has typed the name of a boar, but you have to check if there's one in the room with them.


jmnevil54
18 Oct 2017, 21:28

Actually Enpherdaen, every time you do something to a clone, the default response for Quest is show a menu. It's already built in.

(Only when typing)
The built in response:

attack blue troll
which troll?

  1. Blue troll 1
  2. Blue troll 2

click on blue troll 2
Whatever hit points are taken out.

(This assumes they are in the same room.)

(My own game. Try it out for yourself!
http://textadventures.co.uk/games/view/xb0ge9kzbewhodrtmxnnqw/the-legend-of-the-secret-of-the-smelly-stinky-fish
)


jmnevil54
18 Oct 2017, 21:33

(My game has combat too... Heh, now I'm just patting my own back...)


hegemonkhan
19 Oct 2017, 00:11

(filler for getting my edited post, updated/posted)
(again, filler for getting my edited post, updated/posted)
(again, filler for getting my edited post, updated/posted)
(again, filler for getting my edited post, updated/posted)


@ Jmnevil54:

congrats on doing combat jmnevil54 !!! even simple (let alone more complex) combat takes some time to learn how to do it, and get it working as you want! It took me a long time to learn how to do damage and displaying it, and then to select/choose to attack, and/vs other actions, lol. Not easy to learn... and that's about as simple as you can get with combat design... lol.


@ enpherdean:

unfortunately, computers are stupid, you got to tell them exactly what to do... think of a computer as a very stupid child/baby, you got to hold its hand in-for getting it to do exactly what you want it to do.

the other big part (frustrating mess) of coding... is dealing ("handling") with the user's inputs...

for example,

I can have 'type in your age', which is a simple enough action for the user to do:

msg ("Age?")
get input {
  player.age = ToInt (result)
}

unfortunately... users are either (or whatever combination) stupid, curious, and/or malignant.

for whatever the motive (stupidity, curiousness, malice), the user types in 'sdfneo'

which unfortunately will cause an error because of the 'ToInt', as 'sdfneo' can't be converted into an Integer Data Type.

thus, we have to code in "handling the user/user's input". (handling 'user input' is just one part of what is known as "error and exception handling") ... for example:

msg ("Age?")
get input {
  if (IsInt) {
    player.age = ToInt (result)
  } else {
    msg ("ERROR: wrong input: You did NOT type in an Integer (a non-decimal number), which you must do so, please try again")
  }
}

we're good now, right?

well... not neccesarily.... unfortunately...

the person could type in '-1', which would be a problem, as there's no such thing as a negative age (unless you want to count zygote to birth as negative age, lol), or they could type in '200', which, (in 2017) is impossible for a human to be that age, lol. for example:

msg ("Age?")
get input {
  if (IsInt) {
    input_integer_variable = ToInt (result)
    if (input_integer_variable > 0 and input_integer_variable < 120) {
      player.age = ToInt (result)
    } else {
      msg ("ERROR: wrong input: You must enter a number (Integer: non-decimal number) between 0 and 120, please try again")
    }
  } else {
    msg ("ERROR: wrong input: You did NOT type in an Integer (a non-decimal number), which you must do so, please try again")
  }
}

now, we handled everything that's possible by user input, yay!

though, most stuff requires a lot more handling than just what I've shown with this example


a great way to store/save a specific clone as well as its origin (original object parent), is by using Attributes:

  1. an Attribute on/of the clone Object itself for storing its (reference/pointer of its) original object, so you have access to it
  2. an Attribute elsewhere to store that specific (reference/pointer of the) clone object, so you have access to it

  1. (see below)
create ("box") // creating our original object

clone_object_variable = CloneObject (box) // cloning our box, and temporarily storing its reference/pointer to it, into our local/temporary 'clone_object_variable' Variable VARIABLE

// we do this because/so-that, we can now do this:

clone_object_variable.original_object_pointer_object_attribute = box // we're storing the (reference/pointer of/to the) 'box' (original object) into our 'original_object_pointer_attribute' Object (reference/pointer) Attribute, on our specific (and just made) clone object

msg ("The original Object of our clone, " + clone_object_variable.name + ", is: " + clone_object_variable.original_object_pointer_object_attribute.name)

// output/result:

The original Object of our clone, box1, is: box

// here it what our Objects look like, an example:

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

<object name="box1">
  <attr name="original_object_pointer_object_attribute" type="object"box</attr>
</object>

// and let's say we've made more clones (looped/did-again the scripting, say, 2 more times):

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

<object name="box1">
  <attr name="original_object_pointer_object_attribute" type="object"box</attr>
</object>

<object name="box2">
  <attr name="original_object_pointer_object_attribute" type="object"box</attr>
</object>

<object name="box3">
  <attr name="original_object_pointer_object_attribute" type="object"box</attr>
</object>

// results/output:

The original Object of our clone, box1, is: box
The original Object of our clone, box2, is: box
The original Object of our clone, box3, is: box


  1. (see below)

(see if you can understand/follow this without any comments/explanations on what is happening... as... I'm just lazy... lol)

game.clone_objectlist_attribute = NewObjectList ()

create ("box")

clone_object_variable = CloneObject (box)

list add (game.clone_objectlist_attribute, clone_object_variable)

clone_object_variable = CloneObject (box)

game.clone_object_attribute = clone_object_variable

list add (game.clone_objectlist_attribute, clone_object_variable)

clone_object_variable = CloneObject (box)

list add (game.clone_objectlist_attribute, clone_object_variable)

msg ("Here is specifically the 'box2' clone, if: box2 = " + game.clone_object_attribute.name + ", is true (matching: box2 = box2)")

DisplayList (game.clone_objectlist_attribute, true) // or: DisplayList (game.clone_objectlist_attribute, 1)

failed_boolean_attribute = true

while (failed_boolean_attribute) { // actually, you can't use the 'while' Function with anything that gets user input (which the 'show menu' Function below does... just pretend that it works... this is just an example... lol
  game.clone_object_attribute = StringListItem (game.clone_objectlist_attribute, GetRandomInt (0, ListCount (game.clone_objectlist_attribute) - 1))
  show menu ("Guess the correct clone in my hand!", game.clone_objectlist_attribute, false) {
    if (result = game.clone_object_attribute.name) {
      msg ("Ding ding ding! Correct!")
      failed_boolean_attribute = false
    } else {
      msg ("Bzz! Wrong, try again!")
    }
  }
}

// output/result:

Here is specifically the 'box2' clone, if: box2 = box2, is true (matching: box2 = box2)

1. box1
2. box2
3. box3

Guess the correct clone in my hand!

// (popup menu window)
box1
box2
box3

// if you select the same as the randomly selected clone:
Ding ding ding! Correct!
// otherwise:
Bzz! Wrong, try again!

Enpherdaen
19 Oct 2017, 00:43

Okay so, what do you good lads propose I do in order to cause the object (boar) to be killed?


hegemonkhan
19 Oct 2017, 01:32

(filler for getting my edited post, updated/posted)
(again, filler for getting my edited post, updated/posted)
(again, filler for getting my edited post, updated/posted)


the easiest is via using a 'dead' Boolean Attribute, which you can then use for whatever else you want to do, such as commonly in RPGs, looting the corpse:

(using a Object Type / Type / Inherited Attribute, and a 'loot' Script Attribute / Verb, for an example design)

<object name="Boar">

  <inherit name="boar_type" />

  <attr name="original_object" type="object">Boar</attr>

  <attr name="experience" type="int">5</attr>
  <attr name="currency" type="int">10</attr>

</object>

<type name="boar_type">

  <inherit name="monster_type" />

</type>

<type name="monster_type">

  <attr name="dead" type="boolean">false</attr>

  <attr name="displayverbs" type="listextend">loot</attr>

  <attr name="loot" type="script">
    if (this.dead) {
      firsttime {
        player.experience = player.experience + this.experience
        player.currency = player.currency + this.currency
        msg ("You loot the dead " + this.name + "'s corpse")
      } otherwise {
        msg ("You've already looted the dead " + this.name + "'s corpse, silly")
      }
    } else {
      msg ("You have to kill the boar first, before you can loot it, silly")
    }
  </attr>

</type>

<verb>

  <property>loot</property>
  <pattern>loot</pattern>

  <defaultexpression>You can't loot that!</defaultexpression>

</verb>

using mrangel's code:

  1. Your prefered method
<command name="Kill Boar">
  <pattern>kill boar, attack boar, hunt boar, chase boar</pattern>
  <script><![CDATA[
    if (ListContains(ScopeReachable(), Boar)) {
      if (Got(Bow)) {
        if (Bow.arrows > 0) {
          boarlist = FilterByAttribute (ScopeReachableNotHeld(), "original_object", Boar)
          if (ListCount(boarlist) > 0) {
            thisboar = ListItem (boarlist, 0)
            Bow.arrows = Bow.arrows - 1
            thisboar.dead = true
          }
          else {
            msg ("There isn't a boar here.")
          }
        }
        else {
          msg ("You don't have any arrows.")
        }
      }
      else {
        msg ("You don't have a bow.")
      }
    }
  ]]></script>
</command>
  1. The method I'd use
<command name="Kill">
  <pattern>kill #object#;attack #object#;hunt #object#;chase #object#</pattern>
  <script><![CDATA[
    if (Equal(object.original_object, Boar)) {
      if (Got(Bow)) {
        if (Bow.arrows > 0) {
          Bow.arrows = Bow.arrows - 1
          object.dead = true
        }
        else {
          msg ("You don't have any arrows.")
        }
      }
      else {
        msg ("You don't have a bow.")
      }
    }
    else {
      msg ("You can't see a reason to fight the "+GetDisplayName(object)+".")
    }
  ]]></script>
</command>

jmnevil54
19 Oct 2017, 08:18

To be honest?
Print a message, put the boar in your inventory.

(HK's is good too.
But basically, just making the dead attribute itself is confusing...)


mrangel
19 Oct 2017, 09:53

I would have put in the code for killing the boar; but I wasn't sure if the boar needs hit points; if you might sometimes need to shoot it more than once, or if there should be a chance of missing.

In the simplest case (you give the command, it costs an arrow, and the boar dies), you would be replacing the // Code to kill 'object' here in my previous example with something like…

msg ("You killed the boar.")
foreach (loot, GetDirectChildren(object)) {
  msg ("You recover "+GetDisplayName(loot)+" from its body.")
  AddToInventory (loot)
}
// This destroys the clone. If you've given it a respawn script,
//    you'd want to   RemoveObject(object)   instead
destroy (object.name)

I noticed that the Boar object in your example contains raw meat and animal skin. I assume that the player should gain those when killing it. If you want to leave them on the ground for the player to pick up, replace the AddToInventory with a MoveObjectHere.

If you want to leave a dead boar on the ground so that the player needs more tools to skin it, you could use a 'dead' attribute like HK suggested. But then you'd also have to check that the boar isn't already dead, and change all its descriptions. I'd say that's overkill when you could just do:

CloneObjectAndMove (Boar carcass, object.parent)
destroy (object.name)
msg ("You killed the boar.")

In that case, you'd put the meat/skin in the carcass object. Maybe it's a container, or maybe something more complex that requires extra tools to skin/butcher.

Two ways to do it; you can use the one that best fits how you want the game to work.


jmnevil54
19 Oct 2017, 16:59

Or just...

msg ("You killed the boar.")
foreach (boar meat, GetDirectChildren(object)) {
  msg ("You recover "+GetDisplayName(boar meat)+" from its body.")
  CloneObjectAndMove (boar meat, object.parent)
}
foreach (boar hide, GetDirectChildren(object)) {
  msg ("You recover "+GetDisplayName(boar hide)" from its body.")
  CloneObjectAndMove (boar hide, object.parent)
}
destroy (object.name)

mrangel
19 Oct 2017, 17:09

@jmnevil54

That will work, but doesn't work how you seem to think it does.

Your code is the same as the first example in my post, except:

  1. It uses the variable name boar meat to refer to both the meat and the skin.
  2. It has a second loop which does nothing (boar hide refers to no objects)
  3. It makes new clones of the skin and meat, and destroys the clones that you already had, instead of just moving them.

jmnevil54
19 Oct 2017, 17:28

THAT'S WHAT YOU WROTE!

Fine, change it....

Edit, NOTE: The boar hide refers to the skin. The meat refers to the meat. I just prefer saying those words.


Enpherdaen
19 Oct 2017, 21:31

I can't just add the loot into the inventory, the actual boar object needs to have it's "dead" attribute enabled - therefor making it's body accesable to the player who can skin it/takes it's meat with a knife. The only real difficult part is telling Quest it's the boar in the room with the player that needs to "die".


Enpherdaen
19 Oct 2017, 21:38

Wait, shoudln't player.parent.boar tell Quest that you are refering to the boar in the room with the player?


mrangel
19 Oct 2017, 21:51

Enpherdaen:
I showed two ways to find the right boar in this post; in the first one (now edited to fix an error) it's the thisboar variable, in the second it's object. You can replace the comment "// Code to kill 'thisboar'" with the piece of script that kills the boar (whether that's setting a flag, removing it, or replacing it with a clone of a "Boar corpse" object).

If you need to skin the corpse with tools, then I'd recommend the second code fragment from this post; making a "dead boar" or "boar carcass" object. That way, the script for the killing doesn't need an extra step to check that it's dead, and the code for skinning doesn't need to check that it's dead, because the commands work on different objects. It also means that you can have separate descriptions for the live and dead boar, without your scripts needing to change it explicitly.


jmnevil54
19 Oct 2017, 22:08

This is my dead script for my monsters in my game.

obj.changedhitpoints => {
  if (this.hitpoints < 1) {
    this.dead = true
    msg ("It is dead!")
    player.exp = player.exp + 20
    player.gold = player.gold + 20
    love
    this.displayverbs = Split("Look at;Search", ";")
    this.parent = player.parent
  }
}

Here's the whole function!

Tele 1
MoveObject (player, Battle Room)
if (HasInt(game, "crittercount")) {
  game.crittercount = game.crittercount + 1
}
else {
  game.crittercount = 1
}
create ("critter" + game.crittercount)
obj = GetObject("critter" + game.crittercount)
obj.displayverbs = Split("Look at;Attack;Shoot", ";")
obj.parent = player.parent
obj.dead = false
obj.changedhitpoints => {
  if (this.hitpoints < 1) {
    this.dead = true
    msg ("It is dead!")
    player.exp = player.exp + 20
    player.gold = player.gold + 20
    love
    this.displayverbs = Split("Look at;Search", ";")
    Tele 2
    this.parent = player.parent
  }
}
names = Split("Decrepit;Decomposing;Shambling;Disgusting;Filthy;Falling-apart", ";")
obj.alias = StringListItem(names, game.crittercount % ListCount(names)) + " Stallin"
obj.listalias = CapFirst(obj.alias)
obj.look = ProcessText("A " + obj.alias + ", {random:covered in maggots:missing an arm:one eye hanging out}.")
obj.hitpoints = 30
obj.damage = 2
obj.attack = 1
obj.defence = 0
obj.armour = 0
obj.selectweapon => {
  if (RandomChance(10)) {
    if (this.weapon = vomitreadying) {
      this.weapon = Spade2
    }
  }
  else {
    this.weapon = GetObject(PickOneString("vomitreadying;talonattack;kickattack;Spade2"))
  }
}
obj.searchscript => {
  if (RandomChance(1)) {
    player.potion = player.potion + 1
    msg ("You found 1 Potion!")
  }
  else if (RandomChance(2)) {
    if (game.hard = true) {
      player.ammo = player.ammo + 7
      msg ("You found ammo! Ammo + 7!")
    }
  }
  else if (RandomChance(1)) {
    player.hyper_potion = player.hyper_potion + 1
    msg ("You found 1 Hyper Potion!")
  }
}
obj.critdesc = "A well-placed blow by #attacker# sends you reeling (#hits# hits)."
obj.attackdesc = "#Attacker# has hit you (#hits# hits)."
obj.missdesc = "#Attacker# misses you."

Of course, your's won't exactly look like this, but you get the point.

Edit: Oh, I remember! The Pixie made it so that you couldn't attack something if it was dead! That's where the other part is!


mrangel
19 Oct 2017, 22:13

(sorry I'm posting so much. Trying to be helpful, but not sure how to put it so it's easy to understand)

@jmnevil54

Edit, NOTE: The boar hide refers to the skin. The meat refers to the meat. I just prefer saying those words.

Not in the code you wrote. In your code, first the variable boar meat refers to a clone of Animal Skin; then boar meat refers to a clone of Raw Boar Meat. Your boar hide variable is never used. That's why I commented - those variable names are confusing, and if you needed to edit that code for anything in future, you might end up introducing errors if you assume they are what they say.


jmnevil54
19 Oct 2017, 22:17

For each boar meat, add bore meat to inventory
For each boar hide, add bore hide to inventory

Did you see my function before I edited it? I changed it, you know...

"and if you needed to edit that code for anything in future, you might end up introducing errors if you assume they are what they say."
Mrangel
And what's your solution to that, then?


Enpherdaen
19 Oct 2017, 22:24

Well I'll see what I can do.


jmnevil54
19 Oct 2017, 22:27

I'll make you an example kill command/function!

Just give me a few minutes...
Edit: Ah-hah!

if (not HasBoolean(object, "dead")) {
  msg ("That's not something you can attack.")
}
else if (object.dead) {
  msg ("That one is already dead.")
}
else {
  if (player.arrows >= 5) {
    AddToInventory (Big Rock)
  }
  else if (player.arrows = 4 or player.arrows = 3) {
    roll = GetRandomInt(1,20)
    if (roll > 10) {
      AddToInventory (Big Rock)
    }
  }
  else if (player.arrows = 1 or player.arrows = 2) {
    roll = GetRandomInt(1,20)
    if (roll > 15) {
      AddToInventory (Big Rock)
    }
  }
  else {
    msg ("You don't have any arrows! Go craft some!")
  }
}

Edit:
Changed things to "or", like HK said.

Change the 5 to 10, and switched the "15" and "10".


hegemonkhan
20 Oct 2017, 01:02

@ jnmevil54:

your 'and' logic operators need to be changed to 'or' logic operators

and your 1 arrow left attack will (almost) always succeed:

roll = GetRandomInt(1,20)
if (roll > 1) {

if roll = 1 ----> 1/20 (5%) succeeds
else (if roll = 2 to 20) ----> 19/20 (95%) succeeds

which, I don't think you wanted...

roll = GetRandomInt(1,20)
if (roll > 0) // 100% chance
if (roll > 1) // 95% chance
if (roll > 10) // 50% chance
if (roll > 5) // 75% chance
if (roll > 15) // 25% chance
if (roll > 20) // 0% chance

sample fix below:

if (not HasBoolean(object, "dead")) {
  msg ("That's not something you can attack.")
}
else if (object.dead) {
  msg ("That one is already dead.")
}
else {
  if (player.arrows >= 5) {
    AddToInventory (Big Rock)
  }
  else if (player.arrows = 4 or player.arrows = 3) {
    roll = GetRandomInt(1,20)
    if (roll > 5) {
      AddToInventory (Big Rock)
    }
  }
  else if (player.arrows = 1 or player.arrows = 2) {
    roll = GetRandomInt(1,20)
    if (roll > 15) {
      AddToInventory (Big Rock)
    }
  }
  else {
    msg ("You don't have any arrows! Go craft some!")
  }
}

alternatively you can also do this:

if (not HasBoolean(object, "dead")) {
  msg ("That's not something you can attack.")
}
else if (object.dead) {
  msg ("That one is already dead.")
}
else {
  if (player.arrows > 4) {
    AddToInventory (Big Rock)
  }
  else if (player.arrows > 2) {
    roll = GetRandomInt(1,20)
    if (roll > 5) {
      AddToInventory (Big Rock)
    }
  }
  else if (player.arrows > 0) {
    roll = GetRandomInt(1,20)
    if (roll > 15) {
      AddToInventory (Big Rock)
    }
  }
  else {
    msg ("You don't have any arrows! Go craft some!")
  }
}

jmnevil54
20 Oct 2017, 01:46

Yeah, HK's way works too (multiple > signs), I just didn't want to because I was afraid someone would correct me! ^_^'


hegemonkhan
20 Oct 2017, 05:32

(filler for getting my edited post, updated/posted)


you can do this too:

if (not HasBoolean(object, "dead")) {
  msg ("That's not something you can attack.")
}
else if (object.dead) {
  msg ("That one is already dead.")
}
else {
  if (player.arrows >= 5) {
    AddToInventory (Big Rock)
  }
  else if (player.arrows >= 3) {
    roll = GetRandomInt(1,20)
    if (roll > 5) {
      AddToInventory (Big Rock)
    }
  }
  else if (player.arrows >= 1) {
    roll = GetRandomInt(1,20)
    if (roll > 15) {
      AddToInventory (Big Rock)
    }
  }
  else {
    msg ("You don't have any arrows! Go craft some!")
  }
}

maybe this can help with the logic part involved of using ranges (greater/lesser thans) out for people:

guess my number I'm thinking of, it's between 0 and 10

is it 5 or greater?
No.

(thus, the number has to be less than 5)

is it 3 or greater?
yes.

(thus, the number is 3 or 4, as remember it can't be 5 or greater)

is it 3?
no.

is it 4?
yes!

this is the exact same logic as:

if (test.score >= 90) {
  test.grade = "A"
} else if (test.score >= 80) {
  test.grade = "B"
} else if (test.score >= 70) {
  test.grade = "C"
} else if (test.score >= 60) {
  test.grade = "D"
} else {
  test.grade = "F"
}

as is this logic:

if (player.arrows >= 5) {
    AddToInventory (Big Rock)
  }
  else if (player.arrows >= 3) {
    roll = GetRandomInt(1,20)
    if (roll > 5) {
      AddToInventory (Big Rock)
    }
  }
  else if (player.arrows >= 1) {
    roll = GetRandomInt(1,20)
    if (roll > 15) {
      AddToInventory (Big Rock)
    }
  }
  else {
    msg ("You don't have any arrows! Go craft some!")
  }
}

we can also do from the min to max instead as well:

guess the number that I'm thinking of, it is from 0 to 10

is it less than 5?
no

(thus the number is 5 or greater)

is the number less than 7
no

(thus the number is 7 or greater)

is the number less than 9
no

(thus the number is 9 or 10)

is the number 10?
no

is the number 9?
yes

which is the exact same logic as this:

if (test.score < 60) {
  test.grade = "F"
} else if (test.score < 70) {
  test.grade = "D"
} else if (test.score < 80) {
  test.grade = "C"
} else if (test.score < 90) {
  test.grade = "B"
} else {
  test.grade = "A"
}

which is the same logic as this:

if (player.arrows < 1) {
  msg ("You don't have any arrows! Go craft some!")
  AddToInventory (Big Rock)
}
else if (player.arrows < 3) {
  roll = GetRandomInt(1,20)
  if (roll > 15) {
    AddToInventory (Big Rock)
  }
}
else if (player.arrows < 5) {
  roll = GetRandomInt(1,20)
  if (roll > 5) {
    AddToInventory (Big Rock)
  }
}
else {
  AddToInventory (Big Rock)
}

when you got a chain of conditions (if/else or if/else-ifs or if/else-ifs/else, blocks):

the next condition is based upon the previous condition

the most simple if the 'if/else' block (2 states):

my number is either 1 or 2

is it 2?
no

(thus, the number has to be 1 as it's not 2)

is it 1?
yes!

dead/alive:

if (orc.dead) {
msg ("the orc is dead")
} else {
msg ("the orc is alive")
}

if (orc.alive) {
msg ("the orc is alive")
} else {
msg ("the orc is dead")
}


I don't need to do this:

test scores: 0 to 100

if (test.score >= 90 and test.score <= 100) {

beacause we're assuming that the number can't be more than 100 based upon the initial statement/understanding (condition '0' if you will, lol)

all we need to do is this:

if (test.score >= 90) {

we do NOT need to do this:

else if (test.score >= 80 and test.score < 90) {

because the previous condition already forces the number to be less than 90 (as it wasn't 90 or greater, as it didn't meet that previous condition): the previous condition acts as our upper limit (test.score < 90)...

... thus, we only need to do this:

else if (test.score>= 80) {

...and so forth...


now, if we don't have a chain of conditions, such as having separate independent conditions (multiple 'if' if blocks / multiple 'ifs'), then we must do this:

if (test.score >= 90 and test.score <= 100) {
}
if (test.score >= 80 and test.score < 90) {
}
if (test.score >= 70 and test.score < 80) {
}
if (test.score >= 60 and test.score < 70) {
}
if (test.score >= 0 and test.score < 60) {
}

see the difference?

// test score: 0 to 100 // (condition 0)

if (test.score >= 90) {
  test.grade = "A"
} else if (test.score >= 80) {
  test.grade = "B"
} else if (test.score >= 70) {
  test.grade = "C"
} else if (test.score >= 60) {
  test.grade = "D"
} else {
  test.grade = "F"
}

// -----------VS ----------------------

// (NO condition 0)

if (test.score > 100) {
  test.score = 100
} else if (test.score < 0) {
  test.score = 0
}

if (test.score >= 90 and test.score <= 100) {
  test.grade = "A"
} else if (test.score >= 80) {
  test.grade = "B"
} else if (test.score >= 70) {
  test.grade = "C"
} else if (test.score >= 60) {
  test.grade = "D"
} else if (test.score >= 0) {
  test.grade = "F"
}

// -----------VS ----------------------

// (NO condition 0)

if (test.score > 100) {
  test.score = 100
} else if (test.score < 0) {
  test.score = 0
}

if (test.score >= 90 and test.score <= 100) {
  test.grade = "A"
}
if (test.score >= 80 and test.score < 90) {
  test.grade = "B"
}
if (test.score >= 70 and test.score < 80) {
  test.grade = "C"
}
if (test.score >= 60 and test.score < 70) {
  test.grade = "D"
}
if (test.score >= 0 and test.score < 60) {
  test.grade = "F"
}

// -----------VS ----------------------

// test score: 0 to 100 // (condition 0)

if (test.score >= 90) {
  test.grade = "A"
}
if (test.score >= 80 and test.score < 90) {
  test.grade = "B"
}
if (test.score >= 70 and test.score < 80) {
  test.grade = "C"
}
if (test.score >= 60 and test.score < 70) {
  test.grade = "D"
}
if (test.score < 60) {
  test.grade = "F"
}

jmnevil54
20 Oct 2017, 16:40

A question. If I were to make my own monster/clone respawning system, and I was using a turnsrcipt, and put "set this.spawn = true" for every room, would I then put that script in the turn script, or in each room?


Enpherdaen
20 Oct 2017, 16:51

Mrangel, when trying to use your preferred method: "Sorry, an eternal error occurred". Yes, I have added "this.original_object = this" to the initialisation script.


jmnevil54
20 Oct 2017, 17:26

Do it by "add script" instead of code view, if you haven't figured that out yet.

An internal error is an error in the system, not the code.


Enpherdaen
20 Oct 2017, 19:07

It's the same thing Jmnevil54


K.V.
20 Oct 2017, 19:27

When does it throw the error? On loading the game? Or are you editing online and getting the error when adding the script?


Here's a working example I just threw together using mrangel's code.

It prints messages during functions so you can see when things are happening behind the scenes.

Click here for example game code
<!--Saved by Quest 5.7.6404.15496-->
<asl version="550">
  <include ref="English.aslx" />
  <include ref="Core.aslx" />
  <game name="Cloning Things">
    <gameid>92928bb5-fa01-4351-a0fd-c7b566c47fbb</gameid>
    <version>1.1</version>
    <firstpublished>2017</firstpublished>
    <start type="script"><![CDATA[
      msg ("<b><hr>Start script begins<hr></b>")
      foreach (object, AllObjects()) {
        object.original_object = object
        msg (object + ": original_object = " + object.original_object)
      }
      msg ("<b><hr/>Start Script ends<hr/></b>")
    ]]></start>
  </game>
  <object name="room">
    <inherit name="editor_room" />
    <enter type="script">
    </enter>
    <beforeenter type="script">
      doCloning
    </beforeenter>
    <object name="player">
      <inherit name="editor_object" />
      <inherit name="editor_player" />
      <object name="Bow">
        <inherit name="editor_object" />
        <arrows type="int">12</arrows>
        <take />
      </object>
    </object>
  </object>
  <object name="Aloe Vera">
    <inherit name="editor_object" />
    <feature_startscript />
    <take />
    <attr name="_initialise_" type="script"><![CDATA[
      this.original_object = this
      msg ("<b>Object Initialisation script:</b> " + this + ": original_object = " + this.original_object)
    ]]></attr>
  </object>
  <object name="Stick">
    <inherit name="editor_object" />
    <feature_startscript />
    <take />
    <attr name="_initialise_" type="script"><![CDATA[
      this.original_object = this
      msg ("<b>Object Initialisation script:</b> " + this + ": original_object = " + this.original_object)
    ]]></attr>
  </object>
  <object name="Boar">
    <inherit name="editor_object" />
    <feature_startscript />
    <attr name="_initialise_" type="script"><![CDATA[
      this.original_object = this
      msg ("<b>Object Initialisation script:</b> " + this + ": original_object = " + this.original_object)
    ]]></attr>
    <take />
    <takemsg>The boar must be incapacitated first.</takemsg>
    <displayverbs type="stringlist">
      <value>Look at</value>
      <value>Take</value>
      <value>Attack</value>
    </displayverbs>
  </object>
  <command name="Kill">
    <pattern>kill #object#;attack #object#;hunt #object#;chase #object#</pattern>
    <script><![CDATA[
      msg ("<b>Is object a boar?  Checking...")
      if (Equal(object.original_object, Boar)) {
        msg ("<b>Object is boar.<br><br>Does player have bow? Checking...")
        if (Got(Bow)) {
          msg ("<b>Player has bow.<br><br>Does player have arrows? Checking...")
          if (Bow.arrows > 0) {
            msg ("<b>Player has {=Bow.arrows} arrows.")
            Bow.arrows = Bow.arrows - 1
            msg ("You kill the boar.")
            destroy (object.name)
            msg ("<b>" + object + " has been removed from the source code.")
            CloneObjectAndMoveHere (Dead Boar)
            msg ("<b>Dead Boar has been cloned and moved here.")
          }
          else {
            msg ("You don't have any arrows.")
          }
        }
        else {
          msg ("You don't have a bow.")
        }
      }
      else {
        msg ("You can't see a reason to fight the "+GetDisplayName(object)+".")
      }
    ]]></script>
  </command>
  <object name="Dead Boar">
    <inherit name="editor_object" />
    <inherit name="surface" />
    <feature_container />
    <hidechildren />
    <listchildren />
    <feature_startscript />
    <addscript type="script">
      msg ("You decide not to stack things on the dead boar.")
    </addscript>
    <attr name="_initialise_" type="script"><![CDATA[
      this.original_object = this
      msg ("<b>Object Initialisation script:</b> " + this + ": original_object = " + this.original_object)
    ]]></attr>
    <object name="Boar Meat">
      <inherit name="editor_object" />
      <take />
      <alias>Boar Meat</alias>
    </object>
    <object name="Boar Hide">
      <inherit name="editor_object" />
      <take />
      <alias>Boar Hide</alias>
    </object>
  </object>
  <function name="doCloning">
    CloneObjectAndMoveHere (Aloe Vera)
    CloneObjectAndMoveHere (Stick)
    CloneObjectAndMoveHere (Boar)
  </function>
</asl>

http://textadventures.co.uk/games/view/e4se5ez6juoiwamhxumhgq/cloning-things


jmnevil54
20 Oct 2017, 19:49

@Enpherdaen Yes, but no.
The system simply has a hard time processing scripts!

Add script by hand!


jmnevil54
20 Oct 2017, 19:53

You see, when you paste into code view, the machine then has to translate a very large script all at once, which it cannot handle! It just quits!

The difference between that and the other method, is simply because it's slower!!!
Technically, you could do it slowly by code view too, but hitting "ok" all the time is tiring...
And it's hard to type a "Show Menu" script from scratch.....


K.V.
20 Oct 2017, 19:57

jmne speaks the truth. Sometimes you must enter the code piece by piece via GUI when using the web editor. It won't accept certain things in Code View.

Sorry, an eternal error occurred

Hehehe. I'm really, really not making fun of you. I'm having fun with you.

This is the best error (or typo) I've seen in a while!


mrangel
20 Oct 2017, 20:05

I have noticed that sometimes I get "an internal error has occurred" when I go back from code view to the normal view. Sometimes just pasting in the same code again will work fine. Sometimes, it seems that it doesn't like certain things (like strings with < in) and I have to remove those. I've not seen it do that for an actual parse error, but I think it might.

In this case, I think there is an error in my code. An extra } at the end. It's harder to tell on my phone, because I can only see a couple of lines.


hegemonkhan
21 Oct 2017, 02:35

@ Jnmevil54:

"A question. If I were to make my own monster/clone respawning system, and I was using a turnsrcipt, and put "set this.spawn = true" for every room, would I then put that script in the turn script, or in each room? (Jmnevil54)"


not sure on your design, could I see your monster/clone respawning if you got it done already and any other coding, your design, involved for doing what you want to do? as this will help me better with trying to help you, as from my side, I don't have much knowledge of specific coding/design you're using/planning-on-using, which I need to be of more direct and more helpful help ??

working in the dark on your design,

you could do this:

<turnscript name="global_turnscript">
  // blah other scripting
  foreach (object_variable, AllObjects ()) {
    if (GetBoolean (object_variable, "spawn")) {
      // your spawning/cloning code (using 'object_variable', or whatever you want to name the Variable as, change it to what you want in the above lines of mine as well, of course)
    }
  }
  // blah other scripting
</turnscript>

jmnevil54
21 Oct 2017, 03:28

I have no script yet, but I was going to make one.


jmnevil54
22 Oct 2017, 14:27

So what's happening here? Is everyone okay?


Enpherdaen
22 Oct 2017, 15:40

I think it's actually because Mrangel's scripts includes properties I do not have in my game.


mrangel
22 Oct 2017, 16:15

I can't figure out what the problem is. Does it give any kind of detailed error?


Enpherdaen
22 Oct 2017, 16:42

Nope. It says it right after I click "done" after pasting the code in.


jmnevil54
22 Oct 2017, 17:02

Alright. So your options are use the start script only, or, just change the attribute/put the attribute in the initialisation script.

this.original_object = this

But what I'd do is...
this.oo = true

Mrangel, you can't set an attribute equal to itself, you just can't!
Edit: I edited my post.

So are we done now? I don't know. I just don't know where everyone goes during the day time! Maybe it's because I'm American!?


mrangel
22 Oct 2017, 19:56

Nope. It says it right after I click "done" after pasting the code in.

I've had that before. I think it's a bug in the editor. If you try entering the outermost 'if' statement, and then paste the code into the 2 halves separately, either it'll work or you narrow down which line it has a problem with.

In my experience, if most usually errors over blocks with both a < and a > in; or if statements with multiple dots in the expression; or comments between a } and the corresponding else. Could be other things too, though. I'll try it and see if I can figure it out.


mrangel
22 Oct 2017, 21:46

Another look at the code, I spotted a few more potential issues (I copied the line from your original version, checking if the player has the Bow object. But as your "craft bow" command gives the player a clone, you'd need to check for a clone of the bow as well)

Was going to post an updated copy of the code, and maybe catch any typos I might have missed at the same time; but just had a message saying you don't want my help. So; just shout if you want me to join in again.


jmnevil54
22 Oct 2017, 21:53

@mrangel What. You do realize I messaged you... Did we both message you???


Enpherdaen
23 Oct 2017, 15:10

Mrangel I don't know what you are talking about, I never sent you a message like that.


jmnevil54
23 Oct 2017, 17:09

Enpherdaen, can you post your turn script code, as well as the 'after entering room' or whatever you use to call it? I would like to use it to spawn monsters. Because I did not understand what anybody has been saying the last 2 threads.


Enpherdaen
23 Oct 2017, 22:43

Turn script code for what?


jmnevil54
23 Oct 2017, 22:54

Your turn script. I have a game with combat. I'm wanting to make a turn script for it. I learn best by example.


Enpherdaen
24 Oct 2017, 18:09

I don't have a turn script for combat yet.


jmnevil54
24 Oct 2017, 18:59

*smacks head

No. Spawning monsters. Your script spawns objects after the player takes them. I want a script that spawns monsters after the player kills them.

At the moment, I just know how to either spawn one monster only (first time), a non-stop supply of monsters, and something like spawning monsters twice, possible (with the use of a game.spawn attribute).


mrangel
24 Oct 2017, 19:56

(edited for silly mistake)
Did you get the kill script to input properly?

I'll try writing the script out from scratch, in case it's still giving a problem. If the editor was having a problem with a typo or something, typing it again will presumably remove the typo. (And possibly changing the order of the checks will make it easier to understand).

Two versions.
version 1: Sets a "dead" attribute (which your skin/etc commands will have to check for)

bows = FilterByAttribute (ScopeInventory(), "original_object", Bow)
boars = FilterByAttribute (ScopeReachableNotHeld(), "original_object", Boar)
liveboars = FilterByNotAttribute (boars, "dead", true)
if (ListCount(boars) = 0) {
  msg ("There's no boar here.")
}
else if (ListCount(liveboars) = 0) {
  msg ("The boar is already dead.")
}
else if (ListCount(bows) = 0) {
  msg ("You need a bow to do that.")
}
else if (Bow.arrows = 0) {
  msg ("You need an arrow to do that.")
}
else {
  thisboar = PickOneObject (liveboars)
  msg ("You shoot and kill the boar.")
  thisboar.dead = true

  // You probably want to add code here to change thisboar's description, and maybe change
  //   its alias to "boar carcass" or something, so the player can see if it's alive or dead.
}

version 2: For this one, you'd have a "dead boar" or "boar carcass" object inside the boar, and the meat/skin inside the dead boar. This makes the code a little simpler.

bows = FilterByAttribute (ScopeInventory(), "original_object", Bow)
boars = FilterByAttribute (ScopeReachableNotHeld(), "original_object", Boar)
if (ListCount(boars) = 0) {
  msg ("There's no boar here.")
}
else if (ListCount(bows) = 0) {
  msg ("You need a bow to do that.")
}
else if (Bow.arrows = 0) {
  msg ("You need an arrow to do that.")
}
else {
  thisboar = PickOneObject (boars)
  msg ("You shoot and kill the boar.")
  foreach (carcass, GetDirectChildren(thisboar)) {
    carcass.parent = thisboar.parent
  }
  RemoveObject (thisboar)
}

(Actually, I just thought of a slightly simpler way to do it in some circumstances. You could make the command's pattern a regex, ^(kill|hunt|attack|shoot) (?<object>boar) … this means that your command script will have a variable object, which is in the current room and is an object with "boar" in its name. In this case that's not the best method, because not all objects with "boar" in their name are boars)


K.V.
24 Oct 2017, 20:32

(?<object>boar)

I just learned something new!

Whoo-hoo!!!


Enpherdaen
22 Nov 2017, 17:35

Mr. Angel, can you please explain to be how Quest knows which boar clone to set as "dead"? I have to understand how this bit of coding works if I am to continue working with those pesky clones!


jmnevil54
22 Nov 2017, 19:51

@Enpherdaen Whichever boar has their dead attribute set to true, is the dead one.


mrangel
22 Nov 2017, 22:27

@Enpherdaen
In the last samples I wrote, this line:

  • boars = FilterByAttribute (ScopeReachableNotHeld(), "original_object", Boar)

Takes the list of all objects the player can reach but isn't holding (ScopeReachableNotHeld()) and removes (filters out) all the ones whose original_object isn't a Boar. So you're left with just a list of Boar clones. In the code I've seen you post, there's only ever one boar in a room; so this is a list of either zero or one object.

If there's no boars in the room, then the variable boars is a blank list. So (ListCount(boars) = 0) is true, and it tells the player there's no boar to attack.

If there's one boar present, then thisboar = PickOneObject (boars) picks that boar from the list, and sets the variable thisboar to it. The next few lines handle the actual killing of thisboar. I could just have said thisboar = boars[0]; but I thought PickOneObject was more understandable.

If there's more than one boar in the same room, then PickOneObject will pick one at random. If this is possible, then you might need to think about how you want Quest to make the choice. As your boars are all identical so far, I figured that there's no reason to make the code more complex.


Enpherdaen
23 Nov 2017, 05:55

Oh I see now! Somewhat, at least. Awesome!


Enpherdaen
23 Nov 2017, 15:25

What is "FilterByAttribute" for?

Edit: Never mind, I get it now. Is "original_object" an attribute I'm supposed to manually add?


mrangel
23 Nov 2017, 17:46

I think I mentioned earlier; I use original_object to indicate the original object that something is a clone of. It's very useful, and I end up using it in just about every script that deals with clones.

I think I previously suggested 2 different ways to set that:

  1. In the initialisation scripts of all the objects you're likely to clone:
    this.original_object = this

OR

  1. In the game's start script: (so it does all the objects at once)
foreach (obj, AllObjects()) {
  obj.original_object = obj
}

or you could set the variable when you clone it; but that seems to be more work.

(Pixie's example code on handling clones uses an attribute called prototype in the same way, I believe)


Enpherdaen
23 Nov 2017, 23:21

So what is the purpose of letting Quest know the original object?

By the way pasting your script doesn't work.


mrangel
24 Nov 2017, 11:20

The purpose of the original_object attribute is so that you can use FilterByAttribute to search the inventory or a room for clones of a particular object. As in this case, where we're looking for boars in the room and bows in the inventory.

Which script are you trying? Does it give an error message, or just nothing?


Enpherdaen
24 Nov 2017, 15:51

So what does "this.original_object = this" do? It just seems to say "this object (which has an attribute called original_object) is this object". How does it function?

Why do clones need this attribute? Can't it just search for any objects named "boar" in reach of the player and apply the code to that?


mrangel
24 Nov 2017, 17:10

The original boar has an "original_object" attribute which points to itself. When you clone it, it doesn't change the attribute; so "original_object" still points to the first boar.

Can't it just search for any objects named "boar" in reach of the player and apply the code to that?

The clones aren't named "Boar". They're named "Boar2" or "Boar17" or something like that; because all objects need to have unique names.

You could do something like FilterByAttribute(ScopeReachableNotHeld(), "alias", "Boar") instead, as long as the clones all have the same alias. This works, but may cause problems in some more complex situations, and makes it harder to find typos.


Enpherdaen
24 Nov 2017, 23:03

I was referring to the alias. So why does Quest need to know which boar is the original object? Is the "FilterByAttribute" automatically installed in the game?


Enpherdaen
25 Nov 2017, 01:29

I also need the full 'kill boar which is in sight' function. I need it to replace the "boar" object with the "boar carcass" object (which will contain the contents of the boar).


mrangel
25 Nov 2017, 12:00

I was referring to the alias. So why does Quest need to know which boar is the original object?

You can use the alias if you prefer.

Looking at an object's alias to determine what type of object it is works fine in this case.

Having an attribute that identifies what kind of object a clone is will work well in all cases once it's set up, so as a programmer it's a good habit to get into.

Is the "FilterByAttribute" automatically installed in the game?

Yes, it's one of the core functions.

I also need the full 'kill boar which is in sight' function. I need it to replace the "boar" object with the "boar carcass" object (which will contain the contents of the boar).

I posted one that should work in this post. The 'version 2' is the one with a boar carcass object.

If you want to use the alias instead of original_object, change FilterByAttribute (ScopeReachableNotHeld(), "original_object", Boar) to FilterByAttribute (ScopeReachableNotHeld(), "alias", "Boar").

I assumed there that the boar carcass is placed inside the boar. So the object structure in your game looks like

  • OBJECTS
    • Boar
      • Boar carcass
        • Animal Skin
        • Raw Boar Meat

If the carcass isn't placed in the boar ready to be dropped, you'll want to replace

  foreach (carcass, GetDirectChildren(thisboar)) {
    carcass.parent = thisboar.parent
  }

with

  CloneObjectAndMove (Boar Carcass, thisboar.parent)

Hope that makes sense; I'm still half asleep this morning.


Enpherdaen
25 Nov 2017, 15:27

Well thinking about it, I just now fully understood how "this.original_object" works. Which means using the Alias is no longer necessary.

So because "boar" refers to all clones, refering to the object in sight with the "original_object" attribute will tell Quest which boar you are referring to?


hegemonkhan
25 Nov 2017, 16:55

a specific clone Object (I think) only can be identified (and thus stored, so it can be used later) upon its creation/cloning scripting, for simple example:

game.this_is_my_stored_single_specific_clone_object = CloneObject (Boar)

// or:

game.this_is_my_stored_multiple_specific_clone_objects = NewObjectList ()
object_variable= CloneObject (Boar)
list add (game.this_is_my_stored_multiple_specific_clone_objects, object_variable)

as now you can get this (or these: multiple specific clones: using the Objectlist Attribute) specific clone(s) whenver you need to do something with it (them)


You can use an Attribute to store the name (or a pointer/reference to the original object) of the clone group/type (ie: all clones of the original 'Boar' Object) or (alternatively) using the 'StartsWith' Script/Function (and the 'and not LengthOf (CLONE_OBJECT) = ORIGINAL_OBJECT.name' for EX-cluding the original object, storing/getting only the clones), as well to do the same thing


Enpherdaen
25 Nov 2017, 17:05

I didn't understand any of that lol.

So this.original_object tells Quest which boar is NOT a clone? If that is so then why do we need that?


jmnevil54
25 Nov 2017, 17:34

No, Quest knows which Boar is which because it already knows their names. Boar1, Boar2, Boar3.... It knows them all.


Enpherdaen
25 Nov 2017, 21:42

This is what I currently have in the "kill boar" command:

if (FilterByAttribute (ScopeReachableNotHeld(), "original_object", Boar)) {
  if (FilterByAttribute (ScopeInventory(), "original_object", Bow)) {
    if (Bow.arrows > 0) {
      Bow.arrows = Bow.arrows - 1
      if (RandomChance(Kill.chance)) {
      }
    }
    else {
      msg ("You don't have any arrows.")
    }
  }
  else {
    msg ("You require a bow to do that.")
  }
}

Would this work after I add the actual "kill" script after the random chance? Also tell me if there is anything that needs to be changed.


Enpherdaen
01 Dec 2017, 05:51

HEY


mrangel
01 Dec 2017, 13:06

I've not checked if Quest's if will deal with lists like that. You might need to put a ListCount() around the filter, and compare its result to zero.

(Sorry, I'm not so active at the moment after my laptop hinge snapped. Waiting for it to come back from repair, so not able to test things at present)


hegemonkhan
01 Dec 2017, 21:52

just posting the code of what 'mrangel' said to try/do:

(I'm pretty sure you DO need the 'ListCount (xxx)', as the 'Filter (xxx)' just returns a List, whereas the 'if' needs a condition, such as if the List is empty/zero-items or having-items-and/or-how-many-items)

if (not ListCount (FilterByAttribute (ScopeReachableNotHeld(), "original_object", Boar)) = 0) {
  if (not ListCount (FilterByAttribute (ScopeInventory(), "original_object", Bow)) = 0) {
    if (Bow.arrows > 0) {
      Bow.arrows = Bow.arrows - 1
      if (RandomChance(Kill.chance)) {
      }
    }
    else {
      msg ("You don't have any arrows.")
    }
  }
  else {
    msg ("You require a bow to do that.")
  }
} else {
  msg ("There's no sign of any boars to hunt")
}