achievements-engine 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +271 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +143 -3
- package/dist/index.esm.js +270 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.umd.js +271 -0
- package/dist/index.umd.js.map +1 -1
- package/package.json +5 -2
package/dist/index.cjs
CHANGED
|
@@ -1675,6 +1675,276 @@ class OfflineQueueStorage {
|
|
|
1675
1675
|
}
|
|
1676
1676
|
}
|
|
1677
1677
|
|
|
1678
|
+
/**
|
|
1679
|
+
* Base class for chainable achievement configuration (Tier 2)
|
|
1680
|
+
*/
|
|
1681
|
+
class Achievement {
|
|
1682
|
+
constructor(metric, defaultAward) {
|
|
1683
|
+
this.metric = metric;
|
|
1684
|
+
this.award = defaultAward;
|
|
1685
|
+
}
|
|
1686
|
+
/**
|
|
1687
|
+
* Customize the award details for this achievement
|
|
1688
|
+
* @param award - Custom award details
|
|
1689
|
+
* @returns This achievement for chaining
|
|
1690
|
+
*/
|
|
1691
|
+
withAward(award) {
|
|
1692
|
+
this.award = Object.assign(Object.assign({}, this.award), award);
|
|
1693
|
+
return this;
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
/**
|
|
1697
|
+
* Threshold-based achievement (score, level, etc.)
|
|
1698
|
+
*/
|
|
1699
|
+
class ThresholdAchievement extends Achievement {
|
|
1700
|
+
constructor(metric, threshold, defaultAward) {
|
|
1701
|
+
super(metric, defaultAward);
|
|
1702
|
+
this.threshold = threshold;
|
|
1703
|
+
}
|
|
1704
|
+
toConfig() {
|
|
1705
|
+
return {
|
|
1706
|
+
[this.metric]: {
|
|
1707
|
+
[this.threshold]: {
|
|
1708
|
+
title: this.award.title,
|
|
1709
|
+
description: this.award.description,
|
|
1710
|
+
icon: this.award.icon
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
/**
|
|
1717
|
+
* Boolean achievement (tutorial completion, first login, etc.)
|
|
1718
|
+
*/
|
|
1719
|
+
class BooleanAchievement extends Achievement {
|
|
1720
|
+
toConfig() {
|
|
1721
|
+
return {
|
|
1722
|
+
[this.metric]: {
|
|
1723
|
+
true: {
|
|
1724
|
+
title: this.award.title,
|
|
1725
|
+
description: this.award.description,
|
|
1726
|
+
icon: this.award.icon
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
/**
|
|
1733
|
+
* Value-based achievement (character class, difficulty, etc.)
|
|
1734
|
+
*/
|
|
1735
|
+
class ValueAchievement extends Achievement {
|
|
1736
|
+
constructor(metric, value, defaultAward) {
|
|
1737
|
+
super(metric, defaultAward);
|
|
1738
|
+
this.value = value;
|
|
1739
|
+
}
|
|
1740
|
+
toConfig() {
|
|
1741
|
+
return {
|
|
1742
|
+
[this.metric]: {
|
|
1743
|
+
[this.value]: {
|
|
1744
|
+
title: this.award.title,
|
|
1745
|
+
description: this.award.description,
|
|
1746
|
+
icon: this.award.icon
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
/**
|
|
1753
|
+
* Complex achievement builder for power users (Tier 3)
|
|
1754
|
+
*/
|
|
1755
|
+
class ComplexAchievementBuilder {
|
|
1756
|
+
constructor() {
|
|
1757
|
+
this.metric = '';
|
|
1758
|
+
this.condition = null;
|
|
1759
|
+
this.award = {};
|
|
1760
|
+
}
|
|
1761
|
+
/**
|
|
1762
|
+
* Set the metric this achievement tracks
|
|
1763
|
+
*/
|
|
1764
|
+
withMetric(metric) {
|
|
1765
|
+
this.metric = metric;
|
|
1766
|
+
return this;
|
|
1767
|
+
}
|
|
1768
|
+
/**
|
|
1769
|
+
* Set the condition function that determines if achievement is unlocked
|
|
1770
|
+
*/
|
|
1771
|
+
withCondition(fn) {
|
|
1772
|
+
this.condition = fn;
|
|
1773
|
+
return this;
|
|
1774
|
+
}
|
|
1775
|
+
/**
|
|
1776
|
+
* Set the award details for this achievement
|
|
1777
|
+
*/
|
|
1778
|
+
withAward(award) {
|
|
1779
|
+
this.award = Object.assign(Object.assign({}, this.award), award);
|
|
1780
|
+
return this;
|
|
1781
|
+
}
|
|
1782
|
+
/**
|
|
1783
|
+
* Build the final achievement configuration
|
|
1784
|
+
*/
|
|
1785
|
+
build() {
|
|
1786
|
+
if (!this.metric || !this.condition) {
|
|
1787
|
+
throw new Error('Complex achievement requires metric and condition');
|
|
1788
|
+
}
|
|
1789
|
+
return {
|
|
1790
|
+
[this.metric]: {
|
|
1791
|
+
custom: {
|
|
1792
|
+
title: this.award.title || this.metric,
|
|
1793
|
+
description: this.award.description || `Achieve ${this.award.title || this.metric}`,
|
|
1794
|
+
icon: this.award.icon || '💎',
|
|
1795
|
+
condition: this.condition
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
};
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
/**
|
|
1802
|
+
* Main AchievementBuilder with three-tier API
|
|
1803
|
+
* Tier 1: Simple static methods with smart defaults
|
|
1804
|
+
* Tier 2: Chainable customization
|
|
1805
|
+
* Tier 3: Full builder for complex logic
|
|
1806
|
+
*/
|
|
1807
|
+
class AchievementBuilder {
|
|
1808
|
+
// TIER 1: Simple Static Methods (90% of use cases)
|
|
1809
|
+
/**
|
|
1810
|
+
* Create a single score achievement with smart defaults
|
|
1811
|
+
* @param threshold - Score threshold to achieve
|
|
1812
|
+
* @returns Chainable ThresholdAchievement
|
|
1813
|
+
*/
|
|
1814
|
+
static createScoreAchievement(threshold) {
|
|
1815
|
+
return new ThresholdAchievement('score', threshold, {
|
|
1816
|
+
title: `Score ${threshold}!`,
|
|
1817
|
+
description: `Score ${threshold} points`,
|
|
1818
|
+
icon: '🏆'
|
|
1819
|
+
});
|
|
1820
|
+
}
|
|
1821
|
+
/**
|
|
1822
|
+
* Create multiple score achievements
|
|
1823
|
+
* @param thresholds - Array of thresholds or [threshold, award] tuples
|
|
1824
|
+
* @returns Complete SimpleAchievementConfig
|
|
1825
|
+
*/
|
|
1826
|
+
static createScoreAchievements(thresholds) {
|
|
1827
|
+
const config = { score: {} };
|
|
1828
|
+
thresholds.forEach(item => {
|
|
1829
|
+
if (typeof item === 'number') {
|
|
1830
|
+
// Use default award
|
|
1831
|
+
config.score[item] = {
|
|
1832
|
+
title: `Score ${item}!`,
|
|
1833
|
+
description: `Score ${item} points`,
|
|
1834
|
+
icon: '🏆'
|
|
1835
|
+
};
|
|
1836
|
+
}
|
|
1837
|
+
else {
|
|
1838
|
+
// Custom award
|
|
1839
|
+
const [threshold, award] = item;
|
|
1840
|
+
config.score[threshold] = {
|
|
1841
|
+
title: award.title || `Score ${threshold}!`,
|
|
1842
|
+
description: award.description || `Score ${threshold} points`,
|
|
1843
|
+
icon: award.icon || '🏆'
|
|
1844
|
+
};
|
|
1845
|
+
}
|
|
1846
|
+
});
|
|
1847
|
+
return config;
|
|
1848
|
+
}
|
|
1849
|
+
/**
|
|
1850
|
+
* Create a single level achievement with smart defaults
|
|
1851
|
+
* @param level - Level threshold to achieve
|
|
1852
|
+
* @returns Chainable ThresholdAchievement
|
|
1853
|
+
*/
|
|
1854
|
+
static createLevelAchievement(level) {
|
|
1855
|
+
return new ThresholdAchievement('level', level, {
|
|
1856
|
+
title: `Level ${level}!`,
|
|
1857
|
+
description: `Reach level ${level}`,
|
|
1858
|
+
icon: '📈'
|
|
1859
|
+
});
|
|
1860
|
+
}
|
|
1861
|
+
/**
|
|
1862
|
+
* Create multiple level achievements
|
|
1863
|
+
* @param levels - Array of levels or [level, award] tuples
|
|
1864
|
+
* @returns Complete SimpleAchievementConfig
|
|
1865
|
+
*/
|
|
1866
|
+
static createLevelAchievements(levels) {
|
|
1867
|
+
const config = { level: {} };
|
|
1868
|
+
levels.forEach(item => {
|
|
1869
|
+
if (typeof item === 'number') {
|
|
1870
|
+
// Use default award
|
|
1871
|
+
config.level[item] = {
|
|
1872
|
+
title: `Level ${item}!`,
|
|
1873
|
+
description: `Reach level ${item}`,
|
|
1874
|
+
icon: '📈'
|
|
1875
|
+
};
|
|
1876
|
+
}
|
|
1877
|
+
else {
|
|
1878
|
+
// Custom award
|
|
1879
|
+
const [level, award] = item;
|
|
1880
|
+
config.level[level] = {
|
|
1881
|
+
title: award.title || `Level ${level}!`,
|
|
1882
|
+
description: award.description || `Reach level ${level}`,
|
|
1883
|
+
icon: award.icon || '📈'
|
|
1884
|
+
};
|
|
1885
|
+
}
|
|
1886
|
+
});
|
|
1887
|
+
return config;
|
|
1888
|
+
}
|
|
1889
|
+
/**
|
|
1890
|
+
* Create a boolean achievement with smart defaults
|
|
1891
|
+
* @param metric - The metric name (e.g., 'completedTutorial')
|
|
1892
|
+
* @returns Chainable BooleanAchievement
|
|
1893
|
+
*/
|
|
1894
|
+
static createBooleanAchievement(metric) {
|
|
1895
|
+
// Convert camelCase to Title Case
|
|
1896
|
+
const formattedMetric = metric.replace(/([A-Z])/g, ' $1').toLowerCase();
|
|
1897
|
+
const titleCase = formattedMetric.charAt(0).toUpperCase() + formattedMetric.slice(1);
|
|
1898
|
+
return new BooleanAchievement(metric, {
|
|
1899
|
+
title: `${titleCase}!`,
|
|
1900
|
+
description: `Complete ${formattedMetric}`,
|
|
1901
|
+
icon: '✅'
|
|
1902
|
+
});
|
|
1903
|
+
}
|
|
1904
|
+
/**
|
|
1905
|
+
* Create a value-based achievement with smart defaults
|
|
1906
|
+
* @param metric - The metric name (e.g., 'characterClass')
|
|
1907
|
+
* @param value - The value to match (e.g., 'wizard')
|
|
1908
|
+
* @returns Chainable ValueAchievement
|
|
1909
|
+
*/
|
|
1910
|
+
static createValueAchievement(metric, value) {
|
|
1911
|
+
const formattedValue = value.charAt(0).toUpperCase() + value.slice(1);
|
|
1912
|
+
return new ValueAchievement(metric, value, {
|
|
1913
|
+
title: `${formattedValue}!`,
|
|
1914
|
+
description: `Choose ${formattedValue.toLowerCase()} for ${metric}`,
|
|
1915
|
+
icon: '🎯'
|
|
1916
|
+
});
|
|
1917
|
+
}
|
|
1918
|
+
// TIER 3: Full Builder for Complex Logic
|
|
1919
|
+
/**
|
|
1920
|
+
* Create a complex achievement builder for power users
|
|
1921
|
+
* @returns ComplexAchievementBuilder for full control
|
|
1922
|
+
*/
|
|
1923
|
+
static create() {
|
|
1924
|
+
return new ComplexAchievementBuilder();
|
|
1925
|
+
}
|
|
1926
|
+
// UTILITY METHODS
|
|
1927
|
+
/**
|
|
1928
|
+
* Combine multiple achievement configurations
|
|
1929
|
+
* @param achievements - Array of SimpleAchievementConfig objects or Achievement instances
|
|
1930
|
+
* @returns Combined SimpleAchievementConfig
|
|
1931
|
+
*/
|
|
1932
|
+
static combine(achievements) {
|
|
1933
|
+
const combined = {};
|
|
1934
|
+
achievements.forEach(achievement => {
|
|
1935
|
+
const config = achievement instanceof Achievement ? achievement.toConfig() : achievement;
|
|
1936
|
+
Object.keys(config).forEach(key => {
|
|
1937
|
+
if (!combined[key]) {
|
|
1938
|
+
combined[key] = {};
|
|
1939
|
+
}
|
|
1940
|
+
Object.assign(combined[key], config[key]);
|
|
1941
|
+
});
|
|
1942
|
+
});
|
|
1943
|
+
return combined;
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
exports.AchievementBuilder = AchievementBuilder;
|
|
1678
1948
|
exports.AchievementEngine = AchievementEngine;
|
|
1679
1949
|
exports.AchievementError = AchievementError;
|
|
1680
1950
|
exports.AsyncStorageAdapter = AsyncStorageAdapter;
|
|
@@ -1695,5 +1965,6 @@ exports.importAchievementData = importAchievementData;
|
|
|
1695
1965
|
exports.isAchievementError = isAchievementError;
|
|
1696
1966
|
exports.isAsyncStorage = isAsyncStorage;
|
|
1697
1967
|
exports.isRecoverableError = isRecoverableError;
|
|
1968
|
+
exports.isSimpleConfig = isSimpleConfig;
|
|
1698
1969
|
exports.normalizeAchievements = normalizeAchievements;
|
|
1699
1970
|
//# sourceMappingURL=index.cjs.map
|