To test latest behaviors you must edit your Requestly redirection and point it to https://www.vgm-quiz.com/dev/webliero/client-extended-latest.js

  • With behaviors it is possible to "extend" wObjects with custom logic in pure javascript. The same behavior can be associated to multiple wObjects, but a given wObject can have only one behavior.
  • The code of a behavior is the body of an "update" function in which is exposed:
    • this the current wObject being updated
    • tick the number of frames elapsed since round started
    • life the number of frames elapsed since object was spawned (will be 0 if timeToExplo is 0)
    • api the API to access game functions

News v81

  • api.setUI(id,html) overlays an HTML element on top of the game
  • api.sendAction({event:'ui-click', item:'bar'}) to send a signal based on a UI action

News v80

  • api.randomSpawn() returns a valid spawn point ({x,y})
  • api.getLevel() to get the level object ({width,height,data})
  • api.getMaterial(x,y) to get material value of specific pixel
  • onSignal callback for static behaviors with api.sendSignal({foo:'bar'})

News v75

  • wObjects all have a unique id (obj.id)
    an object can now remember its created children, and find an object with api.getObject(id)
  • platform rope hook fixes
    it is now possible to hook any wObject!
  • new postUpdate/killed callbacks for wObjects
    and also new static callbacks executing at the mod level!
  • new worm properties direction and aimAngle
    all worm properties are also setters!
  • new getPlayerName() getPlayerCountry() getPlayerWeapons()...
  • no more caching and abstraction of getPlayers()
  • new getters/setters on wObjects and nObjects for better OOP

API

  • sendSignal(o)
  • getLevel()
  • getMaterial(x,y)
  • getMaterialArray()
  • createBlood(qt,x,y,vx,vy,?nid=6,?parent=0,?owner=-1)
  • createHealthPack(x,y,t)
  • createWeaponCrate(id,x,y,t)
  • createObject(o)
  • getObject(id)
  • killObject(obj) or obj.kill()
  • removeObject(obj) or obj.remove()
  • setObjectVelocity(obj,x,y) or obj.vx = x
  • getObjectVelocity(obj) or obj.vx / obj.vy
  • setObjectSpeed(obj,v) or obj.speed = v
  • setObjectDirection(obj,angle,?speed]) or obj.move(angle,?speed)
  • setObjectLife(obj,t) or obj.life = t
  • getObjectLife(obj) or obj.life
  • setObjectFrame(obj,i) or obj.frame = i
  • getObjectFrame(obj) or obj.frame
  • getObjects(type)
  • getPlayers()
  • getPlayer(pid)
  • getPlayerName(p)
  • getPlayerCountry(p)
  • getPlayerColor(p)
  • getCurrentPlayer() returns null on headless host
  • getWeapon(id)
  • getPlayerActiveWeapon(p,id)
  • getPlayerWeapons(p)
  • getOwner(obj)
  • setOwner(obj,pid)
  • hit(obj,pid,dmg)
  • setPlayerHealth(pid,hp)
  • setPlayerVelocity(pid,vx,vy)
  • showOverlayMessage(pid,msg,color,duration)
  • playSound(id)
  • isTeam()
  • pack(obj,fields,values)
  • unpack(obj,fields,default)
  • random()
  • randomSpawn(?dist=20)
  • sin()
  • cos()
  • log(o)

Player object

  • id
  • x
  • y
  • vx
  • vy
  • aimAngle
  • direction
  • hp
  • team
  • color?

Weapon object

  • type (ref to a weapontype object {id,name})
  • ammo (current ammo, set to -1 for full reload)
  • reload (current reload time, 0 = not reloading)

Level object

  • width
  • height
  • data

Guidelines and FAQ

  • The mod name must start with a dollar symbol ($). This trick will unlock behaviors in the mod, packing/unpacking them in the game state. A necessary evil to keep compatibility with normal rooms.
  • Because these scripts can spawn other objects via createObject, you must use a special prefix to make merging mods possible:
    Instead of {w:8}, write {w:WID_8}. This will indicate that you actively use a wObject, and the prefix will be removed at runtime.
    Prefixes are WID/NID/SID for objects and SNDID for sounds.
  • this.data is a 8 bits int property packed in the game state. It can be used to store an eventual custom state for the object, allowing implementation of more advanced behaviors (counters, health..).
    NEVER SET this.data to a negative value as it is reserved for api.pack(), a convenient method to store multiple variables in the game state (up to 128 bytes).
  • If you don't use api.pack(), your code should follow the guard design pattern, using return; whenever possible to short-circuit behavior's execution and avoid nested conditionals.
  • createObject() returns the created object, on which you can apply ``api.unpack()`` and pass initial parameters to created object.
  • ``life`` is the number of frames elapsed since object was created, it will be 0 if timeToExplo is 0, and it will start with a value between 0 and timeToExploV
  • The mod itself can be modified at runtime, by changing directly the properties of the objects' (and weapons) types! Warning minified territory until appropriate getters implemented.

Callbacks

By legacy, behaviors are meant for wObjects, and shall implement the ``update`` callback to handle the wObject (available as ``this``) during its lifetime.

  • update callback is running on each frame
  • postUpdate callback is executed after all objects and worms update(), ideal to attach objects to worms with frame precision
  • init callback is executed only once when the object is created
  • killed callback is executed after the object has exploded or expired

  • There are other callbacks that might be implemented one day for wObject
    • collisionObject(obj) when the object collides with another
    • collisionWorm(p) when a worm collides with the object

Static Callbacks

These are callbacks available in the global mod context, where ``this`` is the behavior itself, not a wObject ;
and in which api.pack(this) also works allowing arbitrary variables to be used for tracking global events as game unravels.

  • onGameStart when the round starts
  • onTick triggered each frame
  • onPlayerSpawn(arg:{player:PlayerObject}) when a worm spawns
  • onPlayerDeath(arg:{player:PlayerObject,damage:int,owner:int,weapon:int}) when a worm dies
  • onPlayerHit(arg:{player:PlayerObject,damage:int,owner:int,weapon:int}) whenever a worm is hit. callback must return final value for damage.
  • onSignal(arg:{?}) when a signal is received

These global callbacks don't require a wObject to be associated with, they just need their behavior to be present in the mod.

It allows scripting at the mod level, in a compositing way: multiple callbacks can be registered (via multiple behaviors) for various desired effects

Signals

The story is set in a not so distant future. Worms have evolved and became greedy. They now worship the gold that can buy them plenty goodies down here in extended world. There are many ways to acquire the precious, by crushing hard other wormies, selling loots or finding those hidden chests lost in dangerous quests.

With his inventory full of the best equipment, shields and magic, Baba feels ready to go on a hunt, finally sure to defeat his ever unlucky rival Salezki who seems to never get a worthy drop.

Signals make it possible for behaviors to communicate with each other, via the api.sendSignal() method. The room script can also catch these signals by registering an onSignal function on the room object, like this:

room.onSignal = arg => {
	if(arg.event=='worm-picked-up-coin'){		
		database.incrementWormGold(arg.playerName, arg.value || 1);
	}
}
			

It is then possible from room script to send a signal containing the database player info when a round starts (or when a player joins), and static behaviors can catch the signal and restore the players persistent statistics, gold and inventory, which could be displayed in-game using other behaviors. The example above uses player names, but via the player signatures available in webliero, persistent data can be safely attributed to their real players.

Signals Guidelines

  • Don't send a signal in a onSignal callback unless you know exactly what you doin'
    It could create infinite signal loops which will freeze the game
  • When the room script calls room.sendSignal(), the signal will also be received by the room.onSignal callback
  • It is recommended to send complementary info in a signal, to be able to know its context when it is received. For instance:
    room.onSignal = arg => {
    	if(arg.isRoomSignal) return;
    	// here we have only signals coming from behaviors
    }
    room.sendSignal({
    	event:'foo',
    	isRoomSignal:true
    })
    			
  • When implementing interactive interfaces with api.setUI(), inside click handlers you must not use api.sendSignal(arg) but api.sendAction(arg) instead. It will also trigger the onSignal callback, and arg.player will be set automatically with the id of the player that triggered the action.

Demo weapons

  • BLOODY MIRACLE
    Survive bleeding for 7 seconds to get fullhealth back
  • DIRT PROTECTOR
    A familiar firing dirt at ennemies
  • HELL BARON
    A familiar Crackler granting temporary regeneration
  • LAST WISH GRENADE
    A familiar grenade exploding when you die
  • MEDIKIT FRIEND
    A familiar spawning health packs
  • REGEN RUNE
    A familiar regenerating 1/hp second. can be picked up by other worms on owner death
  • TURRET MINE
    A mine that fires rockets poison darts at closest worm
  • ZIMM SHIELD
    A zimm orbiting around its owner
  • CAMPER HUNTER
    a bot targeting campers
  • FAN SHIELD
    an orbiting fan shield
  • POISON DART
    A dart that stays stuck on worm and inflicts periodic damage
  • LOOTBOX
    gives one of the demo abilities on pickup
  • SPIKEBALL MAGNET
    spikes following owner while respecting terrain collisions
  • METROID LAUNCHER
    spawn a metroid hunting close worms and killable too
  • HERETIC POD
    a perpetual organic explosive that bleeds toxic pixels and explodes after taking enough damage

Incoming concepts (ideas list)

  • RESPAWN RUNE
    set your next respawn point
  • BULLET TIME
    slow down bullets coming towards owner
  • DEATH AURA
    deal 1hp/s damage to surrounding ennemies
  • PORTAL GUN
    (ZaRR)
  • WOJTEK BEAR
    (dsds)
  • DAMAGE BOOSTER
    Temporary damage multiplier
  • SHIELD
    Real shield with true damage reduction

Todo

  • signed script hashes for trusted execution
  • make wids a uint32
  • webrobal