Smart Contract

MMORPG4

A.b576a3926d239682.MMORPG4

Valid From

129,382,423

Deployed

1w ago
Feb 20, 2026, 11:04:13 AM UTC

Dependents

6 imports
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