Passing time without clock

Hello. Hoping to get some insight from people either smarter or more experienced than me. I've been using timers for my game for a little while but have gotten some negative feedback like the timer expiring while people are reading the room's description. Because of that, I wanted a system that was more controlled by the player.

Ideally, I'd like certain actions (like entering/leaving a room) would increase a value, and when that value was at a certain threshold, things would happen like: NPCs/objects would move, the game would check if the player was in in a certain room, etc. What's the best way to do this?

Just to be clear, I don't want an actual clock system, because I wouldn't want the player to move back and forth and be on like day 5 at 3 am. Plus I think it would be more easy for me to check if, for example, if the value is 200 to move an NPC than checking if it's 21:10.


Check out the game object in your editor's Object tree, it's the first thing. It has a Scripts tab. In there you can have a specific script fire anytime any room is entered. Each individual Room object has a Scripts tab also. You can choose what happens during various entry and exit events. You can also use Turn Scripts, which happen by default when a successful command is entered by the player. That is considered a Turn. You can activate turn scripts in the game objects Scripts tab to run every Turn, or you can use the room object's Scripts tab to run turn scripts just while the player is in a particular room.

Whatever method you put your script into, it just has to add time to a variable/attribute that keeps track of your time.


In practice, whether or not you have a clock probably doesn't matter much.

The easiest way to have a time-related event that doesn't depend on real time is to have an attribute (something like game.timepassed) which you increase when time passes.

As TheOtherBen said, there are a bunch of different places you could include a script that changes that attribute. The roomenter script would be good if moving to a new room takes time; or a turnscript if you want all commands to take time. If specific actions (like using a particular item) take time, you would increase the attribute in the scripts for those events.

One thing I would suggest (though a little harder to implement) could be giving a command an attribute for how much time it takes. Then you could add something like:

        if (HasInt (game.pov.currentcommandpattern, "duration")) {
          game.timepassed= game.timepassed + game.pov.currentcommandpattern.duration
        }

in the core function ResolveNextName before the line:

        do (game.pov.currentcommandpattern, "script", game.pov.currentcommandresolvedelements)

Then you could give any command a duration attribute, and have the time automatcially changed whenever the command is used.


I suspect you would then have a bunch of turnscripts for events that happen at a certain time. They would have an attribute for what time they need to happen, and a script which checks if the time has passed, and runs the script if so. This would be pretty much the same as the TurnTimeout functions in the core - just using your timepassed attribute instead of a number of turns.

You could simplify setting it up by using a couple of functions like this:

<function name="RunScriptAtTime" parameters="time, script">
  turnscriptname = GetUniqueElementName("event")
  create turnscript (turnscriptname)
  turnscript = GetObject (turnscriptname)
  turnscript.trigger = time
  turnscript.event =  script
  turnscript.script => {
    if (game.timepassed >= this.trigger) {
      this.enabled = false
      do (this, "event")
    }
  }
  turnscript.enabled = true
</function>

<function name="RunScriptAfterTime" parameters="time, script">
  RunScriptAtTime (time + game.timepassed, script)
</function>

So then you could do something like:

RunScriptAtTime (300) {
  msg ("This message will display when game.timepassed reaches 300 for the first time")
}

RunScriptAfterTime (10) {
  msg ("This message will display 10 timer units after it is set up")
}

About the only difference if you wanted an actual clock would be that you'd add an extra turnscript (or changescript) that converts the timepassed attribute into a human-readable time. Computers store the time as a number internally anyway.


My House on the hill, has a "clock" where things happen after so many turns.
(Just about everything happens in the front room of the house.)
https://textadventures.co.uk/games/view/ytxcboq5uug1zlu4q6sbvw/the-house-on-the-hill
Nothing complex, just counting room entries, if I remember correctly.


Omg thank you mrangel!!! You are a blessing. I got it to work, I believe. Made a couple tests and it all seemed fine.

Just one question, if I have, say, multiple RunScriptAfterTime , where would the best place to put them be? I know if I put it at 'start of the game' script, it works, but will that make loading the game slower when I have a bunch of them loading?

Oh, also, one more thing. If I wanted an NPC to follow a certain repeated path (or schedule) how can I best do this?
For example, if their schedule was: Room A -> Room B -> Room C -> Room B -> Room A (and then it repeated over and over). And they waited, like, 4 turns/passedtime before moving to a new room. Would this be possible? And would it be possible to like stop this cycle eventually? Sorry if I described it poorly.

I know I could prob do it by making a runscriptafter time for each moment it moved (Aka: 4,8,12,16,20,24...) but that might be spaghetti code.


If you want to set up a load of events initially, you could create the turnscripts in the editor. Using a function like this makes it easier to read, and I don't think the time taken to do it would make much difference. It's just what looks neater to you.

For example, if their schedule was: Room A -> Room B -> Room C -> Room B -> Room A (and then it repeated over and over). And they waited, like, 4 turns/passedtime before moving to a new room. Would this be possible?

You could make a script which changes its own trigger time when it is run, so it goes off more than once. For example:

RunScriptAtTime (4) {
  this.enabled = true
  this.trigger = this.trigger + 4
  if (john.parent = room A) {
    john.parent = room B
  }
  else if (john.parent = room B) {
    john.parent = room C
  }
  else if (john.parent = room C) {
    john.parent = room D
  }
  else {
    john.parent = room A
  }
}

In this case, if more than 4 units of time pass at once, the NPC might move every turn until he catches up with his schedule;. There's a few different ways to do it based on how you want him to behave in this case.


Something a little more advanced that can help with commands that have a duration is to edit the CoreTypes.aslx entry for defaultcommand to something like this:

<library>
  <type name="defaultcommand">
    <pattern type="simplepattern"></pattern>
    <duration type="int">10</duration>
  </type>
</library>

You technically can just edit the CoreTypes but pasting it to a file like "MyTypes.aslx" and adding the file to the Included Libraries folder in the That way all commands will have that default duration and a place for you to change it if needed out of the box.


So everything has been working fine, however, I've had a little problem. You see my game has a groundhog day kind of thing where you basically awake again. RunScriptAfterTime: it seems that if you die and reset before game.timepassed reach the event threshold (for example if you died at 5 and the event was set at 10) then when it resets, the event happens at 5. (And then again at 10, bc when you die I set a call function again).

RunScriptAtTime: If you die before the first one procs, you get double text and errors.

Is there a way to fix this? I thought i'd be able to make it work by setting game.timepassed = 0 when you died. Maybe if there's a way to stop callfunction?


Resetting the timepassed to 0 should work. Everything would run at the time it was scheduled to.

However, there's 3 different things you might want to do with an event that's already scheduled when you reset:

  1. Make it run after the same time (so an event that's due to run 2 ticks in the future still runs 2 ticks after resetting) - to do this you'd subtract the previous timepassed from its trigger time.
  2. Event runs at the time it was originally scheduled for.
  3. Event is cancelled.

If you want it to reset completely, you would probably do something like:

events = NewStringList ()
foreach (event, AllTurnScripts ()) {
  if (HasInt (event, "trigger")) {
    list add (events, event.name)
  }
}
foreach (event, events) {
  destroy (event)
}
game.timepassed = 0

That deletes all events, whether they've run yet or not. Then you can run some function to recreate any that you want to be initially available.

Does that help?


Yes! I didn't know if I should make it a function or not, but making it worked. Sorry for all the trouble. Haven't tested the character schedule one (If it works, prob will not respond, but might if it doesn't) however, as far as I know, it is working 100% as I intended! I'm so glad and so sorry for all the work.


Log in to post a reply.

Support

Forums