Skip to main content

Energy

You can configure automatic variables that will be accrued according to the server time, which can be set with upper and lower limits.

Examples:

Configuration

The concept of energy involves setting the maximum and minimum values of a player's field, as well as the interval for automatically adding the variable value.

More about configuring player fields:

More about working with Player State API.

Energy / Lives

To implement lives, you need to create a energy field for the player in the Players section on the project management panel as shown in the screenshot:

In this example, Energy:

  • Is available within the range from 0 to 100 (Limit by range);
  • Starts full upon first entry (Default value);
  • Adds 1 unit of energy every 60 seconds (Automatically incremented variable);
  • Can be manually set above 100.

Checking energy at level start

function onClickButtonStartLevel() {
// Not enough energy
if (ss.player.get('energy') <= 0) {
// Show modal "Not enough energy"
showNotEnoughEnergyModal();
return;
}

// Subtract 1 unit of energy
ss.player.add('energy', -1);
// Save the deduction to persist after reload
ss.player.sync();
// Start the level
startLevel();
}

Bonus energy

Add a certain amount of energy manually as a bonus, for example, when using an item, unlocking an achievement, or making a purchase. If the variable can be set above the maximum, in the following example 10 energy will be added even if the energy is full.

// Add 10 units of energy manually as a bonus
async function addMoreEnergy() {
// Add 10 units of energy
ss.player.add('energy', 10);
// Save
ss.player.sync();
}

Refill energy by watching ads

async function onClickButtonRefillEnergy() {
const success = await ss.ads.showRewardedVideo();
if (!success) {
// Failed to show / complete the ad
return;
}

// Restore all missing energy
ss.player.set('energy', ss.player.getMaxValue('energy'));

// Save
ss.player.sync();
}

Increase energy capacity

Increase the maximum energy capacity. Let's consider the example of a purchase.

async function onClickButtonIncreaseEnergy() {
// Purchase energy increase
await ss.payments.purchase({ tag: 'ENERGY_100' });

// Add 100 to the maximum energy value
ss.player.add('energy:max', 100);
// Replenish energy reserve
ss.player.set('energy', ss.player.getMaxValue('energy'));

// Save
await ss.player.sync();

// Consume the purchase
await ss.payments.consume({ tag: 'GOLD_1000' });
}

Accelerated energy restoration

Energy restoration can be accelerated by either reducing the increment interval or increasing the amount of energy restored per iteration. Let's consider the example of purchasing VIP membership.

Increase the amount of energy restored per interval:

// If the player has a VIP purchase, 2 units of energy are restored per minute instead of 1
async function onGameStart() {
// Set the amount of energy restored per iteration
if (ss.payments.has('VIP')) {
ss.player.set('energy:incrementValue', 2);
} else {
ss.player.set('energy:incrementValue', 1);
}
}

Decrease the energy restoration time:

// If the player has a VIP purchase, 1 unit of energy is restored every 30 seconds instead of 1 minute
async function onGameStart() {
// Set the increment interval
if (ss.payments.has('VIP')) {
ss.player.set('energy:incrementInterval', 30);
} else {
ss.player.set('energy:incrementInterval', 60);
}
}

Get time until restoration

To display how much time is left until the next energy restoration in the interface:

Method:

// Difference in seconds until the next energy restoration
ss.player.get('energy:secondsLeft');

Example:

// Time until the next energy restoration in a human-readable format like 05:47
function getSecondsLeftHuman() {
const secondsLeft = ss.player.get('energy:secondsLeft');
return formatTime(secondsLeft);
}

// Format into a human-readable format like 00:00
function formatTime(seconds) {
const minutes = Math.floor((seconds % 3600) / 60);
const remainingSeconds = seconds % 60;
return `${pad(minutes)}:${pad(remainingSeconds)}`;
}

// Add leading zeros
function pad(num) {
return String(num).padStart(2, '0');
}

Find out the time until full restoration:

Method:

// Difference in seconds until full restoration
ss.player.get('energy:secondsLeftTotal');

Example:

// Time until full restoration in a human-readable format like 05:47
function getSecondsLeftTotalHuman() {
const secondsLeftTotal = ss.player.get('energy:secondsLeftTotal');
// Return in a human-readable format
return formatTime(secondsLeftTotal);
}

Rewards / Boxes (Time-based)

You can implement a mechanism for giving rewards / chests based on a renewable timer.

For example:

  • Common box every hour from the time of opening;
  • Rare box every 6 hours from the time of opening;
  • Epic box every 24 hours from the time of opening;

To implement rewards every hour, you need to create a field common-boxes for the player in the Players section in the project management panel as shown in the screenshot:

Energy Boxes

In this example, Common Boxes:

  • No more than 1 box is available within a period (Limiting the range);
  • Upon the first login, there are 0 boxes (Default value), the first one will be available after 1 hour;
  • Every 3600 seconds (1 hour), 1 box is added (Automatically assigned variable);
  • You can set manually more than 1.

Check Readiness

function refreshRewardsUI() {
// There are boxes to open
if (ss.player.get('common-boxes') > 0) {
// Enable the open button
buttonOpenCommonBox.enable();
} else {
// Disable the open button
buttonOpenCommonBox.disable();
}
}

Bonus Rewards

Manually award a certain amount of rewards as a bonus, for example, upon level completion or boss defeat. If it's possible to set a value higher than the maximum, in the example below, 1 box will be added even if there is already an unopened box.

// Manually give 1 common box as a bonus
async function giveCommonBox() {
// Add 1 common box
ss.player.add('common-boxes', 1);
// Save
ss.player.sync();
}

Acquire boxes through purchase:

// Award boxes for purchase
async function buyCommonBoxPack() {
// Purchase a box pack
await ss.payments.purchase({ tag: 'BOXES_PACK' });

// Add 10 common boxes
ss.player.add('common-boxes', 10);
// Add 3 rare boxes
ss.player.add('rare-boxes', 3);
// Add 1 epic box
ss.player.add('epic-boxes', 1);

// Save
await ss.player.sync();

// Mark the purchase as consumed
await ss.payments.consume({ tag: 'BOXES_PACK' });
}

Get Time Until Opening

To display in the interface how much time is left until the next box can be opened:

Method:

// Difference in seconds until the next opening
ss.player.get('common-boxes:secondsLeft');

Example:

// Amount of time until the next opening in human-readable format 01:37:42
function getSecondsLeftHuman() {
const secondsLeft = ss.player.get('common-boxes:secondsLeft');
return formatTime(secondsLeft);
}

// Format into human-readable format 00:00:00
function formatTime(seconds) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const remainingSeconds = seconds % 60;
return `${pad(hours)}:${pad(minutes)}:${pad(remainingSeconds)}`;
}

// Adding leading zeros
function pad(num) {
return String(num).padStart(2, '0');
}

Speed Up Opening with Currency

Example of exchanging in-game currency for instant opening. In the example, we exchange the player's gems for speeding up the box opening. The exchange rate will depend on the remaining time until opening.

// Get the cost of instant opening
function getOpenCost() {
// Exchange rate of gems to seconds of opening
// 1 crystal = 3 minutes (180 seconds)
const gemsRate = 1 / 180;
// Remaining seconds until opening
const secondsLeft = ss.player.get('common-boxes:secondsLeft');
// Calculate the opening cost
return Math.ceil(secondsLeft * gemsRate);
}

// When "Open for gems" button is clicked
function onButtonInstantOpen() {
// Get the opening cost
const price = getOpenCost();

// Opening time has come, box is already available
if (price === 0) {
// Hide the button
hideInstantOpenButton();
return;
}

// Insufficient gems
if (ss.player.get('gems') < price) {
showModalNotEnoughGems();
return;
}

// Deduct the cost from the player's gems
ss.player.add('gems', -price);
// Add the box
ss.player.add('common-boxes', 1);
// Save the player
ss.player.sync();
}

Offline Piggy Bank

You can implement a mechanism to accumulate in-game currency even while the player is offline with the possibility of expanding the piggy bank capacity.

For the offline piggy bank, you need to create a player field offline-piggy-bank in the Players section of the project management panel as shown in the screenshot:

In this example of the Offline Piggy Bank:

  • Capacity no more than 1000 gold by default (Limit the range);
  • Upon first login, 0 gold (Default value);
  • Every 1 second, 1 gold is added (Automatically incremented variable).

Empty the Piggy Bank

function usePiggyBank() {
// Get the amount of accumulated gold
const rewardGold = ss.player.get('offline-piggy-bank');
// Add gold to the player
ss.player.add('gold', rewardGold);
// Empty the piggy bank
ss.player.set('offline-piggy-bank', 0);
// Save
ss.player.sync();
}

Minimum Time to Open

You can allow the player to empty the piggy bank, for example, no more frequently than once every 5 minutes. The example will also calculate the number of seconds left until opening.

// Check if the piggy bank can be used
function canUsePiggyBank() {
// Find out how much time is left until the piggy bank is fully filled
const secondsToRefill = ss.player.get('offline-piggy-bank:secondsLeftTotal');
// Refill period (1 second in the example)
const iterationTime = ss.player.get('offline-piggy-bank:incrementInterval');
// Income per iteration (1 gold in the example)
const incomePerIteration = ss.player.get('offline-piggy-bank:incrementValue');
// Maximum possible amount of gold in the piggy bank (1000 in the example)
const bankCapacity = ss.player.get('offline-piggy-bank:max');

// Calculate how many iterations are needed to fill the piggy bank from zero
const iterationsToRefillFromEmpty = Math.ceil(bankCapacity / incomePerIteration);
// Calculate how many seconds are needed to fill the piggy bank from zero
const secondsToRefillFromEmpty = iterationTime * iterationsToRefillFromEmpty;
// Calculate how many seconds are left until opening, requirement - at least 5 minutes have passed
const secondsLeft = secondsToRefill - (secondsToRefillFromEmpty - 5 * 60);

// Can open if no time is left (at least 5 minutes have passed)
return secondsLeft <= 0;
}

Increase Piggy Bank Capacity

You can increase the maximum limit of the piggy bank. This is useful when the game has a progression system, such as upgrading the piggy bank.

// Increase piggy bank capacity
async function upgradePiggyBankCapacity() {
// Current capacity of the piggy bank
const currentCapacity = ss.player.get('offline-piggy-bank:max');
// Double the capacity of the piggy bank for each upgrade
ss.player.set('offline-piggy-bank:max', 2 * currentCapacity);
// Save
ss.player.sync();
}

Increase Piggy Bank Income

You can increase the amount of gold replenished per unit of time. This is useful when the game has a progression system, such as upgrading the piggy bank.

// Increase gold earned per second
async function upgradePiggyBankIncome() {
// Increase income per iteration by 1 gold for each upgrade
ss.player.add('offline-piggy-bank:incrementValue', 1);
// Save
ss.player.sync();
}

Loyalty Points

Loyalty Points - an indicator of player activity. It's constantly decreases and need to be maintained at a certain level to receive additional loyalty bonuses. This mechanic helps encourage players to participate in activities and complete more levels to not lose privileges.

For example, you can create a table of privileges:

LevelLoyalty PointsBonuses
Level 11000+10% gold per level
Level 25000+10% experience per level
Level 310000+10 diamonds per level
Level 450000SpellSync Mug (one-time)
+100% gold per level

For loyalty points, you need to create a player field loyalty in the Players section of the project management panel as shown in the screenshot:

In this example of Loyalty:

  • Cannot drop below 0 (Limit the range);
  • Upon first login, 0 loyalty points (Default value);
  • Every 1 hour (3600 seconds), 100 points are deducted (Automatically decremented variable).

Granting Privileges

At any time, you can refer to loyalty points to assign bonuses.

// Gold bonus for completing a level, depends on loyalty points
function goldBonusMultiplier() {
const loyalty = ss.player.get('loyalty');

// Base multiplier of 100% gold
let multiplier = 1;

// +10% when reaching 1,000 points
if (loyalty >= 1000) {
multiplier += 0.1;
}

// +100% when reaching 50,000 points
if (loyalty >= 50000) {
multiplier += 1;
}

return multiplier;
}

// Upon completing a level
function onLevelComplete() {
// Add 500 gold to the player considering the loyalty bonus multiplier
ss.player.add('gold', 500 * goldBonusMultiplier());
// Save
ss.player.sync();
}

Adding Loyalty Points

Example of rewarding a player by adding loyalty points for completing a level.

async function onLevelComplete() {
// Increase loyalty points by 100
ss.player.add('loyalty', 100);
// Save
ss.player.sync();
}

Example of rewarding a player by adding loyalty points for any purchase.

// Purchased any item
ss.payments.on('purchase', () => {
// Increase loyalty points by 1000
ss.player.add('loyalty', 1000);
// Player save will be within the purchase handling
});

Example of rewarding a player by adding loyalty points for watching an ad.

// Player watched a rewarded ad
ss.ads.on('rewarded:reward', () => {
// Increase loyalty points by 200
ss.player.add('loyalty', 200);
// Player save will be within the ad call handling
});

Freeze Loyalty Decrease

The ability to stop losing loyalty points will be an additional incentive for the player to make a purchase. For example, you can offer a "Loyalty Pass (7 days)" which allows stopping the decrease of loyalty points for 7 days. Or offer this option as part of a "VIP" status.

Let's consider an example with a subscription to the loyalty pass.

// Buy a loyalty pass
async function buyLoyaltyPass() {
// Subscribe to the loyalty pass
await ss.payments.subscribe({ tag: 'LOYALTY_PASS' });
// Freeze the decrease of loyalty points
ss.player.set('loyalty:incrementValue', 0);
// Save
ss.player.sync();
}

// Upon game start
async function onGameStart() {
// Check if the decrease of loyalty points is frozen
const isLoyaltyFrozen = ss.player.get('loyalty');

// Check for the presence of a subscription
if (ss.payments.has('LOYALTY_PASS')) {
// Subscription exists, but loyalty is not frozen
// There may have been issues with processing the purchase
if (!isLoyaltyFrozen) {
// Freeze the decrease of loyalty points
ss.player.set('loyalty:incrementValue', 0);
// Save
await ss.player.sync();
}
} else if (isLoyaltyFrozen) {
// Subscription expired but loyalty is still frozen

// Unfreeze the decrease of loyalty points
ss.player.set('loyalty:incrementValue', -100);
// Save
await ss.player.sync();
}
}

Stay in Touch

Other documents of this chapter available Here. To get started, welcome to the Tutorials chapter.

SpellSync Community Telegram: @spellsync.

For your suggestions e-mail: [email protected]

We Wish you Success!