Demo of the Body Guard game was prototyped more than three years ago during Global Game Jam 2018, and right after that tossed into a pile of unfinished projects. Since it is a game where you fight against viruses, we’ve decided to bring it back to life, finish and release it as part of our Web Monetization API experiments, and support Artist Rescue Trust (and Phaser) thanks to the Grant for the Web program as well.
Play BODY GUARD now!
We’ve built the demo in a couple of hours following the transmission theme given by Global Game Jam 2018 organizers, as we liked the concept of defending your body against diseases transmitted by viruses. Now, given the global pandemic, we’ve decided to make Coronavirus our main antagonist - it even looks the same!
Since we’ve been busy with the js13kGames competition and Gamedev.js initiatives (meetups, workshops, jams, etc) for a couple of years now, releasing games wasn’t a top priority anymore. I was doing all those things AND traveling around the world giving talks about WebXR, PWAs, or Web Monetization, but I was constantly missing the actual coding. It’s been four years since the last released game, Flood Escape that came out in May 2017, and I’m happy to say we can now reset the clock!
I had to update the code from Phaser version 2 to 3, similar to how NSHex Roulette was done, where I used Enclave Phaser Template (having the latest Phaser 3 version), moved the gameplay and updated it along the way fixing all the differences between those versions - it went quite smooth and fast.
Grant for the Web and Artist Rescue Trust
As part of our Grant for the Web grant, beside other things, we were going to release three game-related, Web Monetized projects. First was NSHex Roulette, where we monetize the digital expansion of the physical board game. Second is Body Guard, where, since it’s a game where you defend yourself from viruses, we decided to give back and support the fight against Covid-19.
One of the grant recipients is also Artist Rescue Trust, which provide support to musicians and artists affected by the pandemic. It was a perfect case to implement the probabilistic revenue sharing, where Web Monetization API can distribute funds based on a predefined weight.
Web Monetization through probabilistic revenue sharing
It was suppose to be an even split between Artist Rescue Trust and us, Enclave Games, but I got inspired by Björn’s thread on Twitter and decided to toss in Phaser to the mix, since we’re using a free framework to build the game in the first place, so it definitely deserves support as well. The final split is: 40% for Artist Rescue Trust, 30% for Phaser, and 30% for Enclave Games.
This means a player monetized through Coil visiting the website with the Body Guard game will have a 40% chance of having an Artist Rescue Trust’s payment pointer, 30% of Phaser, and 30% Enclave Games. Whenever the website is accessed by a paying user, that session’s amount will go to a preselected recipient. We went even further and allowed anyone to actually change the recipient of the funds collected throughout the gaming session.
Implementation
The probabilistic revenue sharing implementation boils down to this piece of code, taken directly from the documentation and modified to our needs:
const pointers = {
'$ilp.uphold.com/kN2KHpqhNFiM': 40, // Artist Rescue Trust
'$ilp.uphold.com/zdXzL8aWJ4ii': 30, // Phaser
'$ilp.uphold.com/k4eJGQUDF9nw': 30 // Enclave Games
}
function pickPointer() {
const sum = Object.values(pointers).reduce((sum, weight) => sum + weight, 0);
let choice = Math.random() * sum;
for(const pointer in pointers) {
const weight = pointers[pointer];
if((choice -= weight) <= 0) {
return pointer;
}
}
}
The pickPointer
function choses a random pointer based on the weight of it compared to the sum of all the weights, and with our numbers it nicely adds to 100%. Then a selected pointer is added to the (already existing) meta tag:
document.querySelector('meta[name="monetization"]').content = pickPointer();
Body Guard does a little bit more than that though - beside juggling the payment pointer, it also uses the counter example to show the current amount of money sent during this particular session, and the overall amount sent by the player (saved in local storage).
The cool thing about saving the overall amount is that it’s not a single number, but three - for every recipient. This allows us to show the numbers for Artist Rescue Trust, Phaser, and Enclave Games separately. Keep in mind those stats are for this given player using a specific system and the browser, saved locally. If you launch the game on a different device, the stats will be collected independently.
Then, on top of that, it also allows the player to chose between the recipients manually. If the player end up with the Enclave Games payment pointer, but decides to support Phaser instead, they can do so during their playing session.
clickMonetizationPhaser() {
if(this.currentlyActiveMonetization != this.monetizationList[1]) {
this.currentlyActiveMonetization = this.monetizationList[1];
this.clickMonetizationNewPointer('Phaser');
this.countPhaser++;
}
this.monetizationSelectedValue.setText(this.currentlyActiveMonetization);
this.monetizationToggleART.setTexture('button-toggle-off');
this.monetizationTogglePhaser.setTexture('button-toggle-on');
this.monetizationToggleEnclave.setTexture('button-toggle-off');
}
Clicking on the toggle stops the current payment stream, replaces the payment pointer in the head of the document, and restarts the monetization.
clickMonetNewPointer(pointer) {
EPT.Sfx.play('click');
EPT.WebMonetization.pickPointer(pointer);
this.amountART = EPT.Storage.get('BodyGuard-totalAmount-ART');
this.amountPhaser = EPT.Storage.get('BodyGuard-totalAmount-Phaser');
this.amountEnclave = EPT.Storage.get('BodyGuard-totalAmount-Enclave');
this.totalAmount = this.amountART+this.amountPhaser+this.amountEnclave;
}
It also handles the counters to keep track of how many given options were selected, either by randomness or by the player.
savePointerStat: function(pointer) {
var countART = EPT.Storage.get('BodyGuard-chosenTimes-ART');
var countPhaser = EPT.Storage.get('BodyGuard-chosenTimes-Phaser');
var countEnclave = EPT.Storage.get('BodyGuard-chosenTimes-Enclave');
switch(pointer) {
case 'Artist Rescue Trust': {
countART = parseInt(countART)+1;
EPT.Storage.set('BodyGuard-chosenTimes-ART', countART);
break;
}
case 'Phaser': {
countPhaser = parseInt(countPhaser)+1;
EPT.Storage.set('BodyGuard-chosenTimes-Phaser', countPhaser);
break;
}
case 'Enclave Games': {
countEnclave = parseInt(countEnclave)+1;
EPT.Storage.set('BodyGuard-chosenTimes-Enclave', countEnclave);
break;
}
default: {}
}
}
With big enough number of random selections and not too many manual interventions it should end up 40% / 30% / 30% respectively.
Summary
We’re quite happy with how the game turned out, given we liked the core mechanic of the demo and needed a little push to finish it, which Grant for the Web (and the global pandemic) provided.
The Web Monetization “bonus” is rather meta - it’s not giving any benefits to the players within the gameplay, but this is a conscious decision. It’s an experiment to see if people value being offered the control over who their funds support. Would YOU make the decision yourself, or leave this to the algorithm?