public function __construct($Time, $Ability, $Actor, $DecreaseCooldown = false) { $this->Time = $Time; $this->Actor = $Actor; $this->Ability = $Ability; $Duration = AbilityItem::GetDuration($Ability); $Cooldown = AbilityItem::GetCooldown($Ability); $this->TimestampDone = $this->Time + AbilityItem::GetDuration($Ability); if ($DecreaseCooldown) { $Cooldown -= $Cooldown * AbilityItem::GetMultiplier(Enums\EAbility::Support_DecreaseCooldowns); } $this->TimestampCooldown = $this->Time + $Cooldown; }
/** * * @return bool Returns true if ability has been successfully used, false otherwise. */ public static function HandleAbility($Game, $Lane, $Player, $Ability, $Deactivate = false) { $AbilityMultiplier = self::GetMultiplier($Ability->GetAbility()); switch ($Ability->GetAbility()) { case Enums\EAbility::Offensive_HighDamageOneTarget: if (!$Deactivate) { $Enemy = $Lane->GetEnemy($Player->GetTarget()); if ($Enemy === null || $Enemy->IsDead()) { return false; } $Damage = $Enemy->GetMaxHp(); if ($Enemy->GetType() === Enums\EEnemyType::Boss) { $Damage *= self::GetMultiplierBoss(Enums\EAbility::Offensive_HighDamageOneTarget); } else { $Damage *= $AbilityMultiplier; } $Player->Stats->AbilityDamageDealt += $Damage; $Enemy->AbilityDamageTaken += $Damage; } break; case Enums\EAbility::Offensive_DamageAllTargets: if (!$Deactivate) { $Enemies = $Lane->GetAliveEnemies(); if (empty($Enemies)) { return false; } foreach ($Enemies as $Enemy) { $Damage = $Enemy->GetMaxHp() * $AbilityMultiplier; $Player->Stats->AbilityDamageDealt += $Damage; $Enemy->AbilityDamageTaken += $Damage; } } break; case Enums\EAbility::Item_Resurrection: if (!$Deactivate) { $NotUsed = true; $PlayersInLane = $Game->GetPlayersInLane($Lane->GetLaneId()); foreach ($PlayersInLane as $PlayerInLane) { if ($PlayerInLane->IsDead()) { $NotUsed = false; $PlayerInLane->Respawn(); } } if ($NotUsed) { return false; } } break; case Enums\EAbility::Item_KillTower: if (!$Deactivate) { $Enemy = $Lane->GetEnemy($Player->GetTarget()); if ($Enemy === null || $Enemy->IsDead()) { return false; } if ($Enemy->GetType() !== Enums\EEnemyType::Tower) { return false; } $Enemy->SetHp(1); } break; case Enums\EAbility::Item_KillMob: if (!$Deactivate) { $Enemy = $Lane->GetEnemy($Player->GetTarget()); if ($Enemy === null || $Enemy->IsDead()) { return false; } if ($Enemy->GetType() === Enums\EEnemyType::Boss) { $MaxPercentage = $AbilityMultiplier; $Percentage = $MaxPercentage + lcg_value() * abs($MaxPercentage - 0.01); # 1% - 5% $Damage = $Enemy->GetMaxHp() * $Percentage; $Player->Stats->AbilityDamageDealt += $Damage; $Enemy->AbilityDamageTaken += $Damage; } else { $Enemy->SetHp(1); } } break; case Enums\EAbility::Item_IncreaseCritPercentagePermanently: if (!$Deactivate) { $Player->GetTechTree()->IncreaseCritPercentage($AbilityMultiplier); $Lane->AddActivePlayerAbility(new ActiveAbility($Game->Time, Enums\EAbility::Support_IncreaseCritPercentage, $Player->PlayerName, $Lane->HasActivePlayerAbilityDecreaseCooldowns())); } break; case Enums\EAbility::Item_IncreaseHPPermanently: if (!$Deactivate) { $Player->GetTechTree()->IncreaseHpMultiplier($AbilityMultiplier); $Player->GetTechTree()->RecalulateUpgrades(); $Lane->AddActivePlayerAbility(new ActiveAbility($Game->Time, Enums\EAbility::Support_Heal, $Player->PlayerName, $Lane->HasActivePlayerAbilityDecreaseCooldowns())); } break; case Enums\EAbility::Item_GoldForDamage: if (!$Deactivate) { $Enemy = $Lane->GetEnemy($Player->GetTarget()); if ($Player->GetGold() === 0 || $Enemy === null || $Enemy->IsDead()) { return false; } $MaxPercentage = $AbilityMultiplier; $Percentage = $MaxPercentage + lcg_value() * abs($MaxPercentage - 0.01); # 1% - 10% $Player->DecreaseGold($Player->GetGold() * $MaxPercentage); # 10% $Damage = $Enemy->GetMaxHp() * $Percentage; $Player->Stats->AbilityDamageDealt += $Damage; $Enemy->AbilityDamageTaken += $Damage; } break; case Enums\EAbility::Item_GiveGold: if (!$Deactivate) { $Player->IncreaseGold($AbilityMultiplier * pow(10, max(0, floor(log10($Game->GetLevel())) - 1))); $Lane->AddActivePlayerAbility(new ActiveAbility($Game->Time, Enums\EAbility::Support_IncreaseGoldDropped, $Player->PlayerName, $Lane->HasActivePlayerAbilityDecreaseCooldowns())); } break; case Enums\EAbility::Item_GiveRandomItem: if (!$Deactivate) { $PlayersInLane = $Game->GetPlayersInLane($Lane->GetLaneId()); foreach ($PlayersInLane as $PlayerInLane) { if (!$PlayerInLane->IsDead()) { $PlayerInLane->AddLoot($Game->Time, AbilityItem::GetRandomAbilityItem()); } } } break; case Enums\EAbility::Item_SkipLevels: if (!$Deactivate) { $Game->WormholeCount++; } break; } return true; }
public function Update($SecondsPassed = false) { $this->Time = time(); if (!$this->IsRunning()) { Server::GetLogger()->debug('Game #' . $this->GameId . ' is not running, canceling update'); return; } Server::GetLogger()->debug('Updating game #' . $this->GameId); $SecondPassed = $SecondsPassed !== false && $SecondsPassed > 0; $LaneDps = [0 => 0, 1 => 0, 2 => 0]; foreach ($this->Players as $Player) { $Player->ClearLoot($this->Time); $Player->CheckActiveAbilities($this); if ($SecondPassed && !$Player->IsDead()) { // Deal DPS damage to current target $Lane = $this->Lanes[$Player->GetCurrentLane()]; $Enemy = $Lane->GetEnemy($Player->GetTarget()); if ($Enemy === null || $Enemy->IsDead()) { $Enemy = $Lane->GetAliveEnemy(); if ($Enemy !== null) { $Player->SetTarget($Enemy->GetPosition()); } } if ($Enemy !== null) { $DealtDpsDamage = $Player->GetTechTree()->GetDps() * $Player->GetTechTree()->GetExtraDamageMultipliers($this->Lanes[$Player->GetCurrentLane()]->GetElement()) * $this->Lanes[$Player->GetCurrentLane()]->GetDamageMultiplier() * $SecondsPassed; if ($this->Lanes[$Player->GetCurrentLane()]->HasActivePlayerAbilityMaxElementalDamage()) { $DealtDpsDamage *= $Player->GetTechTree()->GetHighestElementalMultiplier(); } if ($Player->GetCritDamage() > 0) { $DealtDpsDamage *= $Player->GetTechTree()->GetDamageMultiplierCrit(); $Player->Stats->CritDamageDealt += $DealtDpsDamage; $Player->CritDamage = 0; } $Player->Stats->DpsDamageDealt += $DealtDpsDamage; $Enemy->DpsDamageTaken += $DealtDpsDamage; if ($DealtDpsDamage > 0) { Server::GetLogger()->debug('Player ' . $Player->PlayerName . ' (#' . $Player->GetAccountId() . ')' . ' did ' . $DealtDpsDamage . ' DPS damage to enemy #' . $Enemy->GetId()); } foreach ($Player->LaneDamageBuffer as $LaneId => $LaneDamage) { $LaneDps[$LaneId] += $LaneDamage / $SecondsPassed; // TODO: This is damage done by clicks, not per second, remove or keep? $Player->LaneDamageBuffer[$LaneId] = 0; } $LaneDps[$Player->GetCurrentLane()] += $Player->GetTechTree()->GetDps() * $SecondsPassed; #TODO: $DealtDpsDamage? } } if ($Player->IsDead() && $Player->CanRespawn($this->Time, true)) { // Respawn player $Player->Respawn(); } } // Loop through lanes and deal damage etc $DeadLanes = 0; foreach ($this->Lanes as $LaneId => $Lane) { if ($SecondPassed) { # TODO: Apply this in Lane::CheckActivePlayerAbilities instead? $ReflectDamageMultiplier = $Lane->GetReflectDamageMultiplier(); $NapalmDamageMultiplier = $Lane->GetNapalmDamageMultiplier(); } $DeadEnemies = 0; $EnemyCount = count($Lane->Enemies); $EnemyDpsDamage = 0; foreach ($Lane->Enemies as $Enemy) { if ($Enemy->IsDead()) { if ($Enemy->GetDpsHpDifference() > 0) { // Find next enemy to deal the rest of the DPS damage to $NextEnemy = $Lane->GetAliveEnemy(); if ($NextEnemy !== null) { Server::GetLogger()->debug('Enemy #' . $Enemy->GetId() . ' is dead. Passing on ' . $Enemy->GetDpsHpDifference() . ' DPS damage to enemy #' . $NextEnemy->GetId()); $NextEnemy->DpsDamageTaken += $Enemy->GetDpsHpDifference(); } } $DeadEnemies++; } else { if ($SecondPassed) { # TODO: Apply this in Lane::CheckActivePlayerAbilities instead? # TODO: Check if $ReflectDamageMultiplier is 0.5% or 50%, 0.5% would make more sense if it stacks.. if ($ReflectDamageMultiplier > 0) { $Enemy->AbilityDamageTaken += $Enemy->GetHp() * $ReflectDamageMultiplier * $SecondsPassed; } if ($NapalmDamageMultiplier > 0) { $Damage = $Enemy->GetMaxHp() * $NapalmDamageMultiplier * $SecondsPassed; $Enemy->AbilityDamageTaken += $Damage; } } $Enemy->Hp -= $Enemy->DpsDamageTaken; if ($Enemy->GetDpsHpDifference() > 0) { // Find next enemy to deal the rest of the DPS damage to $NextEnemy = $Lane->GetAliveEnemy(); if ($NextEnemy !== null) { Server::GetLogger()->debug('Enemy #' . $Enemy->GetId() . ' is dead. Passing on ' . $Enemy->GetDpsHpDifference() . ' DPS damage to enemy #' . $NextEnemy->GetId()); $NextEnemy->DpsDamageTaken += $Enemy->GetDpsHpDifference(); } } $Enemy->Hp -= $Enemy->ClickDamageTaken; $Enemy->Hp -= $Enemy->AbilityDamageTaken; if ($Enemy->IsDead()) { $this->IncreaseEnemiesKilled($Enemy); $Enemy->SetHp(0); $DeadEnemies++; $EnemyGold = $Enemy->GetGold() * $Lane->GetEnemyGoldMultiplier(); Server::GetLogger()->debug('Enemy #' . $Enemy->GetId() . ' is dead. Giving ' . $EnemyGold . ' gold (Multiplier: ' . $Lane->GetEnemyGoldMultiplier() . ') to players in lane #' . $Lane->GetLaneId()); $Lane->GiveGoldToPlayers($this, $EnemyGold); } else { $EnemyDpsDamage += $Enemy->GetDps(); } } $Enemy->DpsDamageTaken = 0; $Enemy->ClickDamageTaken = 0; $Enemy->AbilityDamageTaken = 0; if ($Enemy->HasTimer() && $Enemy->IsTimerEnabled() && $Enemy->HasTimerRanOut($SecondsPassed)) { switch ($Enemy->GetType()) { case Enums\EEnemyType::Tower: if ($Enemy->IsDead()) { continue; } // Revive dead mobs in the lane if the tower timer ran out Server::GetLogger()->debug('Respawn timer has ran out in lane #' . $Lane->GetLaneId() . ', reviving dead mobs in the lane'); foreach ($Lane->GetDeadEnemies(Enums\EEnemyType::Mob) as $DeadEnemy) { $DeadEnemy->ResetHp(); } break; case Enums\EEnemyType::MiniBoss: if (!$Enemy->IsDead()) { continue; } // Revive dead miniboss if he's dead and the timer ran out Server::GetLogger()->debug('Respawn timer has ran out for miniboss #' . $Enemy->GetId() . ' in lane #' . $Lane->GetLaneId() . ', reviving dead miniboss'); $Enemy->ResetHp(); break; case Enums\EEnemyType::TreasureMob: if ($Enemy->IsDead()) { continue; } // Kill the treasure mob and set gold to 0 if the timer (lifetime) ran out Server::GetLogger()->debug('Treasure mob #' . $Enemy->GetId() . ' timer has ran out in lane #' . $Lane->GetLaneId() . ', killing treasure mob'); $Enemy->SetHp(0); $Enemy->SetGold(0); $Enemy->DisableTimer(); $this->AddChatEntry('server', '', 'You were too slow to kill the treasure, it has despawned'); break; } $Enemy->ResetTimer(); } } $DeadLanes += $DeadEnemies === count($Lane->Enemies) ? 1 : 0; // Deal damage to players in lane $PlayersInLane = []; foreach ($this->Players as $Player) { if ($Player->GetCurrentLane() === $LaneId) { $PlayersInLane[] = $Player; if ($Player->IsInvulnerable()) { continue; } if ($SecondPassed && !$Player->IsDead()) { $EnemyDamage = $EnemyDpsDamage * $SecondsPassed; $PlayerHp = $Player->Hp - $EnemyDamage; if ($PlayerHp > 0) { $Player->Stats->DamageTaken += $EnemyDamage; $Player->Hp = $PlayerHp; } else { $Player->Stats->DamageTaken += $Player->Hp; $Player->Hp = 0; $Player->Kill($this->Time); } } } } $Lane->Dps = $LaneDps[$LaneId]; $Lane->CheckActivePlayerAbilities($this, $SecondsPassed); $Lane->UpdateHpBuckets($this->Time, $PlayersInLane); } if ($DeadLanes === 3) { if ($this->WormholeCount > 0) { if ($this->IsGoldHelmBossLevel()) { $this->WormholeCount *= 10; } $this->Level += $this->WormholeCount; $BadgePoints = Util::FloorToMultipleOf(10, $this->WormholeCount * 0.1); $AbilityGold = AbilityItem::GetGoldMultiplier(Enums\EAbility::Item_SkipLevels); $Players = $this->GetPlayers(); foreach ($Players as $Player) { $Player->IncreaseGold($AbilityGold * $this->WormholeCount); # TODO: Is gold stackable as well? if ($BadgePoints > 0) { $Player->GetTechTree()->IncreaseBadgePoints($BadgePoints); } } $this->AddChatEntry('server', '', 'Skipped ' . number_format($this->WormholeCount) . ' level' . ($this->WormholeCount === 1 ? '' : 's')); $this->WormholeCount = 0; foreach ($this->Lanes as $Lane) { $Lane->RemoveActiveWormholes(); } } $this->GenerateNewLevel(); } }
public function AddAbilityItem($AbilityId, $Quantity = 1) { if (AbilityItem::GetType($AbilityId) !== Enums\EAbilityType::Item) { $Upgrade = $this->GetUpgradeByAbility($AbilityId); if ($Upgrade !== null && $Upgrade->GetLevel() === 0) { $Upgrade->IncreaseLevel(); } $this->UnlockAbility($AbilityId); return; } if (!isset($this->AbilityItems[$AbilityId])) { $this->AbilityItems[$AbilityId] = ['ability' => $AbilityId, 'quantity' => 1]; } else { $this->AbilityItems[$AbilityId]['quantity']++; } }
public function CheckActiveAbilities(Game $Game) { foreach ($this->ActiveAbilities as $Key => $ActiveAbility) { if ($ActiveAbility->IsCooledDown($Game->Time)) { AbilityItem::HandleAbility($Game, $Game->GetLane($this->GetCurrentLane()), $this, $ActiveAbility, true); $this->RemoveActiveAbility($Key); } } }
private function GetActivePlayerAbilityMultipler($AbilityId) { # TODO: @Contex: Create an additional array that can cache the result? Instead of looping every single time... $Multiplier = 0; foreach ($this->ActivePlayerAbilities as $ActivePlayerAbility) { if ($ActivePlayerAbility->GetAbility() === $AbilityId) { $Multiplier += AbilityItem::GetMultiplier($ActivePlayerAbility->GetAbility()); } } return $Multiplier; }