Smart Contract
MMORPG5
A.b576a3926d239682.MMORPG5
1import FlowToken from 0x1654653399040a61
2import FungibleToken from 0xf233dcee88fe0abe
3
4access(all) contract MMORPG5 {
5
6 access(self) let FlowTokenVault: Capability<&{FungibleToken.Receiver}>
7 access(self) let info: Info
8 access(self) var totalGamePlayers: UInt // Player ID
9
10 access(all) entitlement ThiefAbility1
11 access(all) entitlement ThiefAbility2
12 access(all) entitlement WarriorAbility1
13 access(all) entitlement WarriorAbility2
14
15 access(all) event ESportsGamerInitiated(battleId: Int, player1Address: Address, player2Address: Address)
16
17 /* 構造体 */
18 access(all) struct Info {
19
20 /* fields */
21 access(contract) var battleQueue: [GameBattle]
22 access(contract) var basicAbility: {String: String} // リソース別の基本アビリティ
23 access(contract) var basicAttackInfo: {String: [UInt8]} // システム側から通常攻撃トランザクションを行うための情報
24
25 /* setter <Return: Battle ID> */
26 access(contract) fun newGameBattle(
27 player1Address: Address,
28 player2Address: Address,
29 lifeOfPlayer1: UInt8,
30 lifeOfPlayer2: UInt8,
31 reward: UFix64
32 ): Int {
33 var battle = GameBattle(
34 player1Address: player1Address,
35 player2Address: player2Address,
36 lifeOfPlayer1: lifeOfPlayer1,
37 lifeOfPlayer2: lifeOfPlayer2,
38 reward: reward
39 )
40 self.battleQueue.append(battle)
41 return self.battleQueue.length - 1
42 }
43
44 /* setter */
45 access(contract) fun updateGameBattle(
46 battleId: Int,
47 target: String?,
48 lifeChange: UInt8,
49 isDamage: Bool
50 ) {
51 if (target != nil) {
52 self.battleQueue[battleId]!.setPlayerLife(target: target!, lifeChange: lifeChange, isDamage: isDamage)
53 } else {
54 self.battleQueue[battleId]!.setEnemyLife(damage: lifeChange) // 敵への攻撃は全体攻撃しかない..
55 }
56 }
57
58 /* init */
59 init() {
60 self.battleQueue = []
61 self.basicAbility = {
62 "Warrior-BasicAttack": "Rage Attack",
63 "Warrior-ShareableAbility": "LargeRecoveryShield",
64 "Thief-BasicAttack": "Whip Attack",
65 "Thief-ShareableAbility": "PoisonMaking",
66 "Enemy-BasicAttack": "A Crushing Blow"
67 }
68 self.basicAttackInfo = {
69 "Warrior": [1, 3], // [GroupAttack?, damageVolume] (Rage Attack)
70 "Thief": [1, 1], // [GroupAttack?, damageVolume] (Whip Attack
71 "Enemy": [0, 4] // [GroupAttack?, damageVolume] (A Crushing Blow)
72 }
73 }
74 }
75
76 /* Info内の構造体 */
77 access(all) struct GameBattle {
78
79 /* fields */
80 access(all) let execTime: UFix64
81 access(all) let player1Address: Address
82 access(all) let player2Address: Address
83 access(all) var lifeOfPlayer1: UInt8
84 access(all) var lifeOfPlayer2: UInt8
85 access(all) var enemy1Life: UInt8
86 access(all) var enemy2Life: UInt8
87 access(all) let reward: UFix64
88
89 /* setter(ダメージまたは回復) */
90 access(contract) fun setPlayerLife(target: String, lifeChange: UInt8, isDamage: Bool) {
91 if (target == "TeamPlayer1") {
92 if (isDamage) {
93 self.lifeOfPlayer1 = self.lifeOfPlayer1 - lifeChange < 0 ? 0 : self.lifeOfPlayer1 - lifeChange
94 } else {
95 self.lifeOfPlayer1 = self.lifeOfPlayer1 + lifeChange
96 }
97 } else if (target == "TeamPlayer2") {
98 if (isDamage) {
99 self.lifeOfPlayer2 = self.lifeOfPlayer2 - lifeChange < 0 ? 0 : self.lifeOfPlayer2 - lifeChange
100 } else {
101 self.lifeOfPlayer2 = self.lifeOfPlayer2 + lifeChange
102 }
103 }
104 }
105 /* setter(敵への全体攻撃) */
106 access(contract) fun setEnemyLife(damage: UInt8) {
107 self.enemy1Life = self.enemy1Life - damage < 0 ? 0 : self.enemy1Life - damage
108 self.enemy2Life = self.enemy2Life - damage < 0 ? 0 : self.enemy2Life - damage
109 }
110
111 /* init */
112 init(player1Address: Address, player2Address: Address, lifeOfPlayer1: UInt8, lifeOfPlayer2: UInt8, reward: UFix64) {
113 let modulo: UInt8 = 3
114 self.execTime = getCurrentBlock().timestamp
115 self.player1Address = player1Address
116 self.player2Address = player2Address
117 self.lifeOfPlayer1 = lifeOfPlayer1
118 self.lifeOfPlayer2 = lifeOfPlayer2
119
120 // 両方戦士タイプなら悪魔を登場させる
121 if (lifeOfPlayer1 > 5 && lifeOfPlayer2 > 5) {
122 self.enemy1Life = 9 + revertibleRandom(modulo: modulo) // 9 ~ 11の整数
123 } else {
124 self.enemy1Life = 5 + revertibleRandom(modulo: modulo) // 5 ~ 7の整数
125 }
126 self.enemy2Life = 8 + revertibleRandom(modulo: modulo) // 8 ~ 10の整数
127 self.reward = reward
128 }
129 }
130
131 // GET
132 access(all) fun getInfo(): Info {
133 return self.info
134 }
135
136 // PUT
137 access(all) fun newGameBattle(
138 payment: @FlowToken.Vault,
139 player1Address: Address,
140 player2Address: Address,
141 lifeOfPlayer1: UInt8,
142 lifeOfPlayer2: UInt8
143 ) {
144 let battleId = self.info.newGameBattle(
145 player1Address: player1Address,
146 player2Address: player2Address,
147 lifeOfPlayer1: lifeOfPlayer1,
148 lifeOfPlayer2: lifeOfPlayer2,
149 reward: payment.balance * 0.97 * 2.0)
150
151 emit ESportsGamerInitiated(battleId: battleId, player1Address: player1Address, player2Address: player2Address)
152
153 // Game Feeをシステムアカウントに保存
154 self.FlowTokenVault.borrow()!.deposit(from: <- payment)
155 }
156
157 access(all) resource Thief {
158 access(all) let id: UInt
159 access(all) let maxLife: UInt
160
161 access(ThiefAbility1) fun PoisonMaking(battleId: Int) {
162 MMORPG5.info.updateGameBattle(battleId: battleId, target: nil, lifeChange: 6, isDamage: true)
163 }
164 access(ThiefAbility2) fun WhipAttack(battleId: Int) {
165 MMORPG5.info.updateGameBattle(battleId: battleId, target: nil, lifeChange: 1, isDamage: true)
166 }
167
168 init() {
169 MMORPG5.totalGamePlayers = MMORPG5.totalGamePlayers + 1
170 self.id = MMORPG5.totalGamePlayers
171 self.maxLife = 5
172 }
173 }
174
175 access(all) resource Warrior {
176 access(all) let id: UInt
177 access(all) let maxLife: UInt
178
179 access(WarriorAbility1) fun LargeRecoveryShield(battleId: Int, target: String) {
180 MMORPG5.info.updateGameBattle(battleId: battleId, target: target, lifeChange: 4, isDamage: false)
181 }
182 access(WarriorAbility2) fun RageAttack(battleId: Int) {
183 MMORPG5.info.updateGameBattle(battleId: battleId, target: nil, lifeChange: 3, isDamage: true)
184 }
185
186 init() {
187 MMORPG5.totalGamePlayers = MMORPG5.totalGamePlayers + 1
188 self.id = MMORPG5.totalGamePlayers
189 self.maxLife = 8
190 }
191 }
192
193 access(all) resource Admin {
194 access(all) fun payRewards(recipient1: Address, recipient2: Address, reward: UFix64) {
195 // Pay the reward.
196 let reward1 <- MMORPG5.account
197 .storage
198 .borrow<auth(FungibleToken.Withdraw) &{FungibleToken.Provider}>(from: /storage/flowTokenVault)!
199 .withdraw(amount: reward) as! @FlowToken.Vault
200
201 getAccount(recipient1)
202 .capabilities
203 .get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)
204 .borrow()!.deposit(from: <- reward1)
205
206 let reward2 <- MMORPG5.account
207 .storage
208 .borrow<auth(FungibleToken.Withdraw) &{FungibleToken.Provider}>(from: /storage/flowTokenVault)!
209 .withdraw(amount: reward) as! @FlowToken.Vault
210
211 getAccount(recipient2)
212 .capabilities
213 .get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)
214 .borrow()!.deposit(from: <- reward2)
215 }
216
217 access(all) fun basicAttack(battleId: Int, target: String?, resourceName: String) {
218 MMORPG5.info.updateGameBattle(battleId: battleId, target: target, lifeChange: MMORPG5.info.basicAttackInfo[resourceName]![1], isDamage: true)
219 }
220 }
221
222 access(all) fun createWarriorResource(payment: @FlowToken.Vault): @MMORPG5.Warrior {
223 pre {
224 payment.balance == 0.5: "Cost of resource must be 5.0FLOW coin."
225 }
226 let _resource <- create Warrior() // resourceは予約語なので_をつけてます
227
228 MMORPG5.FlowTokenVault.borrow()!.deposit(from: <- payment) // 代金徴収
229 return <- _resource
230 }
231
232 access(all) fun createThiefResource(payment: @FlowToken.Vault): @MMORPG5.Thief {
233 pre {
234 payment.balance == 0.5: "Cost of resource must be 5.0FLOW coin."
235 }
236 let _resource <- create Thief()
237
238 MMORPG5.FlowTokenVault.borrow()!.deposit(from: <- payment) // 代金徴収
239 return <- _resource
240 }
241
242 init() {
243 self.info = Info()
244 self.FlowTokenVault = self.account.capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)
245 self.totalGamePlayers = 0
246 self.account.storage.save( <- create Admin(), to: /storage/MMORPG5Admin)
247 }
248}
249