Projects » SFPoser Animation Controller BETA 0.7




Notice: The Beta version has been updated. Please get the new script code from the link at the bottom of this page


WHAT IS SF POSER

SF Poser is a General-Purpose Animation Controller for up to 99 avatars. It can be used to create furniture from scratch and quickly without editing notecards at all. It allows on-the-fly adjustments of poses and change of animations, automatic timers, prop rezzing, NPC rezzing, RLV, expressions, and other built-in powerful utilities.
It uses the same notecard format of the wonderful PMAC system, which means that it works right away with existing PMAC furniture. 


SF POSER FEATURE SET




QUICKSTART WITH SF POSER

You can get the SFposer package from the opensimworld region: https://opensimworld.com/hop/74730
The package contains an empty SFposer Template.

- Rez the template and add your animations in its contents
- Click on it and select [Actions]->New Menu to create a new Pose menu (Singularity viewer may shorten the button title to "New Men")
- Then click on [Adjust]->New Pose to create your first pose. The positioning handles should appear
- Touch each handle to select the animation you want to play for each position in the pose
- When done, click Save Pose and then Save Menu
Congratulations, you just created your first menu card and pose!



TO CONVERT AN EXISTING PMAC OBJECT

- Remove all the existing scripts from the object
- Drop the ~positioner, the ~baseAnim and the ..SFposer script in the contents of the PMAC object. You will find those inside the SFposer Template object
- That's all!


TO CONVERT FROM OTHER SYSTEMS (AvSitter/MLP)


There is an online converter system that you can use to convert MLP and AVsitter cards to the SFposer/PMAC format: https://opensimworld.com/tools



USING SHORTCODES{}


Shortcodes are strings of the form SHORTCODE{arg1;arg2} that are added to notecards to instruct SFposer to do something while playing an animation. They can also be added as add-on buttons in the [Actions] menu, and they can be invoked remotely via the API. 


To add a shortcode to a pose, insert it in the specially reserved second column of the .menu notecards, right after the pose name. The column is usually occupied with "NO COM" or "NOCODE".  Replace those with your shortcode(s)

Dance Together2|NO COM|danceLeft2|...
Dance Together|EXPR{smile;3;smile;4}|danceLeft|...
Dance Together3|SHORTCODE_GOES_HERE|danceLeft3|...


To add a Shortcode as a button in the [Actions] menu, add a Button line in the .SFconfig notecard as follows:

Button=ButtonLabel=SHORTCODE{arg;arg;arg}

After editing notecards, you need to stand up and sit again, to force the script to reload them.




ADDING EXPRESSIONS


For each avatar participating in the pose, you can specify an expression animation to play and the time it takes to repeat the expression (in seconds -- if you dont want repeats , enter 0). The Expressions Shortcode is:

EXPR{expression1;repeatTime1;expression2;repeatTime2;expression3;repeatTime3 ... and so on for every avatar of the pose}

The expressions and repeatTimes can be left empty, but be sure to include the required ';' separators for all avatars in the pose.
For example, the following notecard line defines expressions for the pose "WatchMovie" which is for 3 avatars:

WatchMovie|EXPR{laugh_emote;4;open_mouth;3;frown;0}|chair_sit|<0.2112.... [the rest of the pose line]

In this case, avatar1 will play the animation express_laugh_emote every 4 seconds; avatar2 will play express_open_mouth every 3 seconds and avatar3 will play express_frown only once.
For brevity, the "express_" part of the animation name is omitted (so you only enter "frown" for animation "express_frown") . The full list of available expressions is:

open_mouth, surprise_emote, tongue_out,smile, toothsmile, wink_emote, cry_emote,kiss, laugh_emote,disdain, repulsed_emote, anger_emote, bored_emote,sad_emote, embarrassed_emote, frown,shrug_emote, afraid_emote, worry_emote



PROPS REZZING


"Props" are rezzable objects. All props objects must contain the SFposer Prop script so that they can be deleted after use. Click here to get the script

After adding the script to your prop, drop it in the contens of the object and rename it to "MyProp".

Add the following Shortcode to a pose to rez the prop:

PROP{MyProp;<1,1,1>;<0,0,0>}  


This will rez MyProp at position <1,1,1> and rotation <0,0,0> (in Euler coordinates, radians) relative to the object, when the pose is selected.

Sit and re-sit on the SFposer to reload the notecards, and then select the pose again. The prop should rez in the position <1,1,1>. 

Edit the prop to position it to its final position, and then select  [Adjust]->Edit Pose -> Save Pose. The system will print out the final PROP{} shortcode that you should use to correctly position the prop. Replace the shortcode in the .menu notecard again witht he final one. After re-sitting your prop should rez in its correct position. 

Additional shortcodes for props are supported. You can use them to create Buttons that rez/derez props:

TOGGLEPROP{MyProp;<1,1,1>;<0,0,0>}   When the button is pressed, it rezzes the prop, when pressed again, it deletes it.

DELPROP{MyProp}  Deletes the prop

For example, you can  define a rez/unrez button in the .SFconfig notecard as follows:

Button=Rez/Unrez=TOGGLEPROP{MyProp;<1,1,1>;<1,1,1>}

(remember to re-sit in order to reload the card)


Attachment Props: You can use the "SFposer attachment prop script" (get it here) to create props that auto-attach to the current user. Use the following procedure:

- Wear the attachment, adjust it to its final position and add the "SFposer Attachment Prop script" inside it. 
- RESET THE SCRIPTS in the attachment to record its position
- Detach the attachment,  add it in the contents of the SFposer object, and make it full permissions

Add the following line to the .SFconfig notecard to create a button  to rez the prop:

Button=Attach MyProp=PROP{MyPropName;<0,0,0>;<0,0,0>}

After that, re-sit in your SFposer object  and select [Actions] -> Attach MyProp. The prop should rez and request to attach to your avatar.

Note that Attachment props are temporary and cannot be detached by right clicking, instead the user will have to click on them to detach.



RLV SUPPORT 

SFPoser has built-in support for RLV and uses shortcodes to implement it.

Add the following lines to your .SFconfig notecard to create an "RLV Capture" and  an "RLV Release" button:

Button=RLV Capture=RLVCAPTURE{20}
Button=RLV Release=RLVRELEASE{}


The shortcode RLVCAPTURE{20} indicates that 20 is the maximum distance  (in meters) within which to search for avatars to capture. RLVRELEASE{} does not have any arguments 

For each pose, you can use the RLV{} shortcode to send RLV commands from SFposer: 


RLV{avatarNum; @rlvCommand1 ; @rlvCommand2; @rlvCommand3 ... }


In the line above, avatarNum is the position of the avatar (0 is the first avatar). You can send as many separate RLV commands you wish with a single RLV{} shortcode, but remember to separate them with ';'

RLV support works with osCollar 7




BUILT-IN GIVER


To give an object (e.g. Popcorn) to the user who is currently using the system, create a Button in the .SFconfig notecard with the GIVE{} shortcode as shown below:

Button=Get Popcorn=GIVE{Popcorn}

The button "Get Popcorn" will be added to the Main menu's [Actions] screen. Make sure the Popcorn object is inside the contents of the SFposer object and that it is copyable or else the command will fail silently




NPC REZZING SUPPORT


You can add NPCs to SFposer by adding appearance notecards inside the object. Each NPC appearance notecard must use the naming convention of PMAC: ".NPC00A  Npc Name". Appearance Notecards created for PMAC should work right away. Add the NPC notecards and reset the object by re-sitting. The NPCs submenu is in the [Actions] menu.




LOCKGUARD CHAINS SUPPORT
SFposer supports LockGuard V2 cuffs for chains and ropes. In order to add lockguard settings to a pose, use the LG{} Shortcode which should be added to your pose notecards:

LG{0;rightwrist;rightHook}

This instructs the cuff worn by the avatar sitting at position 0 (the first position) to link to the child prim named "rightHook" and uses the lockguard command "rightwrist" which points to the right wrist cuff. You can use a more elaborate lockguard command instead such as "rightwrist gravity 4 life 1.5 color 1 0 0" but DO NOT INCLUDE the "link" and "unlink" commands. These are added automatically by the system.
You can add multiple LG{} Shortcodes for multiple cuffs. An example notecard line is:

UseCableMachine| LG{0;rightwrist life 1;righthook} LG{0;leftwrist life 1;lefthook} |cables|<1.3282,1.8789,0.8556>|<0.0001,-0.0002,0,1.>

Chains are unlinked automatically when someone stands.




RUNNING EXTRA ANIMATIONS


You can create a Button that runs a set of animations ON TOP OF the currently playing animations. For example you can create a Button that makes everybody laugh at any time by adding the following line to .SFconfig notecard:

Button=All Laugh=ANIM{express_laugh;express_laugh;express_laugh}

to create a button to stop those animations, use the STOPANIM Shortcode instead of ANIM:

Button=All Laugh=STOPANIM{express_laugh;express_laugh;express_laugh}

Note that, unlike EXPR{} , you have to specify the full animation name here, and there is no repeat-time since the animations here are not repeatable




AUTO TIMER DURATION

SFposer supports auto-timer that switches poses every X seconds. The menu option is under Menus->[Actions].
You can specify the default auto timer duration (in seconds) in the .SFconfig notecard:
autoTimer=60

You can set the auto-timer for specific animations by using the shortcode TIMER{50} , which will set the animation every 50 seconds.TIMER{0}  will stop the timer.



ADVANCED SHORTCODES

Shortcodes like GIVE{... }, can be used either via Buttons (shown in the the [Actions] menu) or by adding them to the pose line in a notecard.  The Shortcode-section of a notecard can contain multiple Shortcodes .For example, if you want a pose to both rez a PROP{} and run some animation add the following Shortcodes:

PROP{MyProp;<1,0,0>;<0,0,0>} ANIM{clap;clap;clap;clap}

The same Shortcodes can be used via a Button. The button is defined in the .SFconfig notecard with a single line as follows:

Button=MyButtonLabel=PROP{MyProp;<1,0,0>;<0,0,0>} ANIM{clap;clap;clap;clap}

This allows great flexibility to create many different types of objects without any effort or using addon scripts. Note that you can add only up to 6 Buttons in the [Actions] menu

In addition to the Shortcodes described so far, the following Shortcodes are supported:

MSGPROP{MyProp;Hello, prop} Sends the dataserver message "Hello, prop" to the already-rezzed prop MyProp using osMessageObject()

MSGATT{0;Hello, avatar1 attachment;19,4} Sends the dataserver message "Hello, avatar1 attachment" to the attachments attached on attach points 19 or 4 of the avatar sitting on position 0 of the pose (first position) using osMessageAttachments(). You can read more on the documentation of osMessageAttachment online

MSGLINK{4;Hello link number 4} Uses osMessageObject to send the dataserver message "Hello link number 4" to the linked prim at link number 4

SAYCH{21;Hello, channel 21} Uses llSay() to say the string "Hello, channel 21" to the local chat channel 21. The string can contain the special codes %USER00, %USER01, %USER02 etc which are replaced witht the Name of the user sitting at position 1, position 2 etc. For example: SAYCH{0, The user %USER00 is sitting with %USER01} 


SAY{Hello public channel} Like SAYCH, but for the local chat only


LINKMSG{-1;99;Hello, all prims} Uses llMessageLinked(-1,99, "Hello, all prims" , <list-of-avatarIds> ) to send a link message. Remember LINK_SET = -1, LINK_THIS=-4


SHOW{MyChildPrim; <1 or 0>} Show or hide the child prim named MyChildPrim (1 = show)

TRIGGERSND{My sound} Play the sound "My sound" (to accompany an animation)

LOOPSND{my sound} Start playing a sound loop with sound "my sound"

STOPSND{} Stop sounds



Changing linked prim  properties on the fly


SETPRIM{MyPrimName; <specially-encoded-list> }


This shortcode can be used to change the properties of the linked prim(s) named "MyPrimName"  using the list of prim properties as in llSetPrimitiveParams(). It is essentially a way to call llSetLinkPrimitiveParams() without adding another script in the object. To use this shortcode, you must encode the required list of properties. Use the script "SFposer encoder for SETPRIM" (link at the end of this page) to generate the shortcode

Quick Example: Set the prim "myPrim" to be transparent : SETPRIM{MyPrim; I;18;I;-1;V;<1,1,1>;F;0.0}
Set it to be visible again:  SETPRIM{MyPrim; I;18;I;-1;V;<1,1,1>;F;1.0}


Setting particles per animation


SETPARTICLES{MyPrimName; <specially-encoded-list> }

The shortcode SETPARTICLES{} allows you to set the particles in any named prim in the link set. The parameters list to be passed to llLinkParticleSystem() must be encoded with the same script as above, substituting SETPRIM with SETPARTICLES



Presets of  shortcodes

Presets are groups of shortcodes defined in .SFconfig . The syntax for defining a Preset is:


Preset = MyCommand = SHOW{myPrim,1} TRIGGERSND{bang}  This defines the preset  "MyCommand"  as the series of shortcodes shown 

To use a Preset, use the PRESET{} shortcode in a pose line or anywhere:

PRESET{MyCommand} 


If a preset is already playing,  it won't restart when choosing a new animation. To force it to restart , use " PRESET{} PRESET{MyCommand} " (i.e. call preset with an empty argument first). 


Creating Sequences

Using shortcodes, it is possible to turn an animation notecard to a sequence of actions. Use the TIMER{} code to set  the duration of an animation, and any other Shortcode or Presets to play eacn pose in the menu notecard as a part of an orchestrated sequence. Use TIMER{0} to end the animation without looping. An example notecard for a boxing ring would be:


Warmup| TIMER{30} SAY{%USER00 is preparing to fight %USER01} | prepare1| <0.012....
Warmup| TIMER{30} PRESET{Fighting} | fight1 | <0.012....
Warmup| TIMER{0} TRIGGERSND{victory} SAY{%USER00 won! }  | victory1 | <0.012....



Creating Action Buttons with shortcodes


Any shortcode can be added to an add-on button in the [Actions] menu with the Button option in .SFconfig: 

Button=My Button=SAYCH{0; Hello,world}


Running shortcodes in .SFconfig

It is possible to run shortcodes in .SFconfig every time SFposer is resetting. Add the SHORTCODE{} on a single line by itself  and it will be executed when resetting. This can be used for example to change the properties of prims when the system is reset via a SETPRIM{} code


Using Shortcodes and their combinations, it is easy to create advanced features such as turning on lights/controlling other objects/showing and hiding attached clothes/rezzing and derezzing additional objects etc. without the need for additional scripts. 





API COMMANDS & REMOTE OPERATION

The API allows you to send most of the Shortcodes described above to the SFposer object through link messages and through dataserver messages. 


To send commands through link_messages use :


llMessageLinked(LINK_THIS, -1, "SHORTCODE{...}", NULL_KEY);


To send through dataserver messages, use: 


osMessageObject("<SFposer object UUID>", "SHORTCODE{...}");


In order to enable dataserver messages, the allowRemote option needs to be set in the .SFconfig notecard. This option determines how the SFposer can be used by avatars and dataserver messages, and takes the following values: 


allowRemote=0  Only sitting avatars can use the menus, the object does not  accept dataserver commands

allowRemote=1  All avatars, sitting or not, can touch the object to use the menus, the object does not  accept dataserver commands

allowRemote=2  Only sitting avatars can use the menus, and the object accepts dataserver commands

allowRemote=3  All avatars, sitting or not, can use the menus, and the object accepts dataserver commands

allowRemote=4  Disable all menu dialogs, and the object accepts dataserver commands. Use this in case you want to control SFposer remotely-only, in which case SFposer will not generate any dialog when touched. This is useful you want to host SFposer and another menu-driven script in the same root prim. 

If you want to lock the menu access to be only accessible to the owner, add the .SFconfig setting lockMenus=1


In order to allow changing poses and animation menus remotely, the following shortcodes are available for remote operation:

SWITCHTOMENU{My Menu} Switches to the animation menu "My Menu"

SWITCHTOPOSE{My Posename} Switches to the pose "My Posename" which must be in the currently selected animation menu.

UNSIT{<avatarNumber>} Unsits the avatar in position <avatarNumber> (first position is 0)

ADDNPC{firstname lastname} Adds an NPC from the inventory

DELNPC{firstname lastname} Deletes the NPC

SWAP{pos1; pos2} Swaps the users in pos1 and pos2 (first position is 0)


It is possible to use the API to add Buttons  to the [Actions] menu. In this case, instead of a shortcode, the message sent to SFposer is same as the .SFconfig configuration line for creating a button:

Button = Help me = SAYCH{0, Read more about me: https://opensimworld.com/sfposer}


For deleting a button from the Actions menu, there is the shortcode DELBUTTON{}:

DELBUTTON{Help me}



Buttons with arbitrary-string codes can also be created. This is meant to be used for communicating with other scripts. The syntax is: 

Button = Send code = MyCodeIsHere

When pressed, the button will send a link_message with the following format:
llMessageLinked(LINK_THIS, 0, "MyCodeIsHere|<currennt-user-uuid>", "<uuid1>|<uuid2>|...");




Events

SFposer sends link_message notifications to the root prim for any significant event. You can listen to those events to create your own addon scripts. The code names of the link_message events are the same as the ones used by PMAC. 

This is the list of events that are sent: 

GLOBAL_ANIMATION_SYNCH_CALLED
GLOBAL_NEXT_AN
GLOBAL_NEW_USER_ASSUMED_CONTROL
GLOBAL_SYSTEM_RESET
GLOBAL_START_USING
GLOBAL_USER_SAT
GLOBAL_USER_STOOD
GLOBAL_SYSTEM_GOING_DORMANT
GLOBAL_NOTICE_ENTERING_EDIT_MODE
GLOBAL_NOTICE_LEAVING_EDIT_MODE 
GLOBAL_STORE_ADDON_NOTICE
GLOBAL_EDIT_STORE_TO_CARD

Look into the source code to see their arguments. The events are sent via llMessageLinked() to LINK_THIS (the root object), with numeric Value=0. The 'key' argument is replaced with a concatenated list of the currently sitting avatar uuids. An example of the messages sent out is: 

llMessageLinked( LINK_THIS, 0, "GLOBAL_USER_SAT", "000-000-000|000-000-000|000-000-000" );



Handing Events with Shortcodes

SFposer allows you to define an event filter and execute a Shortcode{} for the events specified above. This allows addon-like behaviour without using additional scripts.

The Event filters are defined in the .SFconfig notecard as follows:

OnEvent = <Event filter> = SHORTCODE{}

for example, this event filter will print "Hello, first user" to the local chat when the first user (user 0) sits on the object

OnEvent = GLOBAL_USER_SAT|0 = SAYCH{0; Hello, first user}


The many different events contain different kinds of information. It is possible for example, to use the the event string to trigger a shortcode when a specific avatar position is being filled or emptied. It is also possible to trigger a shortcode when a specific user-id sits on the object. Examples:

OnEvent = GLOBAL_USER_STOOD|3 = SAYCH{0; Goodbye, user 3}

OnEvent = GLOBAL_USER_SAT|0|2ae39a9c-13  = SAYCH{0; Hello, %USER00, i recognized you sitting in first position}

OnEvent = GLOBAL_SYSTEM_GOING_DORMANT = SAYCH{0; Thanks for using me}


Event handlers can be used combination with SETPRIM{} and SETPARTICLES{}  to turn on effects on the child prims (such adding particles to a shower) without adding additional scripts in the object.




OSSL PERMISSIONS

If you already have PMAC in your region, then you have already enabled all the functions that SFposer requires to operate

In case you need to update your .ini files here are some recommended settings. Depending on the opensim version, these need to be added either in config-include/osslEnable.ini or config-include/osslDefaultEnable.ini . 


Allow_osGetNotecard = true
Allow_osMessageObject = true
Allow_osMessageAttachments = ESTATE_OWNER,ESTATE_MANAGER,PARCEL_OWNER
Allow_osAvatarPlayAnimation = ESTATE_OWNER,ESTATE_MANAGER,PARCEL_OWNER
Allow_osAvatarStopAnimation = ESTATE_OWNER,ESTATE_MANAGER,PARCEL_OWNER
Allow_osMakeNotecard = ESTATE_OWNER,ESTATE_MANAGER,PARCEL_OWNER
Allow_osNpcCreate = ESTATE_OWNER,ESTATE_MANAGER,PARCEL_OWNER
Allow_osNpcRemove = ESTATE_OWNER,ESTATE_MANAGER,PARCEL_OWNER
Allow_osNpcSit = ESTATE_OWNER,ESTATE_MANAGER,PARCEL_OWNER
Allow_osSetPrimitiveParams = ESTATE_OWNER,ESTATE_MANAGER,PARCEL_OWNER




SF POSER SYSTEM PARTS:

- The .SFposer script
- The ~positioner handle object
- The ~baseAnim hip-fix animation
- The .SFconfig configuration notecard (optional)




MENU NOTECARDS FORMAT

SFposer uses the same notecard format as PMAC. Each set of animations (menu) goes in its own notecard which is named with the following convention:


.menu0005A Dance Together

where

.menu : All pose notecards must begin with .menu
00: Used for ordering of menus. Can be between 00-99
05: Means this menu notecard contains poses for 5 avatars
A: Menu is for use by (A)ll. Can also be (G)roup or (O)owner
Dance Together: The label shown in the button is "Dance Together"


You do not need to create your own notecards , as they are created for you automatically via the menus. 



NOTES
Despite the name, SF poser is a generic animation controller and is not specific to SatyrFarm, i.e. it can be used for anything.



LICENSE
(c) 2020 Satyr Aeon . Licensed under Creative Commons CC-BY-SA



CHANGELOG

20-9-20 Added .SFconfig adjusterStep, added DELBUTTON{}, SETPRIM{}, SETPARTICLES{} , added OnEvent, Added Presets, added *SND{} added SHOW{}
19-9-20 Added support for up to 99 avatars. Added allowRemote=3 and 4 and added dataserver API 
18-9-20 Added RLV support!
17-9-20 Added attachment prop support
16-9-20 Added LockGuard support , Props derezzing  after pose, fixed timing issues




Added by: OpenSimWorld
Last Update: 17 hours ago
Project Category: Pose Control

Code

File name Added By Last Updated Actions
1. SFposer Main Script OpenSimWorld 16 hours ago View
2. SFposer Prop Script OpenSimWorld 6 days ago View
3. SFposer Attachment Prop script OpenSimWorld 2 days ago View
4. SFposer encoder for SETPRIM shortcodes OpenSimWorld 2 days ago View


Comments

GaryKohime 2 days ago
Actually its very confusing, the Quick Start guide needs some serious changes, I wish I could provide some screen grabs, as the options, when choosing New Men (perhaps it supposed to say New Menu?) are not as the quick start guide says. So, its not clear what to do and under what circumstances?
like(0)
GaryKohime 2 days ago
btw, there is no option in the menu that says New Menu, there is the following to choose from: 1. Help, 2. Lock Men, 3. New Men, 4. -, 5. Back, 6. Quit, 7. Auto. (then Mute or Ignore on bottom). Should I choose New Men?
like(0)
GaryKohime 2 days ago
The allowRemote=1 was already there. Right click gets the menu by selecting "Touch" in the pie menu. The instructions are counter intuitive to when you hover over the sfposer object that's rezzed, which is like a sit action. If that makes sence. Perhaps if you change your Quick Start guide to say: "Right click on object (SFPoser) and choose "Touch" from the pie menu, and take out the sit hover action thing, whatever its called? Does this make sense? Thanks!
like(0)
GaryKohime 2 days ago
let me try why you suggested, I did obtain the poser from SatyrFarm. Will report back...thanks!
like(0)
GaryKohime 2 days ago
I keep getting this error (which makes no sense): 05:42] SF Poser Template: ERROR: Menu is for 0 users
[05:42] SF Poser Template: No more than 0 users can sit.
[05:42] SF Poser Template: No menus found. Select Actions->New Menu to create menus

I can't select anything as to menu, Actions, or whatever? I followed the Quick Start guide, and updated the scrip to the above item # 1. Please help, thanks!
like(0)
OpenSimWorld 2 days ago
hello. Please add the line "allowRemote=1" to your .SFconfig notecard and try again. You can also get the latest package from Opensimworld region
like(0)
OpenSimWorld 2 days ago
you will then have to right click on it to touch it for the menu. sorry about that
like(0)
Spax Orion 3 days ago
I have been testing this like crazy and also making suggestions to make it fully on par with PMAC. This is nothing short of AMAZING! It honors the A-G-O in .menu and .npc cards so you can assign these to anyone, group or owner operation. I have a 17 seat PMAC and this is an excellent replacement. The script is smart, it will only delete NPCs it generated when 'quit' is invoked, external npcs which used the furniture remain. It works well with activeNPC, for npcs to issue shortcodes, you will need to add a few lines of code to your ..extension script. This will revolutionize how I make NPC shows. I can put SFPoser (I call it SFAC) into a stage and then have activeNPC control it through a simple .scr notecard. This is BEYOND FANTASTIC! Well done!
like(0)