Author Topic: SCI0: Actors and scripts  (Read 1248 times)

0 Members and 1 Guest are viewing this topic.

Offline robbo007

SCI0: Actors and scripts
« on: May 12, 2023, 09:18:40 AM »
Hi,
I'm trying to get my actor walking behind the bar to create a random moving effect. I've tried using the actors instance method init and it works but I need more than one setMotion: MoveTo (left to right) so I think I need to use a change state script. How do you trigger a changestate from a instance of an actor. Because my code does not trigger it or I've got the changestate syntax wrong? Do I need a (method (doit) somewhere possibly?

Code: [Select]
(instance aIsabella of Act
(properties
view 226
loop 1
x 191
y 76
cycleSpeed 5
)
(method (init)
(super init:)
(self
setPri: 7
setScript: aIsabellaScript
;;; cycleSpeed: 0
;;; loop: 1
setCycle: Walk
;;; setMotion: MoveTo 145 76

)
)
)
(instance aIsabellaScript of Script
(properties)

(method (changeState newState)
(= state newState)
(switch state
(0 ; Handle state changes
)
(1
; Walk Isabella to left of bar.
(aIsabella setMotion: MoveTo 145 76) (aIsabella view: 226 loop: 1 setCycle: End)
;repeat
(self changeState: 0)
)
)
)
)



Offline Kawa

Re: SCI0: Actors and scripts
« Reply #1 on: May 12, 2023, 09:41:48 AM »
For one, your state 0 has nothing in it so it just hangs there. For two, the other state doesn't wait for Isabella to arrive because you forgot to specify self as the one whoCares.

What I'd do here is have Isabella move to the right in state 0 and then left in 1.

Code: [Select]
(define ISABELLA_LEFT 145)
(define ISABELLA_RIGHT 191)
(define ISABELLA_Y 76)

(instance aIsabella of Act
(properties
view 226
x ISABELLA_LEFT
y ISABELLA_Y
)

(method (init)
(super init:)
(self
setPri: 7
setScript: aIsabellaScript
)
)
)

(instance aIsabellaScript of Script
(properties)

(method (changeState newState)
(= state newState)
(switch state
(0 ; Move to the right
(client setMotion: MoveTo ISABELLA_RIGHT ISABELLA_Y self)
)
(1 ; And to the left
(client setMotion: MoveTo ISABELLA_LEFT ISABELLA_Y self)
)
(2 ; Cha-cha real smooth
(self changeState: 0)
)
)
)
)

WARNING: The above code is untested.

Offline robbo007

Re: SCI0: Actors and scripts
« Reply #2 on: May 12, 2023, 12:22:43 PM »
ok cool thanks. A few questions. So why are we using "Client" here and not the defined instance name aIsabella ? What does "Client" do?

I've had to add to get Isabella to use the view animation. Otherwise she does a freaky ghostly float across the screen.
Code: [Select]
(client setMotion: MoveTo ISABELLA_LEFT ISABELLA_Y setCycle: Walk loop: 1 self)
I notice she still gets stuck on state 0 and does not move onto state 1. Does "self" end the state and then make it goto the next state? Or do I need to add self changeState:1 for it to move on down.

Offline Kawa

Re: SCI0: Actors and scripts
« Reply #3 on: May 12, 2023, 12:33:54 PM »
A script's client is a property reference to what's using this script. Calling setScript should set the script's client to Isabella. This makes things more reusable I guess.
self is a reference to the current object (the script) so that Isabella's mover, MoveTo in this case, knows "who cares" and can cue the script when it's done moving Isabella to the target location.

A script's changeState will trigger on the following events:
1. its doit has finished counting down seconds or cycles -- set either property to a non-zero value at the end of a state, sit back, and wait.
2. something cue'd it -- so for example a looper or mover reached its target and you told it who to call back to.
3. you yourself called (changeState X) in a state, like in my state 2 to immediately restart at state 0.

In your original code state zero did absolutely nothing, so there'd be nothing to tell the script to advance.

Offline doomlazer

Re: SCI0: Actors and scripts
« Reply #4 on: May 12, 2023, 03:09:36 PM »
Be careful with calling changeState from a script on itself. Doing so repeatedly can crash the game. Kawa once told me it was best to set the desired state minus 1 when doing that from within a script.

Code: [Select]
(3
    ;do state 0 after one second
    (= seconds 1)
    (= state -1)
)
« Last Edit: May 12, 2023, 08:59:25 PM by doomlazer »

Offline Kawa

Re: SCI0: Actors and scripts
« Reply #5 on: May 12, 2023, 03:25:10 PM »
The danger is mostly a stack overflow risk.
To explain how this can be safe: the first changeState in state 2 calls the same method again, which then sets up the MoveTo and returns. Then the outer changeState returns, and the stack survives unmolested.

Here's Steve Conrad doing it in Leisure Suit Larry 5:
Code: [Select]
(instance sMonitorRecording of Script
(method (changeState newState)
(switchto (= state newState)
(
; Returns immediately, as I just described -- Kawa
(= seconds 10)
)
(
(if (Random 0 1)
(reverseBiaz setLoop: 0, setCel: 0, setCycle: EndLoop self)
)
else
(reverseBiaz setLoop: 1, setCel: 0, setCycle: EndLoop self)
)
; As described before, "self" is passed to the EndLoop cycler so when Biaz finishes we cue the script.
)
(
(reverseBiaz setCycle: BegLoop self)
)
(
; Calls this very same method again... which sets up a 10 second delay, returns, and then this returns.
(self changeState: 0)
)
)
)
)

There are nine other places in LSL5 where (self changeState: 0) is used.

Offline robbo007

Re: SCI0: Actors and scripts
« Reply #6 on: May 16, 2023, 12:37:25 PM »
Is it also possible to delay the change state moving to the next stage? IE when walking left have the actor pause x amount of seconds, then move to next change state right. I see in the LSL3 source they use (delay 0) but this does not work. In your waves code you had:
Code: [Select]
(= seconds (+ 1 (* (Random 1 3) 2))) Not sure if this is the same? I've tried Wait=X but that seems to freeze everything.

The other question when using registers, whats the correct syntax for using in a method for talk for example?

Code: [Select]
((Said 'talk/man')
changeState 4
register (++ messageCount)

Then in the change state use:
Code: [Select]

(0 ; Responses to talk
        (switch register
        (1
        ( Print "\"Hello,\"  \"New in town?\"")
         )
         (2
          (Print next response...)
(1 ; next change state

Sorry for so many questions, I'm getting deeper and deeper into this and loving it. Hopefully my questions can also help others.

Offline Kawa

Re: SCI0: Actors and scripts
« Reply #7 on: May 16, 2023, 12:48:30 PM »
seconds is a property of Script, just like cycles is. Script::doit, if it finds either of these to be non-zero, ticks them down by one each cycle or second, and when they hit zero it cues itself, causing a state advance that you can then handle in changeState.

Larry 3 has several defines set up in Game.sh that SCI Companion can't use. "delay" is one, and is set to mean "= seconds". So (delay 5) literally just means (= seconds 5).

register, far as I can tell, is a free general-purpose property for your Script instance. Saves you a local.

Offline doomlazer

Re: SCI0: Actors and scripts
« Reply #8 on: May 16, 2023, 02:12:20 PM »
The other question when using registers, whats the correct syntax for using in a method for talk for example?

Code: [Select]
((Said 'talk/man')
changeState 4
register (++ messageCount)

Then in the change state use:
Code: [Select]

(0 ; Responses to talk
        (switch register
        (1
        ( Print "\"Hello,\"  \"New in town?\"")
         )
         (2
          (Print next response...)
(1 ; next change state

I'm still learning myself so don't take anything I say as gospel, but you shouldn't need use changeState. It can be done right in the eventHandler. You'd need to use a local variable instead of register though (if called from a "room" handleEvent at least; register would likely work from a script's handleEvent):

Code: [Select]
(local
    messageCount
)

Code: [Select]
((Said 'talk/man')
    (switch messageCount
        (0
            ;response 1
        )
        (1
            ;response 2
        )
        (else
            ;final repeating response
        )
    )
    (++ messageCount)
)

If you did need to use a separate script for more complex logic/animation:

Code: [Select]
(4 ;state 4
    (switch register    ;either a local var or register should work in this case
        (0
            ;response 1
        )
        (else
            ;final repeating response
        )
    )
    (++ register)
)

Also, you'll find it easer to use print statements with braces/curly brackets so you don't have to escape your quotes for the dialog.

Code: [Select]
(Print {"Hello," said man.\n"New in town?"})


Even better would be to add all your message strings to the script's text resource (see attached png), which saves a bunch of heap vs having strings in the script:

Code: [Select]
(Print 23 1) ;(Print [textResourceNumber] [stringNumber])
« Last Edit: May 16, 2023, 04:05:12 PM by doomlazer »

Offline robbo007

Re: SCI0: Actors and scripts
« Reply #9 on: May 18, 2023, 12:01:37 PM »
Really appreciate the examples. The script is looking very nice now:

Code: [Select]
(instance aIsabelaScript of Script
(properties)
(method (handleEvent pEvent)
(super handleEvent: pEvent)
(if(Said 'talk/girl, woman, chick, babe')
(if (& (gEgo onControl:) ctlPURPLE)
(self changeState: 3)
(++ messageCount)
else
(PrintNotCloseEnough)
)
)
)
(method (changeState newState)
(= state newState)
(switch state
(0 ; Walk Isabela to the left/center random with a pause of 15/10 seconds.
(client setMotion: MoveTo (Random ISABELA_LEFT ISABELA_CENTRE) ISABELA_Y setCycle: Walk loop: 1 self)(= seconds (Random 15 10))
)
(1 ; Walk Isabela to the right with a pause of 15/13 seconds.
(client setMotion: MoveTo ISABELA_RIGHT ISABELA_Y setCycle: Walk loop: 0 self)(= seconds (Random 15 13))
)
(2 ; Kawa's Cha-cha real smooth repeat
(self changeState: 0)
)
(3 ; Talk a little. Isabela stops to talk. Then returns walking changestate 0
(client setMotion: FALSE setCel: 0 loop: 2 setCycle: End self ) (= seconds 20)
    )
(4
            ;Responses that are cycled through
            (switch register
        (0
            (Print {response 1})
        )
        (1
            (Print {response 2})
        )
        (else ;Once responses have been exhausted repeat response
            (Print {General response.})
        )
    )
    (++ register)
    (= messageCount 2) ;counts the response message register
)
(5
;(= register 0) ; resets register
(self changeState: 0)
    )
)
)
)

Offline Kawa

Re: SCI0: Actors and scripts
« Reply #10 on: May 18, 2023, 12:22:41 PM »
(client setMotion: MoveTo (Random ISABELA_LEFT ISABELA_CENTRE) ISABELA_Y setCycle: Walk loop: 1 self)(= seconds (Random 15 10))

1. Semantics error: self is supposed to be used as a parameter to setMotion. This may even be a syntax error considering loop is a property with a single value.
You seem to be doing this: setMotion to MoveTo, setCycle to Walk, change property loop to 1. But it reads like calling the loop "method" with 1 and self as the arguments.

This is probably why Sierra ended their individual sends with commas, though proper outlining is a Very Good Idea? too. Compare and contrast:
Code: [Select]
(client
  setMotion: MoveTo (Random ISABELA_LEFT ISABELA_CENTRE) ISABELA_Y
  setCycle: Walk
  loop: 1 self ;<-- you can now clearly see something's not kosher.
)
(= seconds (Random 15 10))
Exactly the same code snippet, completely different visual effect.

2. Using a self and the seconds property is gonna be fun: if that self had a use, like if it were given to the setMotion call? The moment Isabella reaches her target position, the script is cued and changes to the next state, 1. If she does that within, say, five seconds, the ten to fifteen you set right after will elapse and the script immediately changes to state 2. The same thing would happen if the amount of seconds is lower than the time it'd take Isabella to walk over to the left/center. So you're lucky that self is useless where you put it.

Offline robbo007

Re: SCI0: Actors and scripts
« Reply #11 on: May 19, 2023, 08:58:11 AM »
Hi,
Yes your right. I need to clean up my code and format it correctly. How you have displayed it makes more sense and makes it more readable :)
I was using Self thinking that was what was responsible for making the changestate move to the next state. Hence why I slapped it on the end of loop. I now I think I have abetter understandanding how its meant to be used. I've now fixed up my code and it works how I want it to :)

Thanks for clearing this up. My next feat is working out the dying scripts. That was one of the last things on my main list to get worked out.



Offline Kawa

Re: SCI0: Actors and scripts
« Reply #12 on: May 19, 2023, 09:39:10 AM »
self is literally just a reference to The Current Method's Owner, such as aIsabelaScript, no matter the name. It could refer to an instance that was dynamically allocated with new:.

How it works in conjunction with movers is this: the Motion class, from which MoveTo descends, has this method, (method (init theClient theX theY theCaller). There's some temps there too but I don't care. When you do Actor::setMotion, you specify a class name and a bunch of arguments. setMotion: cleans up the previous mover if any, instantiates a new one of the given class, and passes the rest of the arguments to its init:. That then notices theCaller is actually given (or not) and remembers it in its own caller property. If none was given (you only said where to go), caller is set to null.

Afterwards, every cycle, Isabelle will notice her mover property is non-null (it's set to that new MoveTo instance) and call its doit:. That, in turn, will eventually realize it's arrived and, having a non-null caller property, calls its cue:. And because you're expected to specify a Script (or rather anything that responds to cue:), well... yeah.

Note that it doesn't have to be self. One script can have a mover or cycler cue another script.


SMF 2.0.19 | SMF © 2021, Simple Machines
Simple Audio Video Embedder

Page created in 0.043 seconds with 23 queries.