------------------------ -- EXCERPTS FROM ANIMATION CLASS IN PLAYFARM -- Copyright 1994 Kaleida Labs, Inc. ------------------------ class Animation(TwoDShape, Projectile, Pannable) instance variables seq cell animationClock blockflag animationq basicset transitionset lasttransition partialpivots movecontroller modelshell end ------------------------ method startAnimation self {class Animation} -> ( startClock self self.blockflag.state := @open ) ------------------------ method doaction self {class Animation} #key anim:(undefined) -> ( local allseqs if anim = undefined do ( print "animation called without a sequence" return false ) - if the animation is in the list of the last transitionsetŐs - animations then add it to the queue if (ismember self.transitionset[self.lasttransition] anim) then ( append self.animationq self.basicset[anim] ) - otherwise find the transition necessary to perform the animation by - checking each transition list to see if animation is contained in it else ( - append the entire animation sequence returned to the queue local aq aq:= (buildAnimSequence self anim #(self.basicset[anim])) addmany self.animationq aq ) self.blockflag.state := @closed gateWait self.blockflag ) ----------------------------------- - This method recursively builds the sequence of animations required - to perform the requested animation since it was not possible to - perform the animation from the last transition. anim is the - animation you want to perform seq is the array that gets built that - holds the entire sequence through recursion When a method (other - than this one) calls this method set seq:= #(anim) method buildAnimSequence self {class Animation} anim seq -> ( local a, akey, atran - get the transition that the animation can be performed from a:= select first array in self.transitionset where (ismember it anim) - get the transition token akey:= getkeyone self.transitionset a.target - if this transition is not the last transition, recurse to find - the transition that akey is a member of if akey <> self.lasttransition do ( - add it to the animation sequence prepend seq self.basicset[akey] buildAnimSequence self akey seq atran:= self.partialpivots[akey] if atran <> empty then self.lasttransition:= atran else self.lasttransition:= akey ) seq ) -------------------------------- method tick self {class Animation} -> ( if (self.seq = empty) do ( if ((self.seq := self.animationq[1]) = empty) do return self.velocity.x := self.seq.velocityx self.velocity.y := self.seq.velocityy - If there is a sound for this frame sequence, play it. local s := self.seq.sound if (s <> undefined) do ( - Add time callback so player will stop and go to beginning. addTimeCallback \ s (p -> (stop p; goToBegin p)) s #() s.duration false play s ) self.seq ) - Change the stencil to the next animation cell in the series. self.boundary := self.seq[self.cell] tickle self.movecontroller self.animationClock self.cell := ((mod self.cell (size self.seq)) as Integer) + 1 if self.cell = 1 do ( if (self.animationq[3] = empty) do ( self.blockflag.state := @open ) setPresenterX self self.x + self.seq.extrax self.y := self.y + self.seq.extray deletenth self.animationq 1 self.seq := empty ) ) ------------------------ - EXCERPTS FROM SHEEP PRESENTER CLASS ------------------------ class SheepPresenter (Animation, ModelDragger) end ---------------------------- method init self {class SheepPresenter} #rest args -> ( apply nextmethod self args self.x := 0 self.y := 180 self.velocity := new point x:0 y:0 m := rootHandle "media" SheepStore - Changed: added sound. local baaahSound := Sounds["sheep baaah friendly"] - Changed: added sound. self.basicset := #( @walkright: \ (makeFrameSequence m 1 1 #(151,152,153,154,155,156,157,158)), @walkrightdown: \ (makeFrameSequence m 2 1 #(151,152,153,154,155,156,157,158)), @knitright: \ (makeFrameSequence m 0 0 #(163,164,165,166,167,168,169,170) \ sound:baaahSound), @exerciseleft: \ (makeFrameSequence m 0 0 \ #(63,64,65,66,67,68,69,70,71,72,73,74)), -........and so on for the rest of the sequences.... ) self.transitionset := #( @turnleft: \ #(@walkleft, @walkleftdown, @walkleftup, @measureleft, \ @exerciseleft, @complainleft, @tiltheadleft, @eatleft, \ @dropleft, @knitleft, @faceleft,@turnright), @turnright: #(@walkright, @walkrightup, @walkrightdown, \ @measureright, @exerciseright, @tiltheadright, @eatright, \ @dropright, @knitright, @faceright,@turnleft) ) self.partialpivots:= #() self.lasttransition := @turnright self.boundary := m[151] return self ) ------------------------ - EXCERPTS FROM SHELL CLASS ------------------------ class Shell (rootObject) instance variables attributes - the attributes of the shell, such as category, - size, moveable shelltype - types in Playfarm are @prop or @character or @fixture homecell - the cell the shell is contained in now parentModel - the model grid parentSpace - the presentation space shellPresenter - the animation, or presentation object, for this shell direction - own direction, @left or @right are supported targetdirection - holder for the direction of another shell - interacting with this one busy - boolean for busy state (when in a choreography - or being dragged a shellŐs busy state is true) end ------------------------------ - This method adds a new shell to the model space and creates a - representation in presentation space. method addToScene self {class Shell} model space -> ( self.parentModel := model self.parentSpace := space if (self.homecell <> undefined) then ( reconcilecell self switchto self.homecell self ) else reconcilexy self if (self.shellpresenter <> undefined) do prepend space self.shellpresenter ) -------------------------------- method celltoxy self {class Shell} -> ( - translate the row/column coordinates to the x,y coordinates - in presentation space. local node := self.homecell.location local x, y y := node[1] * NODEHALF + HORIZON - self.shellpresenter.height if (mod node[1] 2 = 0) then ( x := (node[2] * NODELENGTH \ - (self.shellpresenter.width / 2) - NODEHALF) as integer ) else ( x := (node[2] * NODELENGTH \ - (self.shellpresenter.width / 2) - NODELENGTH) as integer ) setPresenterX self.shellpresenter x self.shellpresenter.y := y ) -------------------------------- - reconcile presentation and model space coordinates based on - presentation coordinates. This method is typically called after a - shellpresenter is dragged and dropped. method reconcilexy self {class Shell} #key x: y: -> ( if (x <> unsupplied) do ( setPresenterX self.shellpresenter x self.shellpresenter.y := y ) xytocell self celltoxy self ) ------------------------ - EXCERPTS FROM CHARACTER SHELL CLASS ------------------------ class CharacterShell (shell) instance variables belongings - props (and possible other objects) the character - is carrying charActivityEngine - the activity engine guiding this character end ------------------------------- method init self {class CharacterShell} #rest args -> ( apply nextmethod self args self.belongings := #() self.shelltype := @character self.charActivityEngine := new ActivityEngine ) ---------------------------------- - EXCERPTS FROM SHEEP CLASS - Specialized behavior for Irma La Sheep. Activities and choreographies. ---------------------------------- class Sheep (CharacterShell) end method init self {class Sheep} #rest args -> ( apply nextmethod self args self.shellpresenter := new SheepPresenter \ actioneng:self.charActivityEngine self.shellpresenter.modelshell := self - Wander Around Activity local wander := new CharacterWanderAction actor:self - Knit Sweaters Activity local isAnimalNearBy := new AnimalNearbySensor pshell: self local measure := new MAction \ actor:self targetAction: self.shellpresenter \ anim: #(@measureleft,@measureright) - function that creates a new sweater object. This function - is passed to the knit action as the effect local function knitfunc arg -> ( local b := getnth \ #(propBitmaps["sweater 1"],propBitmaps["sweater 2"],\ propBitmaps["sweater 3"]) \ ((rand 3) + 1) local pp := new PropPresenter boundary:b fill:blackbrush \ stroke:undefined local p := new PropShell propPresent: pp p.attributes := new SortedKeyedArray \ keys:#(@category, @size, @moveable) \ values:#(@item,@small, @true) return p ) local knit := new ProduceAction actor:self \ effect: knitfunc \ anim: #(@knitleft,@knitright) \ targetAction: self.shellpresenter local giveGift := new DropAction actor:self \ targetAction: self.shellpresenter \ anim: #(@dropleft,@dropright) local hand_item := new MessageAction actor:self message: @hand_item \ effect: (arg -> \ arg.target := isAnimalNearBy.searchResult; \ arg.adata := giveGift.target) local knit_seq := new TwoShellChoreog \ seq: #(measure, knit, giveGift, hand_item) \ actor: self sensor:isAnimalNearBy local wander_loop := new LoopAction \ targetaction: wander condition: (-> ask isAnimalNearBy) local findAnimal2Knit := new ActionSequence actor:self \ seq: #(wander_loop, knit_seq, wander, wander) - Complain Activity local complainAction := new MAction \ actor:self targetAction: self.shellpresenter \ anim: #(@complainleft,@complainleft) local randComplain := new randomAction \ actor:self targetAction:complainAction \ probability: 60 local complainActivity := new ACtionSequence actor:self \ seq: #(wander, wander, wander, randComplain) - The schedule for the sheep contains 2 activities: - findAnimal2Knit and complainActivity The time slot - each would run is 5 cycles. See comments in - activeng.sx. This is a primitive implementation of - schedules, will be upgraded. self.charActivityEngine.schedule := #(findAnimal2Knit,complainActivity) self.charActivityEngine.timeunit := 5 self.charActivityEngine.timeCycle := 10 ) ---------------------------------- - EXCERPTS FROM ACTION CLASS ---------------------------------- class MAction (rootObject) instance variables actor - the character or prop that owns this action effect - a script to run after the actio is complete. - things like increasing or decreasing strengthners go here anim - token for animation to call targetAction - the presenter (or embeded action) associated with action end ----------------------------------- method init self {class MAction} #rest args #key targetAction:(undefined) \ anim: actor: effect: -> ( self.actor := actor self.targetAction := targetAction self.effect := effect self.anim := anim ) - This method carries out the targetAction. If target action is - an animation, it will animate. method doAction self {class MAction} #key anim: -> ( if ((anim <> unsupplied) and (anim <> undefined)) do self.anim := anim if self.targetAction <> undefined do ( if self.anim <> unsupplied and self.anim <> undefined then ( doAction self.targetAction \ anim: (if (self.actor.targetdirection = @left) then \ self.anim[1] else self.anim[2]) ) else doAction self.targetAction ) if self.effect <> unsupplied do self.effect(self) ) ----------------------------------- - This action class encapsulates the behavior of taking an object - form the environemt and then owning (carrying) it. class TakingAction (ActionWithOther) end method doAction self {class TakingAction} #key anim: -> ( nextmethod self anim:anim if self.actor <> undefined and self.target <> undefined do ( - Remove the prop form the environent and add to the actor. - addProp self.actor self.target removeProp self.target.homecell self.target deleteOne self.target.parentSpace self.target.shellpresenter ) ) ----------------------------------- - This action is the complementary action to taking action. It - encapsulates the behavior of adding an object to the environment. class DropAction (ActionWithOther) end method doAction self {class DropAction} #key anim: -> ( nextmethod self anim: anim if self.actor <> undefined and \ ((getnth self.actor.belongings 1) <> empty) do ( self.target := (getnth self.actor.belongings 1) deleteOne self.actor.belongings self.target self.target.homecell := self.actor.homecell addToScene self.target self.actor.parentModel \ theSceneSpace if ((getOne (attributesGetter self.target) @moveable) = @true) do append theDragController self.target.shellPresenter ) ) End Listing