예제 #1
0
 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();
     }
 }
예제 #2
0
 public function HandleAbilityUsage($Game, $RequestedAbilities)
 {
     foreach ($RequestedAbilities as $RequestedAbility) {
         $RequestedAbility['ability'] = (int) $RequestedAbility['ability'];
         if ($RequestedAbility['ability'] <= Enums\EAbility::Invalid || $RequestedAbility['ability'] >= Enums\EAbility::MaxAbilities) {
             Server::GetLogger()->debug($this->AccountId . ' tried to use an invalid ability: ' . $RequestedAbility['ability']);
             // Ignore invalid abilities
             continue;
         }
         if (isset($this->AbilityLastUsed[$RequestedAbility['ability']]) && $this->AbilityLastUsed[$RequestedAbility['ability']] >= $Game->Time || $this->IsDead() && $RequestedAbility['ability'] !== Enums\EAbility::ChangeLane && $RequestedAbility['ability'] !== Enums\EAbility::Respawn && $RequestedAbility['ability'] !== Enums\EAbility::ChatMessage) {
             // Rate limit
             continue;
         }
         $this->AbilityLastUsed[$RequestedAbility['ability']] = $Game->Time + 1;
         $AllowedAbilityTypes = [Enums\EAbilityType::Support => true, Enums\EAbilityType::Offensive => true, Enums\EAbilityType::Item => true];
         if (isset($AllowedAbilityTypes[AbilityItem::GetType($RequestedAbility['ability'])])) {
             $this->UseAbility($Game, $RequestedAbility['ability']);
             continue;
         }
         # Handle rest of the abilities below
         // TODO: @Contex: move this to AbilityItem as well?
         switch ($RequestedAbility['ability']) {
             case Enums\EAbility::ChatMessage:
                 if (!isset($RequestedAbility['message'])) {
                     break;
                 }
                 $Message = mb_substr($RequestedAbility['message'], 0, 500);
                 $Game->AddChatEntry('chat', $this->PlayerName, $Message);
                 // TODO: This is for debugging only, remove later
                 if (function_exists('SendToIRC')) {
                     SendToIRC("[GAME] 12" . $this->PlayerName . ": " . $Message);
                 }
                 break;
             case Enums\EAbility::Attack:
                 if (!isset($RequestedAbility['num_clicks'])) {
                     break;
                 }
                 $NumClicks = (int) $RequestedAbility['num_clicks'];
                 if ($NumClicks > self::MAX_CLICKS) {
                     $NumClicks = self::MAX_CLICKS;
                 } else {
                     if ($NumClicks < 1) {
                         $NumClicks = 1;
                     }
                 }
                 $Damage = $NumClicks * $this->GetTechTree()->GetDamagePerClick();
                 $Lane = $Game->GetLane($this->GetCurrentLane());
                 // Abilities
                 $Damage *= $Lane->GetDamageMultiplier();
                 if ($Lane->HasActivePlayerAbilityMaxElementalDamage()) {
                     $Damage *= $this->GetTechTree()->GetHighestElementalMultiplier();
                 }
                 // Elementals
                 $Damage *= $this->GetTechTree()->GetExtraDamageMultipliers($Lane->GetElement());
                 $this->CritDamage = 0;
                 if ($this->IsCriticalHit($Lane)) {
                     $Damage *= $this->GetTechTree()->GetDamageMultiplierCrit();
                     $this->CritDamage = $Damage;
                     $this->Stats->CritDamageDealt += $Damage;
                 }
                 $this->Stats->NumClicks += $NumClicks;
                 $this->Stats->ClickDamageDealt += $Damage;
                 $Game->NumClicks += $NumClicks;
                 $this->LaneDamageBuffer[$this->GetCurrentLane()] += $Damage;
                 # TODO: this logic isn't correct.. it shouldn't buffer the whole lane, FIX!
                 $Enemy = $Lane->GetEnemy($this->GetTarget());
                 if ($Enemy === null || $Enemy->IsDead()) {
                     break;
                 }
                 $Enemy->ClickDamageTaken += $Damage;
                 $GoldMultiplier = $Lane->GetGoldPerClickMultiplier();
                 if ($GoldMultiplier > 0) {
                     $this->IncreaseGold($GoldMultiplier * $NumClicks * $Enemy->GetGold());
                 }
                 $StealHealthMultiplier = $Lane->GetStealHealthMultiplier();
                 if ($StealHealthMultiplier > 0) {
                     $this->IncreaseHp($StealHealthMultiplier * $NumClicks * $Damage);
                 }
                 break;
             case Enums\EAbility::ChangeLane:
                 if (!isset($RequestedAbility['new_lane'])) {
                     break;
                 }
                 $NewLane = (int) $RequestedAbility['new_lane'];
                 if ($NewLane < 0 || $NewLane > 2) {
                     break;
                 }
                 $Lane = $Game->GetLane($this->GetCurrentLane());
                 $Lane->RemovePlayer($this);
                 $this->SetLane($NewLane);
                 $NewLane = $Game->GetLane($this->GetCurrentLane());
                 $NewLane->AddPlayer($this);
                 break;
             case Enums\EAbility::Respawn:
                 if ($this->IsDead() && $this->CanRespawn($Game->Time)) {
                     $this->Respawn();
                 }
                 break;
             case Enums\EAbility::ChangeTarget:
                 if (!isset($RequestedAbility['new_target'])) {
                     break;
                 }
                 $NewTarget = (int) $RequestedAbility['new_target'];
                 if ($NewTarget < 0 || $NewTarget > 3) {
                     break;
                 }
                 $this->SetTarget($NewTarget);
                 break;
         }
     }
 }