Smart Contract
FixesTraits
A.d2abb5dbf5e08666.FixesTraits
1/**
2> Author: Fixes Lab <https://github.com/fixes-world/>
3
4# FixesTraits
5
6This contract mainly includes commonly used Structs related to Asset Traits.
7
8*/
9// Thirdparty Imports
10import MetadataViews from 0x1d7e57aa55817448
11import ViewResolver from 0x1d7e57aa55817448
12
13/// The `FixesTraits` contract
14///
15access(all) contract FixesTraits {
16
17 access(all) entitlement Write;
18
19 /// ============= Trait: Fungible Token =============
20
21 /// The mergeable data interface
22 ///
23 access(all) struct interface MergeableData {
24 /// Get the id of the data
25 access(all)
26 view fun getId(): String
27 /// Get the string value of the data
28 access(all)
29 view fun toString(): String
30 /// Get the data keys
31 access(all)
32 view fun getKeys(): [String]
33 /// Get the value of the data
34 access(all)
35 view fun getValue(_ key: String): AnyStruct?
36 /// Get the writable keys
37 access(all)
38 view fun getWritableKeys(): [String] {
39 return []
40 }
41 /// Set the value of the data
42 access(Write)
43 fun setValue(_ key: String, _ value: AnyStruct) {
44 pre {
45 self.getWritableKeys().contains(key): "The key is not writable"
46 }
47 let writableKeys = self.getWritableKeys()
48 if writableKeys.length == 0 {
49 panic("The gene data cannot be modified by set method")
50 }
51 }
52 /// Split the data into another instance
53 access(Write)
54 fun split(_ perc: UFix64): {MergeableData} {
55 pre {
56 perc > 0.0 && perc <= 1.0: "The percentage should be between 0 and 1"
57 }
58 post {
59 self.getType().identifier == result.getType().identifier: "The data type must be the same"
60 }
61 }
62 /// Merge the data from another instance
63 access(Write)
64 fun merge(_ from: {MergeableData}): Void {
65 pre {
66 self.getType().identifier == from.getType().identifier: "The data type must be the same"
67 }
68 }
69 }
70
71 /// ============= Trait: Season 0 - Secret Garden =============
72
73 /// The Definition of the Marketplace Season 0
74 access(all) enum Season0SecretPlaces: UInt8 {
75 access(all) case HeartOfTheAzureOcean // 蔚蓝海洋之心
76 access(all) case HeartOfTheDarkForest // 黑暗森林之心
77 access(all) case GardenofVenus // 维纳斯的花园
78 access(all) case CityOfTheDead // 亡者之城
79 access(all) case DragonboneWasteland // 龙骨荒原
80 access(all) case MysticForest // 神秘森林
81 access(all) case SoulWaterfall // 灵魂瀑布
82 access(all) case AbyssalHollow // 深渊之穴
83 access(all) case SilentGlacier // 静寂冰川
84 access(all) case FrostWasteland // 霜冻荒原
85 access(all) case DesolateGround // 荒芜之地
86 access(all) case MirageCity // 海市蜃楼
87 access(all) case ScorpionGorge // 蛇蝎峡谷
88 access(all) case MysteriousIceLake // 神秘冰湖
89 access(all) case NightShadowForest // 夜影密林
90 access(all) case SpiritualValley // 灵犀山谷
91 access(all) case RavensPerch // 乌鸦栖息地
92 access(all) case RainbowFalls // 彩虹瀑布
93 access(all) case TwilightValley // 暮色谷地
94 access(all) case RuggedHill // 乱石山岗
95 }
96
97 access(all)
98 view fun getSeason0SecretPlacesDefs(): [Definition] {
99 return [
100 Definition(5, 100), // 1% chance, rarity 2
101 Definition(12, 1900), // 19% chance, rarity 1
102 Definition(20, 8000) // 80% chance, rarity 0
103 ]
104 }
105
106 /// ============= Trait: Season 0 - Ability =============
107
108 /// The Definition of the Marketplace Season 0
109 ///
110 access(all) enum Season0Ability: UInt8 {
111 access(all) case Omniscience // 全知全能
112 access(all) case ElementalMastery // 全元素掌控
113 access(all) case TimeStand // 时间静止
114 access(all) case MillenniumFreeze // 千年冰封
115 access(all) case FossilResurgence // 化石重生
116 access(all) case MysticVision // 神秘视界
117 access(all) case PhoenixRebirth // 凤凰复生
118 access(all) case SoulBind // 灵魂束缚
119 access(all) case PrayerOfLight // 光明祈祷
120 access(all) case Starfall // 星辰坠落
121 access(all) case DragonsBreath // 龙焰吐息
122 access(all) case PsychicSense // 心灵感应
123 access(all) case MindControl // 心灵控制
124 access(all) case EndlessTorment // 无尽痛苦
125 access(all) case MeditationInDespair // 绝境冥思
126 access(all) case SilenceFear // 沉默恐惧
127 access(all) case GloryChallenge // 荣耀挑战
128 access(all) case ShieldWall // 防御罩墙
129 access(all) case TidalCall // 海潮呼唤
130 access(all) case FountainOfLife // 生命之泉
131 access(all) case PsychicInteraction // 精神互动
132 access(all) case PlagueTransmission // 疫病传染
133 access(all) case NinjaStealth // 忍者潜行
134 access(all) case BattleRoar // 战斗吼叫
135 access(all) case CongestiveStrike // 充血打击
136 access(all) case HolyGuidance // 圣光指引
137 access(all) case EmpoweredBarrier // 强化结界
138 access(all) case PerpetualLife // 生生不息
139 access(all) case CombatEvade // 战斗闪避
140 access(all) case AbyssArrow // 深渊之箭
141 access(all) case SoulEcho // 灵魂回响
142 access(all) case ArcaneBlink // 魔力闪现
143 access(all) case ArcaneExplosion // 魔力爆炸
144 access(all) case ShadowStep // 暗黑影步
145 access(all) case JadeStoneSpell // 玉石咒语
146 access(all) case PhantomDodge // 鬼魅闪避
147 access(all) case KissOfDeath // 死亡之吻
148 access(all) case PhantomSummoning // 幻影召唤
149 access(all) case EyeOfTheRaven // 乌鸦之眼
150 access(all) case RatSwarmSurge // 鼠群涌动
151 access(all) case FlameShock // 烈焰冲击
152 access(all) case GaleSpeedBlade // 疾风快剑
153 access(all) case InterstellarFlight // 星界飞行
154 access(all) case WraithSeal // 怨灵封印
155 access(all) case DivineRestoration // 神力恢复
156 access(all) case LifePull // 生命拉扯
157 access(all) case RapidFire // 快速射击
158 access(all) case MightyBlow // 强力打击
159 access(all) case PhysicalTraining // 锻炼体魄
160 }
161
162 access(all)
163 view fun getSeason0AbilityDefs(): [Definition] {
164 return [
165 Definition(5, 20), // 0.2% chance, rarity 3
166 Definition(12, 100), // 1% chance, rarity 2
167 Definition(25, 1880), // 18.8% chance, rarity 1
168 Definition(49, 8000) // 80% chance, rarity 0
169 ]
170 }
171
172 /// ============= Trait: Season 0 - Weapons =============
173
174 access(all) enum Season0Weapons: UInt8 {
175 access(all) case Starstaff // 星辰法杖
176 access(all) case BowOfTheMysteriousBird // 九天玄鸟之弓
177 access(all) case VoidSpiritWand // 虚空灵杖
178 access(all) case GodlyWand // 神祇法杖
179 access(all) case SunriseHolySword // 旭日圣剑
180 access(all) case DeepSeaTrident // 深海三叉戟
181 access(all) case DragonboneBow // 龙骨弓
182 access(all) case RainbowHolySword // 虹光圣剑
183 access(all) case MysticalGrimoire // 神秘法书
184 access(all) case SaintsStaff // 圣者圣杖
185 access(all) case FirePhoenixWhip // 火凤长鞭
186 access(all) case SoulOrb // 灵魂法球
187 access(all) case LightningSpear // 闪电长矛
188 access(all) case DarkScepter // 黑暗权杖
189 access(all) case DawnLance // 破晓长枪
190 access(all) case RedLotusRocket // 红莲火箭
191 access(all) case DemonBoneSpike // 恶魔骨刺
192 access(all) case EvilStarCatapult // 魔星投石器
193 access(all) case SwordOfTenderness // 温柔之剑
194 access(all) case WindWarriorLongbow // 风战者长弓
195 access(all) case NightDagger // 黑夜匕首
196 access(all) case GalaxyHalberd // 银河双戟
197 access(all) case MoonshadowScimitar // 影月弯刀
198 access(all) case IceCrownDagger // 冰冠短剑
199 access(all) case StormBattleAxe // 风暴战斧
200 access(all) case ArcaneStaff // 奥术长杖
201 access(all) case AxeOfInferno // 烈火之斧
202 access(all) case SkybreakerDualBlade // 破空双刃
203 access(all) case IceGiantSword // 寒冰巨剑
204 access(all) case TrollsHammer // 巨魔之锤
205 }
206
207 access(all)
208 view fun getSeason0WeaponsDefs(): [Definition] {
209 return [
210 Definition(5, 20), // 0.2% chance, rarity 3
211 Definition(12, 100), // 1% chance, rarity 2
212 Definition(20, 1880), // 18.8% chance, rarity 1
213 Definition(30, 8000) // 80% chance, rarity 0
214 ]
215 }
216
217 access(account)
218 fun attemptToGenerateRandomEntryForSeason0(): @Entry? {
219 // 5% for secret places, 10% for ability, 15% for weapons, 70% for nothing
220 let randForTypePercent = revertibleRandom<UInt8>(modulo: 100)
221 if randForTypePercent >= 30 {
222 return nil
223 }
224 var type: Type? = nil
225 if randForTypePercent < 5 {
226 type = Type<Season0SecretPlaces>()
227 } else if randForTypePercent < 15 {
228 type = Type<Season0Ability>()
229 } else {
230 type = Type<Season0Weapons>()
231 }
232 return <- self.generateRandomEntry(type!)
233 }
234
235 /**
236 ------------------------ Public Methods ------------------------
237 */
238
239 /// Get the rarity definition array for a given series
240 /// The higher the rarity in front.
241 ///
242 access(all)
243 view fun getRarityDefinition(_ series: Type): [Definition]? {
244 switch series {
245 case Type<Season0SecretPlaces>():
246 return self.getSeason0SecretPlacesDefs()
247 case Type<Season0Ability>():
248 return self.getSeason0AbilityDefs()
249 case Type<Season0Weapons>():
250 return self.getSeason0WeaponsDefs()
251 }
252 return nil
253 }
254
255 /// Get the maximum rarity for a given series
256 ///
257 access(all)
258 view fun getMaxRarity(_ series: Type): UInt8 {
259 if let arr = self.getRarityDefinition(series) {
260 return UInt8(arr.length - 1)
261 }
262 return UInt8.max
263 }
264
265 /**
266 ------------------------ Genreal Interfaces & Resources ------------------------
267 */
268
269 /// The Entry Definition
270 ///
271 access(all) struct Definition {
272 access(all)
273 let threshold: UInt8 // max value for this rarity, not included
274 access(all)
275 let weight: UInt64 // weight of this rarity
276
277 view init (
278 _ threshold: UInt8,
279 _ weight: UInt64
280 ) {
281 self.threshold = threshold
282 self.weight = weight
283 }
284 }
285
286 /// The TraitWithOffset Definition
287 ///
288 access(all) struct TraitWithOffset {
289 // Series is the identifier of the series enum
290 access(all)
291 let series: Type
292 // Value is the value of the trait, as the rawValue of the enum
293 access(all)
294 let value: UInt8
295 // Rarity is the rarity of the trait, from 0 to maxRarity
296 access(all)
297 let rarity: UInt8
298 // Offset is random between -20 and 20, to be used for rarity extension
299 access(all)
300 let offset: Int8
301
302 init(
303 series: Type,
304 value: UInt8,
305 rarity: UInt8
306 ) {
307 self.series = series
308 self.value = value
309 self.rarity = rarity
310 // Offset is random between -20 and 20
311 let rand = revertibleRandom<UInt8>(modulo: 40)
312 self.offset = Int8(rand) - 20
313 }
314 }
315
316 /// The `Entry` resource
317 ///
318 access(all) resource Entry: ViewResolver.Resolver {
319 access(self)
320 let trait: TraitWithOffset
321
322 init (
323 series: Type,
324 value: UInt8,
325 rarity: UInt8
326 ) {
327 self.trait = TraitWithOffset(
328 series: series,
329 value: value,
330 rarity: rarity
331 )
332 }
333
334 /// Get the trait
335 ///
336 access(all)
337 view fun getTrait(): TraitWithOffset {
338 return self.trait
339 }
340
341 // ---- implement Resolver ----
342
343 /// Function that returns all the Metadata Views available for this profile
344 ///
345 access(all)
346 view fun getViews(): [Type] {
347 return [
348 Type<TraitWithOffset>(),
349 Type<MetadataViews.Trait>()
350 ]
351 }
352
353 /// Function that resolves a metadata view for this profile
354 ///
355 access(all)
356 fun resolveView(_ view: Type): AnyStruct? {
357 switch view {
358 case Type<TraitWithOffset>():
359 return self.trait
360 case Type<MetadataViews.Trait>():
361 return MetadataViews.Trait(
362 name: self.trait.series.identifier,
363 value: self.trait.value,
364 displayType: "number",
365 rarity: MetadataViews.Rarity(
366 score: UFix64(self.trait.rarity),
367 max: UFix64(FixesTraits.getMaxRarity(self.trait.series)),
368 description: nil
369 )
370 )
371 }
372 return nil
373 }
374 }
375
376 /// Create a new entry
377 ///
378 access(account)
379 fun createEntry(_ series: Type, _ value: UInt8, _ rarity: UInt8): @Entry {
380 return <- create Entry(
381 series: series,
382 value: value,
383 rarity: rarity
384 )
385 }
386
387 /// Generate a random entry
388 ///
389 access(account)
390 fun generateRandomEntry(_ series: Type): @Entry? {
391 let defs = self.getRarityDefinition(series)
392 if defs == nil {
393 return nil // DO NOT PANIC
394 }
395
396 // generate a random number for the entry
397 let randForEntry = revertibleRandom<UInt64>(modulo: 10000)
398
399 // calculate the rarity
400 var totalWeight: UInt64 = 0
401 var lastThreshold: UInt8 = 0
402 var currentThreshold: UInt8 = 0
403 let maxRarity = UInt8(defs!.length - 1)
404 var currentRarity: UInt8 = 0
405 // find the right rarity
406 for i, def in defs! {
407 totalWeight = totalWeight + def.weight
408 if randForEntry < totalWeight {
409 currentThreshold = def.threshold
410 currentRarity = maxRarity - UInt8(i)
411 break
412 }
413 lastThreshold = def.threshold
414 }
415 // create the entry
416 return <- self.createEntry(
417 series,
418 // calculate the value
419 lastThreshold + (UInt8(randForEntry % 255) % (currentThreshold - lastThreshold)),
420 currentRarity
421 )
422 }
423}
424