DeploySEALED
■&░!*╱█!█$◇%░◆▪■○?○╲~▓▪?■%&*%■●^▫$█#!▪█◇■▪■▪■!%◇▓~~○█@■%^$█?^&○□
Transaction ID
Execution Fee
0.00000384 FLOWTransaction Summary
DeployContract 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 }