DeploySEALED

■&░!*╱█!█$◇%░◆▪■○?○╲~▓▪?■%&*%■●^▫$█#!▪█◇■▪■▪■!%◇▓~~○█@■%^$█?^&○□

Transaction ID

Timestamp

Aug 19, 2024, 06:25:49 PM UTC
1y ago

Block Height

84,896,173

Computation

0

Execution Fee

0.00000384 FLOW

Transaction Summary

Deploy

Contract deployment

Contract deployment

Script Arguments

0nameString
ABRoyaleGame
1codeString
import FungibleToken from 0xf233dcee88fe0abe import FlowToken from 0x1654653399040a61 import ArenaBoyzGlobals from 0xc59f4df08f49f89d import ArenaBoyzHistory from 0xc59f4df08f49f89d pub contract ABRoyaleGame { pub var pods: [Pod] pub var heroes: [Hero] pub var seeded_hash: [UInt8] pub var attacks : [Attack] pub var last_results : [AttackResult] //TODO: emit events instead of a struct saying what happened during attacks pub var seconds_per_turn : Int //this is now how many seconds each player is given to act pub var turn : UInt32 pub var circle : UInt32 pub var enrolled_players : Int32 pub var alive_heroes : Int32 pub var current_player_id : Int32 pub var pod_size: UInt32 pub var turn_end_timestamp: UFix64 pub var game_started : Bool pub var game_id : UInt32 pub var entry_fee : UFix64 pub var payment_address : Address pub var runners_up : [Int32] pub var runner_up_id : Int32 pub var second_runner_up_id : Int32 pub var current_key_id : Int pub var signup_keys: {UInt32: Int} pub let KeyStoragePath: StoragePath pub let KeyPublicPath: PublicPath pub let AdminStoragePath: StoragePath pub event AttackResultEvent(pod: UInt32, attacker: UInt32, attacker_key: Int, target_key: Int, action_key: Int, target: UInt32, damage: UInt16, critical: Bool, counter_critical: Bool, counter_damage: UInt16, action_target: UInt32, action_amount: UFix64, action_item: UInt8, game_id: UInt32, attacker_hp: UInt16, target_hp: UInt16, action_target_hp: UInt16, attacker_hp_max: UInt16, target_hp_max: UInt16, action_target_hp_max: UInt16 ) pub event GameResultEvent(game_id: UInt32, victor_id: Int32, placement: Int, ending_turn: UInt32, enrolled_players: Int32) pub event GameResultEventV2(game_id: UInt32, victor_id: Int32, runner_up_id: Int32, second_runner_up_id: Int32, placement: Int, ending_turn: UInt32, enrolled_players: Int32) pub struct ScavengeConflict { pub set var hero_ids: [UInt32] pub set var attack_indicies: [Int] init(hero_ids: [UInt32], attack_indicies: [Int]) { self.hero_ids = hero_ids self.attack_indicies = attack_indicies } access(contract) fun addConflict(hero_id: UInt32, attack_index: Int) { self.hero_ids.append(hero_id) self.attack_indicies.append(attack_index) } } pub struct Pod { pub(set) var hero_ids: [UInt32] //open question: is it better to iterate through pods (there will be total players / heroes per pod of them, approx one order of magnitude less, to find which pod a hero is in, or store the pod in each hero. trading computation vs. storage) pub(set) var scavenge_pool: [UInt8; 32] init() { self.hero_ids = [] self.scavenge_pool = ArenaBoyzGlobals.scavenge_pool_0 } pub fun ResetScavengePool(Circle : UInt32) { if(Circle == 1) { self.scavenge_pool = ArenaBoyzGlobals.scavenge_pool_1 } if(Circle == 2) { self.scavenge_pool = ArenaBoyzGlobals.scavenge_pool_2 } if(Circle == 3) { self.scavenge_pool = ArenaBoyzGlobals.scavenge_pool_3 } if(Circle == 4) { self.scavenge_pool = ArenaBoyzGlobals.scavenge_pool_4 } if(Circle == 5) { self.scavenge_pool = ArenaBoyzGlobals.scavenge_pool_5 } if(Circle == 6) { self.scavenge_pool = ArenaBoyzGlobals.scavenge_pool_6 } if(Circle >= 7) { self.scavenge_pool = ArenaBoyzGlobals.getScavengePool7() } } pub fun SortPlayers(Players: [Hero]) { var i: Int = 0 while i < self.hero_ids.length - 1 { var maxIndex: Int = i var j: Int = i + 1 while j < self.hero_ids.length { let heroIdJ: UInt32 = self.hero_ids[j] let heroIdMax: UInt32 = self.hero_ids[maxIndex] var speedJ: UInt16 = 0 var speedMax: UInt16 = 0 var hpJ: UInt16 = 0 var hpMax: UInt16 = 0 // Find hero by ID for heroIdJ for player in Players { if player.hero_id == heroIdJ { speedJ = player.GetSpeed() hpJ = player.hitpoints break } } // Find hero by ID for heroIdMax for player in Players { if player.hero_id == heroIdMax { speedMax = player.GetSpeed() hpMax = player.hitpoints break } } if hpJ == 0 && hpMax > 0 { // Skip, as heroes with 0 HP should be sorted to the back } else if hpMax == 0 && hpJ > 0 { maxIndex = j } else if speedJ > speedMax || (speedJ == speedMax && revertibleRandom() > revertibleRandom()) { maxIndex = j } j = j + 1 } if maxIndex != i { let temp: UInt32 = self.hero_ids[i] self.hero_ids[i] = self.hero_ids[maxIndex] self.hero_ids[maxIndex] = temp } i = i + 1 } } pub fun DropItemIntoPool(Item : UInt8) { var i: UInt8 = 0 for item in self.scavenge_pool { if(item == 0) { self.scavenge_pool[i] = Item break } i = i + 1 } } } pub struct Hero { pub(set) var hero_id: UInt32 pub(set) var key_id: Int pub(set) var hitpoints: UInt16 pub(set) var max_hitpoints: UInt16 pub(set) var shields: UInt16 pub(set) var damage: UInt16 pub(set) var defense: UInt16 pub(set) var accuracy: UInt16 pub(set) var speed: UInt16 pub(set) var counter: UInt16 pub(set) var attack_award: UInt8 pub(set) var stim: UFix64 pub(set) var has_moved: Bool //0: cannot go yet 1: can take action 2: has already taken action pub(set) var move_timestamp: UFix64 pub(set) var current_pod_id: UInt32 //its almost certainly going to be preferable to store the pod the hero is in, rather than iterate through all pods pub(set) var items: [UInt8; 24] init() { self.hero_id = 0 self.key_id = 0 self.hitpoints = 0 self.shields = 0 self.max_hitpoints = 0 self.damage = 0 self.defense = 0 self.accuracy = 0 self.speed = 0 self.counter = 0 self.attack_award = 0 self.stim = 0.0 self.current_pod_id = 0 self.has_moved = false self.move_timestamp = 0.0 self.items = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] } pub fun GetDamage() : UInt16 { var temp_stat: UInt16 = self.damage var effective_item_max: Int = ArenaBoyzGlobals.effective_item_max var i = 0 for item in self.items { if(effective_item_max > 0) { if(i == 0) { // only count primary weapons in slot 0 if(item >= ArenaBoyzGlobals.ar_start && item < ArenaBoyzGlobals.sniper_start) { //AR, defense-based temp_stat = temp_stat + (ArenaBoyzGlobals.primary_bonus_per_thresh*(UInt16(item)))*(self.GetDefense()/ArenaBoyzGlobals.threshhold_amount) } if(item >= ArenaBoyzGlobals.sniper_start && item < ArenaBoyzGlobals.smg_start) { //Sniper, accuracy-based temp_stat = temp_stat + (ArenaBoyzGlobals.primary_bonus_per_thresh*(UInt16(item-(ArenaBoyzGlobals.sniper_start-1))))*(self.GetAccuracy()/ArenaBoyzGlobals.threshhold_amount) } if(item >= ArenaBoyzGlobals.smg_start && item < ArenaBoyzGlobals.shotgun_start) { //SMG, speed-based temp_stat = temp_stat + (ArenaBoyzGlobals.primary_bonus_per_thresh*(UInt16(item-(ArenaBoyzGlobals.smg_start-1))))*(self.GetSpeed()/ArenaBoyzGlobals.threshhold_amount) } if(item >= ArenaBoyzGlobals.shotgun_start && item < ArenaBoyzGlobals.throwable_start) { //Shotgun, counter-based temp_stat = temp_stat + (ArenaBoyzGlobals.primary_bonus_per_thresh*(UInt16(item-(ArenaBoyzGlobals.shotgun_start-1))))*(self.GetCounter()/ArenaBoyzGlobals.threshhold_amount) } } if(item >= ArenaBoyzGlobals.damage_start && item < ArenaBoyzGlobals.defense_start) { temp_stat = temp_stat + ArenaBoyzGlobals.equipment_bonus_per_level*UInt16(item - (ArenaBoyzGlobals.damage_start -1)) } } effective_item_max = effective_item_max - 1 i = i + 1 } return temp_stat } pub fun GetAccuracy() : UInt16 { var temp_stat: UInt16 = self.accuracy var effective_item_max: Int = ArenaBoyzGlobals.effective_item_max for item in self.items { if(effective_item_max > 0) { if(item >= ArenaBoyzGlobals.accuracy_start && item < ArenaBoyzGlobals.speed_start) { temp_stat = temp_stat + ArenaBoyzGlobals.equipment_bonus_per_level*(UInt16(item - (ArenaBoyzGlobals.accuracy_start - 1))) } } effective_item_max = effective_item_max - 1 } return temp_stat; } pub fun GetCounter() : UInt16 { var temp_stat: UInt16 = self.counter var effective_item_max: Int = ArenaBoyzGlobals.effective_item_max for item in self.items { if(effective_item_max > 0) { if(item >= ArenaBoyzGlobals.counter_start && item < ArenaBoyzGlobals.items_end) { temp_stat = temp_stat + ArenaBoyzGlobals.equipment_bonus_per_level*(UInt16(item - (ArenaBoyzGlobals.counter_start-1))) } } effective_item_max = effective_item_max - 1 } return temp_stat; } pub fun GetDefense() : UInt16 { var temp_stat: UInt16 = self.defense var effective_item_max: Int = ArenaBoyzGlobals.effective_item_max for item in self.items { if(effective_item_max > 0) { if(item >= ArenaBoyzGlobals.defense_start && item < ArenaBoyzGlobals.accuracy_start) { temp_stat = temp_stat + ArenaBoyzGlobals.equipment_bonus_per_level*(UInt16(item - (ArenaBoyzGlobals.defense_start-1))) } } effective_item_max = effective_item_max - 1 } return temp_stat; } pub fun GetSpeed() : UInt16 { var temp_stat: UInt16 = self.speed var effective_item_max: Int = ArenaBoyzGlobals.effective_item_max for item in self.items { if(effective_item_max > 0) { if(item >= ArenaBoyzGlobals.speed_start && item < ArenaBoyzGlobals.counter_start) { temp_stat = temp_stat + ArenaBoyzGlobals.equipment_bonus_per_level*(UInt16(item - (ArenaBoyzGlobals.speed_start - 1))) } } effective_item_max = effective_item_max - 1 } return temp_stat; } pub fun GetAttackAwardAmount() : UFix64 { var health_pct:UFix64 = UFix64(self.hitpoints)/UFix64(self.max_hitpoints) var base_value:UFix64 = 5.0 if(health_pct >= 1.0) { return base_value*5.0 } if(health_pct < 1.0 && health_pct >= 0.75) { return base_value*4.0 } if(health_pct < 0.75 && health_pct >= 0.5) { return base_value*3.0 } if(health_pct < 0.5 && health_pct >= 0.25) { return base_value*2.0 } return base_value } access(contract) fun GenerateHero(hero_id: UInt32, starting_pod_id : UInt32) { self.hero_id = hero_id self.max_hitpoints = ArenaBoyzGlobals.hp_base self.hitpoints = ArenaBoyzGlobals.hp_base self.damage = ArenaBoyzGlobals.damage_base self.defense = ArenaBoyzGlobals.defense_base self.accuracy = ArenaBoyzGlobals.accuracy_base self.speed = ArenaBoyzGlobals.speed_base self.counter = ArenaBoyzGlobals.counter_base self.current_pod_id = starting_pod_id self.attack_award = UInt8(revertibleRandom() & 0xFF)%5 self.items[0] = ArenaBoyzGlobals.starting_primary_pool[UInt8(revertibleRandom() & 0xFF)%4] //10% bonus jitter applied below, for some variance self.max_hitpoints = self.max_hitpoints + UInt16(revertibleRandom() & 0xFFFF)%100 self.hitpoints = self.max_hitpoints self.damage = self.damage + UInt16(revertibleRandom() & 0xFFFF)%30 self.defense = self.defense + UInt16(revertibleRandom() & 0xFFFF)%10 self.accuracy = self.accuracy + UInt16(revertibleRandom() & 0xFFFF)%20 self.speed = self.speed + UInt16(revertibleRandom() & 0xFFFF)%20 self.counter = self.counter + UInt16(revertibleRandom() & 0xFFFF)%20 } access(contract) fun ProcessConsumableItems() { var i: Int = 0 for item in self.items { if(item >= ArenaBoyzGlobals.throwable_start && item < ArenaBoyzGlobals.healing_start) { //self.counter = self.counter + ArenaBoyzGlobals.targetable_bonus_per_level * (1 + UInt16(item) - UInt16(ArenaBoyzGlobals.throwable_start)) self.items[i] = 0 } else if(item >= ArenaBoyzGlobals.healing_start && item < ArenaBoyzGlobals.shield_start) { self.defense = self.defense + ArenaBoyzGlobals.targetable_bonus_per_level * (1 + UInt16(item) - UInt16(ArenaBoyzGlobals.healing_start)) self.items[i] = 0 } else if(item >= ArenaBoyzGlobals.shield_start && item < ArenaBoyzGlobals.stim_start) { self.speed = self.speed + ArenaBoyzGlobals.targetable_bonus_per_level * (1 + UInt16(item) - UInt16(ArenaBoyzGlobals.shield_start)) self.items[i] = 0 } else if(item >= ArenaBoyzGlobals.stim_start && item < ArenaBoyzGlobals.medal_start) { //self.speed = self.speed + ArenaBoyzGlobals.targetable_bonus_per_level * (1 + UInt16(item) - UInt16(ArenaBoyzGlobals.stim_start)) self.items[i] = 0 } else if(item >= ArenaBoyzGlobals.medal_start && item < ArenaBoyzGlobals.damage_start) { self.counter = self.counter + ArenaBoyzGlobals.targetable_bonus_per_level * (1 + UInt16(item) - UInt16(ArenaBoyzGlobals.medal_start)) self.items[i] = 0 } i = i + 1 } self.RemoveInventoryGaps() } access(contract) fun ProcessEndPod() { var i: Int = 0 for item in self.items { if(item >= ArenaBoyzGlobals.medal_start && item < ArenaBoyzGlobals.damage_start) { self.max_hitpoints = self.max_hitpoints + ArenaBoyzGlobals.medal_max_hp_per_level * (1 + UInt16(item) - UInt16(ArenaBoyzGlobals.medal_start)) } i = i + 1 } if(self.hitpoints > 0) { self.max_hitpoints = self.max_hitpoints + ArenaBoyzGlobals.free_max_hp_per_level; self.hitpoints = self.max_hitpoints } } access(contract) fun RemoveInventoryGaps() { var j:Int = 0 // Pointer for the next position of a non-zero element var i:Int = 0 for item in self.items { // When a non-zero element is found, swap it with the element at j if (item != 0) { var temp:UInt8 = item self.items[i] = self.items[j] self.items[j] = temp j = j + 1 } i = i + 1 } } access(contract) fun DiscardExcessInventory() { var i:Int = 0 for item in self.items { if (i >= ArenaBoyzGlobals.effective_item_max) { self.items[i] = 0 } i = i + 1 } } access(contract) fun ProcessEndTurn() { self.DiscardExcessInventory() self.has_moved = false self.stim = 0.0 } access(contract) fun SetTimestamp(timestamp : UFix64) { self.move_timestamp = timestamp } } pub struct Attack { pub(set) var attacker: UInt32 pub(set) var attack_target: UInt32 pub(set) var action_target: UInt32 pub(set) var scavenge_choice: UInt8 pub(set) var selected_loot: UInt8 pub(set) var equip_primary : Bool pub(set) var turn : UInt32 init(attacker : UInt32, attack_target : UInt32, action_target : UInt32, scavenge_choice : UInt8, equip_primary : Bool, selected_loot : UInt8, turn : UInt32 ) { self.attacker = attacker self.attack_target = attack_target self.action_target = action_target self.scavenge_choice = scavenge_choice self.equip_primary = equip_primary self.selected_loot = selected_loot self.turn = turn } } pub struct AttackResult { pub(set) var attacker: UInt32 pub(set) var target: UInt32 pub(set) var damage: UInt32 init() { self.attacker = 0 self.target = 0 self.damage = 0 } } init() { self.heroes = [] self.attacks = [] self.last_results = [] self.seeded_hash = [] self.pods = [] self.signup_keys = {} self.runners_up = [-1,- 1, -1] self.enrolled_players = 0 self.alive_heroes = 0 self.current_player_id = -1 self.current_key_id = -1 self.seconds_per_turn = ArenaBoyzGlobals.seconds_per_turn self.turn = 0 self.turn_end_timestamp = 0.0 self.circle = 0 self.pod_size = ArenaBoyzGlobals.heroes_per_pod self.game_id = 0 self.entry_fee = 10.0 self.payment_address = self.account.address self.runner_up_id = 0 self.second_runner_up_id = 0 self.game_started = false self.AdminStoragePath = /storage/arenaboyzPaths4Admin self.KeyStoragePath = /storage/arenaboyzPaths4Key self.KeyPublicPath = /public/arenaboyzPaths4Key let admin: @ABRoyaleGame.Admin <- create Admin() self.account.save(<-admin, to: self.AdminStoragePath) } //a player signs up to play the game here, pays the game fee pub fun playerSignup(payment: @FungibleToken.Vault) : @PlayerKey { pre { self.game_started == false : "game must not be running yet" } assert(payment.balance == self.entry_fee, message: "insufficient funds") let receiverRef = getAccount(self.payment_address) .getCapability(/public/flowTokenReceiver) .borrow<&{FungibleToken.Receiver}>() ?? panic("Could not borrow receiver reference to the recipient's Vault") receiverRef.deposit(from: <- payment) self.enrolled_players = self.enrolled_players + 1 self.current_player_id = self.current_player_id + 1 self.current_key_id = self.current_key_id + 1 self.signup_keys[UInt32(self.current_player_id)] = self.current_key_id log(self.signup_keys[UInt32(self.current_player_id)]) return <- create PlayerKey(player_id: UInt32(self.current_player_id), game_id: self.game_id, key_id : self.current_key_id) } pub fun updatePlayerKey(signup_key: @PlayerKey, payment: @FungibleToken.Vault) : @PlayerKey { pre { self.game_started == false : "game must not be running yet" } assert(payment.balance == self.entry_fee, message: "insufficient funds") var i: Int = 0 while i < Int(self.current_player_id+1) { if(self.signup_keys[UInt32(i)] == signup_key.key_id) { panic("player has already signed up") } i = i + 1 } let receiverRef = getAccount(self.payment_address) .getCapability(/public/flowTokenReceiver) .borrow<&{FungibleToken.Receiver}>() ?? panic("Could not borrow receiver reference to the recipient's Vault") receiverRef.deposit(from: <- payment) signup_key.setGameID(game_id: self.game_id) self.current_player_id = self.current_player_id + 1 signup_key.setPlayerID(player_id: UInt32(self.current_player_id)) self.signup_keys[UInt32(self.current_player_id)] = signup_key.key_id log(self.signup_keys[UInt32(self.current_player_id)]) self.enrolled_players = self.enrolled_players + 1 return <-signup_key } pub fun shuffle(array: [UInt32]) : [UInt32] { let n = array.length var shuffledArray = array var i: Int = 0 while i < n { let j = i + (Int(revertibleRandom()) % (n - i)) let temp = shuffledArray[i] shuffledArray[i] = shuffledArray[j] shuffledArray[j] = temp i = i + 1 } return shuffledArray } pub fun getPodSizes(total_players: Int32): [Int] { //pod balancing aims to create specific balacing to ensure fairness var podSizes: [Int] = [] var totalPlayers: Int = Int(total_players) if totalPlayers <= 8 { podSizes = [totalPlayers] } else if totalPlayers <= 16 { let playersInFirstPod = (totalPlayers + 1) / 2 let playersInSecondPod = totalPlayers - playersInFirstPod podSizes = [playersInFirstPod, playersInSecondPod] } else if totalPlayers <= 24 { let playersInFirstPod = (totalPlayers + 2) / 3 let playersInSecondPod = (totalPlayers + 1) / 3 let playersInThirdPod = totalPlayers - playersInFirstPod - playersInSecondPod podSizes = [playersInFirstPod, playersInSecondPod, playersInThirdPod] } else { while totalPlayers > 24 { podSizes.append(8) totalPlayers = totalPlayers - 8 } if totalPlayers <= 8 { podSizes.append(totalPlayers) } else if totalPlayers <= 16 { let playersInFirstPod = (totalPlayers + 1) / 2 let playersInSecondPod = totalPlayers - playersInFirstPod podSizes.append(playersInFirstPod) podSizes.append(playersInSecondPod) } else { let playersInFirstPod = (totalPlayers + 2) / 3 let playersInSecondPod = (totalPlayers + 1) / 3 let playersInThirdPod = totalPlayers - playersInFirstPod - playersInSecondPod podSizes.append(playersInFirstPod) podSizes.append(playersInSecondPod) podSizes.append(playersInThirdPod) } } var maxPodSize = 0 var i = 0 while i < podSizes.length { if podSizes[i] > maxPodSize { maxPodSize = podSizes[i] } i = i + 1 } self.pod_size = UInt32(maxPodSize) return podSizes } priv fun initializeHeroesAndPods(podSizes : [Int]) { if(self.enrolled_players < Int32(self.pod_size)) { //When we go back to pods, this will come back into play self.pod_size = UInt32(self.enrolled_players) } var hero_ids: [UInt32] = [] var id : UInt32 = 0 while id < UInt32(self.enrolled_players) { hero_ids.append(id) self.heroes.append(Hero()) self.heroes[id].GenerateHero(hero_id: id, starting_pod_id: 0) if let signupKey = self.signup_keys[id] { self.heroes[id].key_id = signupKey log(signupKey) } else { panic("Signup key for hero ID is nil") } id = id + 1 } var podIndex = 0 while podIndex < podSizes.length { self.pods.append(Pod()) log("appended pod") podIndex = podIndex + 1 } hero_ids = self.shuffle(array: hero_ids) podIndex = 0 var currentHeroIndex: Int = 0 podIndex = 0 while podIndex < podSizes.length { let podSize = podSizes[podIndex] var podHeroCount = 0 while podHeroCount < podSize && currentHeroIndex < hero_ids.length { let heroID = hero_ids[currentHeroIndex] self.pods[podIndex].hero_ids.append(heroID) self.heroes[heroID].current_pod_id = UInt32(podIndex) log("appending hero") log(heroID) log(UInt32(podIndex)) currentHeroIndex = currentHeroIndex + 1 podHeroCount = podHeroCount + 1 } podIndex = podIndex + 1 } var largest_pod: Int = 0 for pod in self.pods { var localHeroes: [Hero] = [] for hero_id in pod.hero_ids { localHeroes.append(self.heroes[hero_id]) } pod.SortPlayers(Players: localHeroes) var i: Int = 0 while i < Int(pod.hero_ids.length) { let podHeroId: UInt32 = pod.hero_ids[i] self.heroes[podHeroId].move_timestamp = getCurrentBlock().timestamp + UFix64(i * self.seconds_per_turn) i = i + 1 } if(i > largest_pod) { largest_pod = i } } log("largest pod") log(largest_pod) self.turn_end_timestamp = getCurrentBlock().timestamp + UFix64(self.seconds_per_turn * largest_pod) self.game_started = true self.alive_heroes = self.enrolled_players log(self.turn) } priv fun resetTurn(podSizes : [Int]) { pre { self.game_started == true : "game must be running" } self.turn = self.turn + 1 if (self.turn % ArenaBoyzGlobals.turns_per_circle == 0) { self.circle = self.circle + 1 var aliveHeroIDs: [UInt32] = [] var heroIndex = 0 // Collect all alive heroes while heroIndex < self.heroes.length { let hero = self.heroes[heroIndex] if (hero.hitpoints > 0) { aliveHeroIDs.append(hero.hero_id) } heroIndex = heroIndex + 1 } while (self.pods.length > 0) { self.pods.removeLast() } var podIndex = 0 while podIndex < podSizes.length { self.pods.append(Pod()) podIndex = podIndex + 1 } var shuffledHeroIDs: [UInt32] = self.shuffle(array: aliveHeroIDs) var currentHeroIndex: Int = 0 podIndex = 0 while podIndex < podSizes.length { let podSize = podSizes[podIndex] var podHeroCount = 0 while podHeroCount < podSize && currentHeroIndex < shuffledHeroIDs.length { let heroID = shuffledHeroIDs[currentHeroIndex] self.pods[podIndex].hero_ids.append(heroID) self.heroes[heroID].current_pod_id = UInt32(podIndex) currentHeroIndex = currentHeroIndex + 1 podHeroCount = podHeroCount + 1 } podIndex = podIndex + 1 } } var pod_i : Int = 0 for pod in self.pods { if(self.turn % ArenaBoyzGlobals.turns_per_circle == 0) { self.pods[pod_i].ResetScavengePool(Circle: self.circle) } if(self.turn/ArenaBoyzGlobals.turns_per_circle < 7 ) { // end of game, final circle! var i: Int = 0 for heroId in pod.hero_ids { self.heroes[heroId].ProcessEndTurn() if(self.turn % ArenaBoyzGlobals.turns_per_circle == 0) { self.heroes[heroId].ProcessEndPod() } } } else { var i: Int = 0 for heroId in pod.hero_ids { self.heroes[heroId].ProcessEndTurn() if(self.turn % ArenaBoyzGlobals.turns_per_circle == 0) { self.heroes[heroId].ProcessEndPod() } } } //shorten turn length when a player is killed, but we need to do this only one time per player var pod_survivors : Int = 0 for live_i in pod.hero_ids { if self.heroes[live_i].hitpoints > 0 { pod_survivors = pod_survivors + 1 } } // if(self.seconds_per_turn < pod_survivors*60 + 60) { // self.seconds_per_turn = pod_survivors*60 + 60 // } pod_i = pod_i + 1 } // Sort players within each pod and set move timestamps var largest_pod: Int = 0 for pod in self.pods { // Create a local array of heroes for this pod var localHeroes: [Hero] = [] for hero_id in pod.hero_ids { localHeroes.append(self.heroes[hero_id]) } // Sort the local array of heroes pod.SortPlayers(Players: localHeroes) var i: Int = 0 while i < Int(pod.hero_ids.length) { let podHeroId: UInt32 = pod.hero_ids[i] self.heroes[podHeroId].move_timestamp = getCurrentBlock().timestamp + UFix64(i * self.seconds_per_turn) i = i + 1 } if(i > largest_pod) { largest_pod = i } } self.turn_end_timestamp = getCurrentBlock().timestamp + UFix64(self.seconds_per_turn * largest_pod) log(self.turn) } //TODO: reshuffle pods on new circle. circle should be incremented, and players should be randomly shuffled into new pods. //Player commits to an attack here pub fun submitAttack(attacker_key: @PlayerKey, attack_target : UInt32, action_target : UInt32, scavenge_choice : UInt8, equip_primary : Bool): @PlayerKey { pre { //TODO: speed management goes here. players should be prohibited from entering attacks before it is their turn to do so //see self.tick and self.turn vs. their speed. it should also be recorded if they have moved already this turn. attacker_key.game_id == self.game_id : "attacker must be from this game" self.heroes[attacker_key.player_id].hitpoints > 0 : "attacker must be alive!" self.heroes[attack_target].hitpoints > 0 : "attack target must be alive!" self.heroes[action_target].hitpoints > 0 : "action target must be alive!" self.heroes[attacker_key.player_id].has_moved == false : "hero has already attacked!" self.heroes[attacker_key.player_id].move_timestamp <= getCurrentBlock().timestamp : "attacker is not allowed to enter an attack yet!" self.heroes[attacker_key.player_id].current_pod_id == self.heroes[attack_target].current_pod_id : "attack target is not in the same pod!" self.heroes[attacker_key.player_id].current_pod_id == self.heroes[action_target].current_pod_id : "action target is not in the same pod!" //TODO: Can we put in some constants or globals for design-related items in code } self.heroes[attacker_key.player_id].key_id = attacker_key.key_id //this is an okay place to do this, but a player who never attacks will never set their key_id var a_remove: Int = 0 for attack in self.attacks { if(attack.attacker == attacker_key.player_id) { //if the player has submitted an attack already, remove it self.attacks.remove(at: a_remove) break } a_remove = a_remove + 1 } self.attacks.append(Attack(attacker : attacker_key.player_id, attack_target : attack_target, action_target : action_target, scavenge_choice : scavenge_choice, equip_primary: equip_primary, selected_loot:self.pods[self.heroes[attacker_key.player_id].current_pod_id].scavenge_pool[scavenge_choice], turn: self.turn )) //TODO: we can change contract state here, such as setting a block height return <-attacker_key } pub fun dropItem(dropper_key: @PlayerKey, item_index : Int): @PlayerKey { pre { dropper_key.game_id == self.game_id : "dropper must be from this game" } let dropped_item: Int = Int(self.heroes[dropper_key.player_id].items[item_index]) self.heroes[dropper_key.player_id].items[item_index] = 0 self.pods[self.heroes[dropper_key.player_id].current_pod_id].DropItemIntoPool(Item: UInt8(dropped_item)) return <-dropper_key } pub fun equipItem(equipper_key: @PlayerKey, item_index : Int): @PlayerKey { pre{ equipper_key.game_id == self.game_id : "dropper must be from this game" self.heroes[equipper_key.player_id].items[item_index] < ArenaBoyzGlobals.targetables_start : "has to pick a primary weapon to equip primary!" } let old_primary: Int = Int(self.heroes[equipper_key.player_id].items[0]) self.heroes[equipper_key.player_id].items[0] = self.heroes[equipper_key.player_id].items[item_index] self.heroes[equipper_key.player_id].items[item_index] = UInt8(old_primary) return <-equipper_key } //at the time handleDeath is called, all damage has already been applied to both attack target and attacker due to counter. so either //both players are already dead, or just the attacker is. priv fun handleDeath(): Bool { log("handling death") self.alive_heroes = 0 var winner_id: Int32 = -1 var runner_up_id: Int32 = -1 var second_runner_up_id: Int32 = -1 var game_over: Bool = false var runners_up_ids: [Int32] = [] // Dynamic array for hero in self.heroes { if(hero.hitpoints > 0) { self.alive_heroes = self.alive_heroes + 1 winner_id = Int32(hero.key_id) runners_up_ids.append(Int32(hero.key_id)) // Add survivor IDs dynamically log(Int32(hero.key_id)) } } if(self.alive_heroes == 1) { var eliminatedPlayer: Int32 = -1 for h in self.runners_up { log(h) if (!runners_up_ids.contains(h)) { eliminatedPlayer = h break } } self.runner_up_id = eliminatedPlayer emit GameResultEvent(game_id: self.game_id, victor_id: winner_id, placement: 1, ending_turn: self.turn, enrolled_players: self.enrolled_players) ArenaBoyzHistory.addGame(game_id: self.game_id, victor_id: winner_id, runner_up_id: self.runner_up_id, second_runner_up_id: self.second_runner_up_id, placement: 1, ending_turn: self.turn, enrolled_players: self.enrolled_players) game_over = true } else if(self.alive_heroes == 2) { var eliminatedPlayer: Int32 = -1 for h in self.runners_up { if (!runners_up_ids.contains(h)) { eliminatedPlayer = h break } } self.runners_up = runners_up_ids // Update runners_up directly from dynamic array self.second_runner_up_id = eliminatedPlayer } else if(self.alive_heroes == 3) { self.runners_up = runners_up_ids // Update runners_up directly from dynamic array } if(game_over) { self.resetGame() return true } return false } priv fun resolveAttacks() { var validAttacks: [Attack] = [] //new code to remove attacks that have not been flagged for attack in self.attacks { if attack.turn == self.turn { validAttacks.append(attack) } } self.attacks = validAttacks var conflicts: {UInt8: ScavengeConflict} = {} var conflict_i = 0 for attack in self.attacks { let item_id = attack.scavenge_choice let hero_id = attack.attacker if conflicts[item_id] != nil { conflicts[item_id]?.addConflict(hero_id: hero_id, attack_index: conflict_i) } else { conflicts[item_id] = ScavengeConflict( hero_ids: [hero_id], attack_indicies: [conflict_i]) } conflict_i = conflict_i + 1 } for conflict in conflicts.values { if conflict.hero_ids.length > 1 { // More than one hero is trying to scavenge the same item, resolve conflict let winnerIndex = Int(revertibleRandom())%conflict.hero_ids.length var attack_index_i = 0 for attack_index in conflict.attack_indicies { if attack_index_i != winnerIndex { // Losers get consolation item self.attacks[attack_index].scavenge_choice = 0 } // The winner keeps their original choice, no action needed attack_index_i = attack_index_i+1 } } // If only one hero is involved, no conflict to resolve } for attack in self.attacks { self.heroes[attack.attacker].has_moved = true self.heroes[attack.attacker].shields = 0 var attack_pod: UInt32 = self.heroes[attack.attacker].current_pod_id var attacker_hp: UInt16 = self.heroes[attack.attacker].hitpoints var target_hp: UInt16 = self.heroes[attack.attack_target].hitpoints var action_target_hp: UInt16 = 0 var attacker_hp_max: UInt16 = self.heroes[attack.attacker].max_hitpoints var target_hp_max: UInt16 = self.heroes[attack.attack_target].max_hitpoints var action_target_hp_max: UInt16 = 0 var stimBoost: UFix64 = 1.0 var scavenged_item_id: Int = 0 var actionAmount: UFix64 = 0.0 //item and looting logic if(self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice] >= ArenaBoyzGlobals.throwable_start && self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice] < ArenaBoyzGlobals.healing_start) { //throwing weapon var thrownDamage: UInt16 = UInt16(ArenaBoyzGlobals.throwable_damage_per_level * (1 + UInt16(self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice]) - UInt16(ArenaBoyzGlobals.throwable_start))) var originalThrownDamage: UInt16 = thrownDamage action_target_hp = self.heroes[attack.action_target].hitpoints action_target_hp_max = self.heroes[attack.action_target].max_hitpoints if(self.heroes[attack.action_target].shields > thrownDamage) { thrownDamage = 0 } else { thrownDamage = thrownDamage - self.heroes[attack.action_target].shields } if(originalThrownDamage > self.heroes[attack.action_target].shields ) { self.heroes[attack.action_target].shields = 0 } else { self.heroes[attack.action_target].shields = self.heroes[attack.action_target].shields - originalThrownDamage } thrownDamage = self.CalculateDamage(baseDamage : thrownDamage, defense: self.heroes[attack.action_target].GetDefense()); actionAmount = UFix64(thrownDamage) if(thrownDamage > self.heroes[attack.action_target].hitpoints) { self.heroes[attack.action_target].hitpoints = 0; } else { self.heroes[attack.action_target].hitpoints = self.heroes[attack.action_target].hitpoints - thrownDamage; } } if(self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice] >= ArenaBoyzGlobals.healing_start && self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice] < ArenaBoyzGlobals.shield_start) { action_target_hp = self.heroes[attack.action_target].hitpoints action_target_hp_max = self.heroes[attack.action_target].max_hitpoints var hitpointsBefore : UInt16 = self.heroes[attack.action_target].hitpoints self.heroes[attack.action_target].hitpoints = self.heroes[attack.action_target].hitpoints + 100 + UInt16(ArenaBoyzGlobals.heal_per_level * (1 + UInt16(self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice]) - UInt16(ArenaBoyzGlobals.healing_start))) actionAmount = UFix64(UInt16(100) + UInt16(ArenaBoyzGlobals.heal_per_level * (1 + UInt16(self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice]) - UInt16(ArenaBoyzGlobals.healing_start)))) if(self.heroes[attack.action_target].hitpoints > self.heroes[attack.action_target].max_hitpoints) { actionAmount = UFix64(self.heroes[attack.action_target].max_hitpoints - hitpointsBefore) self.heroes[attack.action_target].hitpoints = self.heroes[attack.action_target].max_hitpoints } } if(self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice] >= ArenaBoyzGlobals.shield_start && self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice] < ArenaBoyzGlobals.stim_start) { action_target_hp = self.heroes[attack.action_target].hitpoints action_target_hp_max = self.heroes[attack.action_target].max_hitpoints self.heroes[attack.action_target].shields = self.heroes[attack.action_target].shields + 100 + UInt16(ArenaBoyzGlobals.shield_per_level * (1 + UInt16(self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice]) - UInt16(ArenaBoyzGlobals.shield_start))) actionAmount = UFix64(UInt16(100) + UInt16(ArenaBoyzGlobals.shield_per_level * (1 + UInt16(self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice]) - UInt16(ArenaBoyzGlobals.shield_start)))) } if(self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice] >= ArenaBoyzGlobals.stim_start && self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice] < ArenaBoyzGlobals.medal_start) { action_target_hp = self.heroes[attack.action_target].hitpoints action_target_hp_max = self.heroes[attack.action_target].max_hitpoints self.heroes[attack.action_target].stim = self.heroes[attack.action_target].stim + ArenaBoyzGlobals.stim_per_level * UFix64(1 + Int(self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice]) - Int(ArenaBoyzGlobals.stim_start)) actionAmount = UFix64(ArenaBoyzGlobals.stim_per_level * UFix64(1 + Int(self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice]) - Int(ArenaBoyzGlobals.stim_start))) } if(self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice] >= ArenaBoyzGlobals.medal_start && self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice] < ArenaBoyzGlobals.damage_start) { action_target_hp = self.heroes[attack.action_target].hitpoints action_target_hp_max = self.heroes[attack.action_target].max_hitpoints self.heroes[attack.action_target].max_hitpoints = self.heroes[attack.action_target].max_hitpoints+ ArenaBoyzGlobals.medal_max_hp_per_level * (1 + UInt16(self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice]) - UInt16(ArenaBoyzGlobals.medal_start)) actionAmount = UFix64(ArenaBoyzGlobals.medal_max_hp_per_level * (1 + UInt16(self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice]) - UInt16(ArenaBoyzGlobals.medal_start))) } stimBoost = stimBoost + self.heroes[attack.attacker].stim self.heroes[attack.attacker].stim = 0.0 var scavenging_iterator: Int = 0 if(!attack.equip_primary || self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice] == 0 || self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice] >= ArenaBoyzGlobals.targetables_start) { for item in self.heroes[attack.attacker].items { //get our scavenge item if it isn't being equipped- it goes into the first empty inventory slot if(item == 0) { //TODO - stop if they go over max inventory of 12, dont let them loot in this case. self.heroes[attack.attacker].items[scavenging_iterator] = self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice] scavenged_item_id = scavenging_iterator break; } scavenging_iterator = scavenging_iterator + 1 } } else { //Or they are equipping a primary, in which case, they will swap out their existing weapon and move it to their first available spot for item in self.heroes[attack.attacker].items { //get our scavenge item if it isn't being equipped- it goes into the first empty inventory slot if(item == 0) { //TODO - stop if they go over max inventory of 12, dont let them loot in this case. scavenged_item_id = scavenging_iterator break; } scavenging_iterator = scavenging_iterator + 1 } var oldPrimary: UInt8 = self.heroes[attack.attacker].items[0] self.heroes[attack.attacker].items[0] = self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice] if(oldPrimary != 0) { //fixes a bug if player is picking up a primary when they have no items self.heroes[attack.attacker].items[scavenged_item_id] = oldPrimary; } } //"tighten up" scavenge pool, closing in on the taken item /* var currentIndex: Int = Int(attack.scavenge_choice); while ( currentIndex < self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool.length - 1) { self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[currentIndex] = self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[currentIndex + 1] currentIndex = currentIndex + 1 }*/ self.heroes[attack.attacker].ProcessConsumableItems(); self.pods[self.heroes[attack.attacker].current_pod_id].scavenge_pool[attack.scavenge_choice] = 0 //Attack damage is calculated below var isCritical: Bool = (UInt16(revertibleRandom() & 0xFFFF)%1000 < self.heroes[attack.attacker].GetAccuracy()) var isCounterCritical: Bool = (UInt16(revertibleRandom() & 0xFFFF)%1000 < self.heroes[attack.attack_target].GetAccuracy()) var counterAttack: Bool = (UInt16(revertibleRandom() & 0xFFFF)%1000 < self.heroes[attack.attack_target].GetCounter())// - shieldMod var damageDealt: UInt16 = self.heroes[attack.attacker].GetDamage() var originalDamageDealt: UInt16 = damageDealt if(self.heroes[attack.attack_target].shields > damageDealt) { damageDealt = 0 } else { damageDealt = damageDealt - self.heroes[attack.attack_target].shields } if(originalDamageDealt > self.heroes[attack.attack_target].shields ) { self.heroes[attack.attack_target].shields = 0 } else { self.heroes[attack.attack_target].shields = self.heroes[attack.attack_target].shields - originalDamageDealt } damageDealt = self.CalculateDamage(baseDamage: damageDealt, defense: self.heroes[attack.attack_target].GetDefense()) if (isCritical) { damageDealt = damageDealt * 2 } // Process the counterattack var counterDamage: UInt16 = 0 if (counterAttack) { counterDamage = self.CalculateDamage(baseDamage : self.heroes[attack.attack_target].GetDamage(), defense: self.heroes[attack.attacker].GetDefense()); var originalCounterDamageDealt: UInt16 = counterDamage if(self.heroes[attack.attacker].shields > counterDamage) { counterDamage = 0 } else { counterDamage = counterDamage - self.heroes[attack.attacker].shields } if(originalCounterDamageDealt > self.heroes[attack.attacker].shields ) { self.heroes[attack.attacker].shields = 0 } else { self.heroes[attack.attacker].shields = self.heroes[attack.attacker].shields - originalCounterDamageDealt } if (isCounterCritical) { counterDamage = counterDamage * 2 } } // pub event AttackResult(attacker: UInt32, target: UInt32, damage: UInt32, critical: Bool, counter_damage: UInt32, action_target: UInt32, action_damage: UInt32, action_item: UInt8 ) //get attack awards before damage is dealt, so we get damage scaling if(self.heroes[attack.attack_target].attack_award == 0) { self.heroes[attack.attacker].damage = self.heroes[attack.attacker].damage + UInt16(self.heroes[attack.attack_target].GetAttackAwardAmount() * stimBoost) } if(self.heroes[attack.attack_target].attack_award == 1) { self.heroes[attack.attacker].defense = self.heroes[attack.attacker].defense + UInt16(self.heroes[attack.attack_target].GetAttackAwardAmount() * stimBoost) } if(self.heroes[attack.attack_target].attack_award == 2) { self.heroes[attack.attacker].accuracy = self.heroes[attack.attacker].accuracy + UInt16(self.heroes[attack.attack_target].GetAttackAwardAmount() * stimBoost) } if(self.heroes[attack.attack_target].attack_award == 3) { self.heroes[attack.attacker].speed = self.heroes[attack.attacker].speed + UInt16(self.heroes[attack.attack_target].GetAttackAwardAmount() * stimBoost) } if(self.heroes[attack.attack_target].attack_award == 4) { self.heroes[attack.attacker].counter = self.heroes[attack.attacker].counter + UInt16(self.heroes[attack.attack_target].GetAttackAwardAmount() * stimBoost) } if(self.heroes[attack.attack_target].hitpoints < damageDealt) { self.heroes[attack.attack_target].hitpoints = 0 } else { self.heroes[attack.attack_target].hitpoints = self.heroes[attack.attack_target].hitpoints - damageDealt } var game_over : Bool = false var event_emitted : Bool = false; if(self.heroes[attack.attack_target].hitpoints <= 0) { counterDamage = 0; emit AttackResultEvent(pod: attack_pod, attacker: attack.attacker, attacker_key: self.heroes[attack.attacker].key_id, target_key: self.heroes[attack.attack_target].key_id, action_key: self.heroes[attack.action_target].key_id, target: attack.attack_target, damage: damageDealt, critical: isCritical, counter_critical: isCounterCritical, counter_damage: counterDamage, action_target: attack.action_target, action_amount: actionAmount, action_item: attack.selected_loot, game_id: self.game_id, attacker_hp: attacker_hp, target_hp: target_hp, action_target_hp: action_target_hp, attacker_hp_max: attacker_hp_max, target_hp_max: target_hp_max, action_target_hp_max: action_target_hp_max ) event_emitted = true self.heroes[attack.attack_target].current_pod_id = 4294967295; var target_item_iterator: Int = 0; for target_item in self.heroes[attack.attack_target].items { var attacker_item_iterator: Int = 0; for attacker_item in self.heroes[attack.attacker].items { //process looting if(attacker_item == 0) { self.heroes[attack.attacker].items[attacker_item_iterator] = target_item self.heroes[attack.attack_target].items[target_item_iterator] = 0; continue; } attacker_item_iterator = attacker_item_iterator + 1 ; } target_item_iterator = target_item_iterator + 1; } if(self.handleDeath()) { game_over = true return } } if(!game_over && self.heroes[attack.action_target].hitpoints <= 0) { counterDamage = 0; if(!event_emitted) { emit AttackResultEvent(pod: attack_pod, attacker: attack.attacker, attacker_key: self.heroes[attack.attacker].key_id, target_key: self.heroes[attack.attack_target].key_id, action_key: self.heroes[attack.action_target].key_id, target: attack.attack_target, damage: damageDealt, critical: isCritical, counter_critical: isCounterCritical, counter_damage: counterDamage, action_target: attack.action_target, action_amount: actionAmount, action_item: attack.selected_loot, game_id: self.game_id, attacker_hp: attacker_hp, target_hp: target_hp, action_target_hp: action_target_hp, attacker_hp_max: attacker_hp_max, target_hp_max: target_hp_max, action_target_hp_max: action_target_hp_max ) event_emitted = true } self.heroes[attack.action_target].current_pod_id = 4294967295; var action_target_item_iterator: Int = 0; for action_target_item in self.heroes[attack.action_target].items { var attacker_item_iterator: Int = 0; for attacker_item in self.heroes[attack.attacker].items { //process looting if(attacker_item == 0) { self.heroes[attack.attacker].items[attacker_item_iterator] = action_target_item self.heroes[attack.action_target].items[action_target_item_iterator] = 0; continue; } attacker_item_iterator = attacker_item_iterator + 1 ; } action_target_item_iterator = action_target_item_iterator + 1; } if(self.handleDeath()) { game_over = true return } } if(self.heroes[attack.attacker].hitpoints < counterDamage) { self.heroes[attack.attacker].hitpoints = 0 } else { self.heroes[attack.attacker].hitpoints = self.heroes[attack.attacker].hitpoints - counterDamage } if(!event_emitted) { emit AttackResultEvent(pod: attack_pod, attacker: attack.attacker, attacker_key: self.heroes[attack.attacker].key_id, target_key: self.heroes[attack.attack_target].key_id, action_key: self.heroes[attack.action_target].key_id, target: attack.attack_target, damage: damageDealt, critical: isCritical, counter_critical: isCounterCritical, counter_damage: counterDamage, action_target: attack.action_target, action_amount: actionAmount, action_item: attack.selected_loot, game_id: self.game_id, attacker_hp: attacker_hp, target_hp: target_hp, action_target_hp: action_target_hp, attacker_hp_max: attacker_hp_max, target_hp_max: target_hp_max, action_target_hp_max: action_target_hp_max ) event_emitted = true } if(!game_over && self.heroes[attack.attacker].hitpoints <= 0) { self.heroes[attack.attacker].current_pod_id = 4294967295; var counter_item_iterator: Int = 0; for counter_target_item in self.heroes[attack.attacker].items { var attacker_item_iterator: Int = 0; for attacker_item in self.heroes[attack.attack_target].items { //process looting if(attacker_item == 0) { self.heroes[attack.attack_target].items[attacker_item_iterator] = counter_target_item self.heroes[attack.attacker].items[counter_item_iterator] = 0; continue; } attacker_item_iterator = attacker_item_iterator + 1 ; } counter_item_iterator = counter_item_iterator + 1; } self.handleDeath() } } while(self.attacks.length > 0){ self.attacks.removeLast() } } priv fun resetGame() { while(self.heroes.length > 0) { self.heroes.removeLast() } while(self.attacks.length > 0){ self.attacks.removeLast() } while(self.pods.length > 0){ self.pods.removeLast() } self.signup_keys = {} self.game_id = self.game_id + 1 self.enrolled_players = 0 //enrolled players starting here fixes a problem but it also does not reflect now the number of players in the game0 self.current_player_id = -1 self.seconds_per_turn = ArenaBoyzGlobals.seconds_per_turn self.turn = 0 self.turn_end_timestamp = 0.0 self.circle = 0 self.pod_size = ArenaBoyzGlobals.heroes_per_pod self.game_started = false } priv fun setEntryFee(entry_fee : UFix64) { self.entry_fee = entry_fee } pub fun CalculateDamage(baseDamage : UInt16, defense : UInt16) : UInt16 { var damage : UInt16 = 0 if(defense > baseDamage) { damage = 0 } else { damage = baseDamage - defense } if(damage < baseDamage/ArenaBoyzGlobals.minimum_damage_divisor) { damage = baseDamage/ArenaBoyzGlobals.minimum_damage_divisor //min damage is 20% of your base } return damage } pub resource interface PlayerKeyInterface { pub fun getPlayerID(): UInt32 pub fun getKeyID(): Int } pub resource PlayerKey: PlayerKeyInterface { access(contract) var player_id: UInt32 access(contract) var game_id: UInt32 access(contract) var key_id: Int init( player_id: UInt32, game_id: UInt32, key_id: Int ) { self.player_id = player_id self.game_id = game_id self.key_id = key_id } pub fun getPlayerID() : UInt32 { return self.player_id } pub fun getKeyID() : Int { return self.key_id } access(contract) fun setGameID(game_id : UInt32) { self.game_id = game_id log(self.game_id) } access(contract) fun setPlayerID(player_id : UInt32) { self.player_id = player_id } } pub resource Admin { init() {} pub fun initializeHeroesAndPods() { ABRoyaleGame.initializeHeroesAndPods(podSizes: ABRoyaleGame.getPodSizes(total_players: ABRoyaleGame.enrolled_players)) } pub fun resolveAttacks() { ABRoyaleGame.resolveAttacks() } pub fun resetTurn() { ABRoyaleGame.resetTurn(podSizes: ABRoyaleGame.getPodSizes(total_players: ABRoyaleGame.alive_heroes)) } pub fun resetGame() { ABRoyaleGame.resetGame() } pub fun setEntryFee(entry_fee : UFix64) { ABRoyaleGame.setEntryFee(entry_fee: entry_fee) } } }

Cadence Script

1transaction(name: String, code: String ) {
2		prepare(signer: AuthAccount) {
3			signer.contracts.add(name: name, code: code.utf8 )
4		}
5	}