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