152 lines
5.0 KiB
JavaScript
152 lines
5.0 KiB
JavaScript
import { Locator } from "@popiplay/slot-game-kit";
|
|
import { SKIN_TYPES } from "../src/config";
|
|
|
|
/**
|
|
* Creates a callback function that executes a specific callback after a defined count of invocations.
|
|
*
|
|
* @param {number} n - The count threshold that triggers the callback execution.
|
|
* @param {Function} callback - The function to execute after `n` invocations. Defaults to an empty function.
|
|
* @param {Function} eachCallback - The function to execute on each invocation. Defaults to an empty function.
|
|
* @return {Function} A new function that increments a count each time it's called, executes `eachCallback`,
|
|
* and if the count equals `n`, executes `callback` and resets the count.
|
|
*
|
|
* @example
|
|
* // Logs 'Hello, third time!' every third invocation
|
|
* const logEveryTime = (...args) => console.log('Called with', args);
|
|
* const sayHelloEveryThirdTime = () => console.log('Hello, third time!');
|
|
* const thresholdCallback = createThresholdCallback(3, sayHelloEveryThirdTime, logEveryTime);
|
|
*
|
|
* thresholdCallback('test'); // Logs 'Called with ["test"]'
|
|
* thresholdCallback('again'); // Logs 'Called with ["again"]'
|
|
* thresholdCallback('and again'); // Logs 'Called with ["and again"]' and 'Hello, third time!'
|
|
* thresholdCallback('another one'); // Logs 'Called with ["another one"]'
|
|
*
|
|
*/
|
|
export function createThresholdCallback(
|
|
n,
|
|
callback = () => { },
|
|
eachCallback = () => { }
|
|
) {
|
|
let counter = 0;
|
|
return function (...args) {
|
|
counter++;
|
|
eachCallback(...args);
|
|
if (n === counter) {
|
|
counter = 0;
|
|
callback(...args);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Asynchronously waits for a specified event to be emitted once on the target object.
|
|
*
|
|
* @async
|
|
* @param {EventEmitter} target - The EventEmitter object on which to listen for the event.
|
|
* @param {string} event - The name of the event to listen for.
|
|
* @return {Promise<void>} A promise that resolves when the specified event is emitted on the target.
|
|
*
|
|
* Usage:
|
|
* ```javascript
|
|
* await waitForEventOnce(someObject, 'eventName');
|
|
* // Following code won't execute until 'eventName' is emitted on 'someObject' for the first time.
|
|
* ```
|
|
*/
|
|
export async function waitForEventOnce(target, event) {
|
|
return new Promise((resolve) => {
|
|
target.once(event, () => {
|
|
resolve()
|
|
});
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Asynchronously waits for the first event from a list of events to be emitted.
|
|
*
|
|
* @async
|
|
* @param {Array<{ target: EventEmitter, event: string }>} events - An array of objects, each containing an EventEmitter object and the event to listen for.
|
|
* @return {Promise<void>} A Promise that resolves as soon as one of the specified events is emitted on its respective EventEmitter.
|
|
*
|
|
* Usage:
|
|
* ```javascript
|
|
* const events = [
|
|
* { target: someObject1, event: 'eventName1'},
|
|
* { target: someObject2, event: 'eventName2'}
|
|
* ];
|
|
*
|
|
* await waitForFirstEmittedEvent(events);
|
|
* // Following code won't execute until either 'eventName1' is emitted on 'someObject1' or 'eventName2' is emitted on 'someObject2' for the first time.
|
|
* ```
|
|
*/
|
|
export async function waitForFirstEmittedEvent(events) {
|
|
const promises = events.map(({ target, event }) => {
|
|
return waitForEventOnce(target, event);
|
|
})
|
|
return Promise.race(promises)
|
|
}
|
|
|
|
export function capitalizeFirstLetter(str) {
|
|
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
|
}
|
|
|
|
export const transpose = (matrix) => matrix[0].map((_, i) => matrix.map(row => row[i]));
|
|
|
|
export const isObject = (obj) => obj && typeof obj === 'object' && !Array.isArray(obj);
|
|
|
|
/**
|
|
* @description - Deeply merges two objects.
|
|
*
|
|
* @param {Object} target - The target object to merge properties into.
|
|
* @param {Object} source - The source object from which to copy properties.
|
|
* @return {Object} The merged object.
|
|
*
|
|
* @example
|
|
* const obj1 = { a: 1, b: { c: 2 } };
|
|
* const obj2 = { b: { d: 3 }, e: 4 };
|
|
* const result = deepMerge(obj1, obj2);
|
|
* // result is { a: 1, b: { c: 2, d: 3 }, e: 4 }
|
|
*/
|
|
export function deepMerge(target, source) {
|
|
if (!isObject(target) || !isObject(source)) {
|
|
return source;
|
|
}
|
|
|
|
const merged = { ...target };
|
|
|
|
Object.keys(source).forEach(key => {
|
|
if (isObject(source[key])) {
|
|
if (!target[key]) {
|
|
merged[key] = source[key];
|
|
} else {
|
|
merged[key] = deepMerge(target[key], source[key]);
|
|
}
|
|
} else {
|
|
merged[key] = source[key];
|
|
}
|
|
});
|
|
|
|
return merged;
|
|
}
|
|
|
|
export const getSkinType = (threshhold) => {
|
|
return Locator.viewport.cropWidthRatio < threshhold
|
|
? SKIN_TYPES.PORT
|
|
: SKIN_TYPES.LAND;
|
|
}
|
|
|
|
export const getStyle = (styleTempl, dynamicStyle, threshhold) => {
|
|
const skinType = getSkinType(threshhold);
|
|
const hasDynamicStyle = !!dynamicStyle
|
|
&& typeof dynamicStyle === 'object'
|
|
&& !!Object.entries(dynamicStyle).length;
|
|
const additionalStyles = dynamicStyle?.[skinType] || dynamicStyle;
|
|
|
|
return hasDynamicStyle ? deepMerge(styleTempl, additionalStyles) : styleTempl;
|
|
}
|
|
|
|
export const getRandomInt = (min, max) => {
|
|
min = Math.ceil(min);
|
|
max = Math.floor(max);
|
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
}
|