Advertisement
edmazur

bots4.net battle code

Aug 27th, 2011
488
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 20.59 KB | None | 0 0
  1. <?php
  2.  
  3. class Battle {
  4.  
  5.     private $attacker;
  6.     private $defender;
  7.     private $type;
  8.     private $log;
  9.  
  10.     private $attackerStartLevel;
  11.     private $defenderStartLevel;
  12.  
  13.     private $attackerExp;
  14.     private $defenderExp;
  15.     private $kudosChange;
  16.  
  17.     private $buffBattleSpeed;
  18.     private $buffExperience;
  19.     private $buffKudos;
  20.  
  21.     /**
  22.      * @param Bot $attacker
  23.      * @param Bot $defender
  24.      */
  25.     public function __construct($attacker, $defender, $type, $limits = null) {
  26.         $this->attacker = $attacker;
  27.         $this->defender = $defender;
  28.         $this->type = $type;
  29.         $this->log = new BattleLog($type, $defender->getId());
  30.  
  31.         $this->attackerStartLevel = $attacker->getLevel();
  32.         $this->defenderStartLevel = $defender->getLevel();
  33.  
  34.         global $db, $player;
  35.         $db->query('
  36.            SELECT SUM(unseen) AS unseen
  37.            FROM attacks
  38.            WHERE defender = ?;
  39.        ', 'i', $player->getId());
  40.         $row = $db->fetchRow();
  41.         $this->log->setAttackCount((int) $row['unseen']);
  42.  
  43.         if ($type === 'fight') {
  44.             $this->log->setAttacksLeft($limits->getAttacksLeft($defender));
  45.             $this->log->setIsOnline($defender->isOnline());
  46.  
  47.             // Dickhead trophy
  48.             if ($defender->isOnline()) {
  49.                 $attacker->getTrophies()->give(208);
  50.             }
  51.         }
  52.     }
  53.  
  54.     public function getAttackerStartLevel() {
  55.         return $this->attackerStartLevel;
  56.     }
  57.  
  58.     public function getDefenderStartLevel() {
  59.         return $this->defenderStartLevel;
  60.     }
  61.  
  62.     public function doBattle() {
  63.         $attackerHP = $this->attacker->getMaxHP();
  64.         $defenderHP = $this->defender->getMaxHP();
  65.         $attempts = 0;
  66.         $blocks = 0;
  67.         $hitsInARow = 0;
  68.  
  69.         // see who hits first
  70.         $attackerIntRoll = mt_rand(0, $this->attacker->getIntel());
  71.         $defenderIntRoll = mt_rand(0, $this->defender->getIntel());
  72.         $attackerTurn = $attackerIntRoll >= $defenderIntRoll;
  73.  
  74.         $shieldPresent = $this->attacker->hasShield() || $this->defender->hasShield();
  75.  
  76.         // statistics
  77.         $attackerStats = array();
  78.         $attackerStats['attempts'] = 0;
  79.         $attackerStats['maxHit'] = 0;
  80.         $attackerStats['blocks'] = 0;
  81.         $attackerStats['misses'] = 0;
  82.         $attackerStats['inflicted'] = 0;
  83.  
  84.         $defenderStats = array();
  85.         $defenderStats['attempts'] = 0;
  86.         $defenderStats['maxHit'] = 0;
  87.         $defenderStats['blocks'] = 0;
  88.         $defenderStats['misses'] = 0;
  89.         $defenderStats['inflicted'] = 0;
  90.  
  91.         // main battle loop
  92.         while (true) {
  93.             // bookkeeping
  94.             $hitsInARow++;
  95.             $isPrimary = $shieldPresent ? $hitsInARow === 1 : $attempts % 4 === 0 || $attempts % 4 === 1;
  96.             $attempts++;
  97.  
  98.             // do the attempt
  99.             if ($attackerTurn) {
  100.                 $attempt = $this->attempt($this->attacker, $this->defender, $isPrimary);
  101.                 $attempt['who'] = $this->attacker->getName();
  102.                 $attackerStats['attempts']++;
  103.  
  104.                 switch ($attempt['type']) {
  105.                     case 'hit':
  106.                         $this->checkDamageTrophies($this->attacker, $attempt['damage']);
  107.                         $defenderHP -= $attempt['damage'];
  108.                         $attempt['stage'] = $this->getStage($defenderHP, $this->defender->getMaxHP());
  109.                         $attempt['isPrimary'] = $isPrimary;
  110.                         $attackerStats['maxHit'] = max($attackerStats['maxHit'], $attempt['damage']);
  111.                         $attackerStats['inflicted'] += $attempt['damage'];
  112.                         break;
  113.                     case 'block':
  114.                         $defenderStats['blocks']++;
  115.                         break;
  116.                     case 'miss':
  117.                         $attackerStats['misses']++;
  118.                         break;
  119.                 }
  120.                 $attempt['condition'] = $this->doConditionChecks($this->attacker, $this->defender, $attempt['type'], $isPrimary);
  121.             } else {
  122.                 $attempt = $this->attempt($this->defender, $this->attacker, $isPrimary);
  123.                 $attempt['who'] = $this->defender->getName();
  124.                 $defenderStats['attempts']++;
  125.  
  126.                 switch ($attempt['type']) {
  127.                     case 'hit':
  128.                         $this->checkDamageTrophies($this->defender, $attempt['damage']);
  129.                         $attackerHP -= $attempt['damage'];
  130.                         $attempt['stage'] = $this->getStage($attackerHP, $this->attacker->getMaxHP());
  131.                         $attempt['isPrimary'] = $isPrimary;
  132.                         $defenderStats['maxHit'] = max($defenderStats['maxHit'], $attempt['damage']);
  133.                         $defenderStats['inflicted'] += $attempt['damage'];
  134.                         break;
  135.                     case 'block':
  136.                         $attackerStats['blocks']++;
  137.                         break;
  138.                     case 'miss':
  139.                         $defenderStats['misses']++;
  140.                         break;
  141.                 }
  142.                 $attempt['condition'] = $this->doConditionChecks($this->defender, $this->attacker, $attempt['type'], $isPrimary);
  143.             }
  144.             $this->log->addAttempt($attempt);
  145.  
  146.             // check if battle is over
  147.             if ($attackerHP <= 0 || $attempts >= 300) {
  148.                 // Skin Of Your Teeth trophy
  149.                 if ($defenderHP < 5 && $this->type === 'fight') {
  150.                     $this->defender->getTrophies()->give(125);
  151.                 }
  152.  
  153.                 // Can't Touch This trophy
  154.                 if ($defenderHP === $this->defender->getMaxHP() && $this->type === 'fight') {
  155.                     $this->defender->getTrophies()->give(126);
  156.                 }
  157.  
  158.                 $winner = $this->defender->getName();
  159.                 $isWin = false;
  160.                 break;
  161.             } else if ($defenderHP <= 0) {
  162.                 // Skin Of Your Teeth trophy
  163.                 if ($attackerHP < 5 && $this->type === 'fight') {
  164.                     $this->attacker->getTrophies()->give(125);
  165.                 }
  166.  
  167.                 // Can't Touch This trophy
  168.                 if ($attackerHP === $this->attacker->getMaxHP() && $this->type === 'fight') {
  169.                     $this->attacker->getTrophies()->give(126);
  170.                 }
  171.  
  172.                 $winner = $this->attacker->getName();
  173.                 $isWin = true;
  174.                 break;
  175.             }
  176.  
  177.             // switch turn if necessary
  178.             if ($attackerTurn) {
  179.                 if ($hitsInARow === 2 || $this->attacker->hasShield() || !$shieldPresent) {
  180.                     $attackerTurn = false;
  181.                     $hitsInARow = 0;
  182.                 }
  183.             } else {
  184.                 if ($hitsInARow === 2 || $this->defender->hasShield() || !$shieldPresent) {
  185.                     $attackerTurn = true;
  186.                     $hitsInARow = 0;
  187.                 }
  188.             }
  189.         }
  190.  
  191.         $this->log->setSummaryMeta($winner);
  192.         $this->setMetaData();
  193.         $this->setSummary($attackerStats, $defenderStats);
  194.         if ($this->type === 'train') {
  195.             // trainer trophies
  196.             if ($isWin) {
  197.                 // to avoid writing these all out, use the fact that Infant is id = 1 and trophy = 127
  198.                 $this->attacker->getTrophies()->give($this->defender->getId() + 126);
  199.             }
  200.  
  201.             $this->attackerExp = round(Formulas::getExperienceTrain(
  202.                 $isWin,
  203.                 $this->defender->getLevel(),
  204.                 $this->attacker->getLevel(),
  205.                 $attackerStats['inflicted'],
  206.                 $this->attacker->getIntel(),
  207.                 $attackerStats['blocks'] + $defenderStats['blocks'],
  208.                 $attackerStats['attempts'] + $defenderStats['attempts'])
  209.                 * ($this->getBuffExperience() / 100));
  210.             $this->kudosChange = round(Formulas::getKudosGain(
  211.                 $this->defender->getLevel(),
  212.                 $this->attacker->getLevel(),
  213.                 $attackerStats['inflicted'],
  214.                 $this->attacker->getIntel())
  215.                 * ($this->getBuffKudos() / 100));
  216.         } else {
  217.             if ($this->attacker->hasClan() && $this->defender->hasClan()) {
  218.                 $energyExchange = new EnergyExchange($this->attacker, $this->defender, $isWin, $attackerStats['attempts'] + $defenderStats['attempts']);
  219.                 $energy = $energyExchange->exchange();
  220.                 $this->log->setEnergy($energy);
  221.             }
  222.             $this->attackerExp = round(Formulas::getExperienceFight(
  223.                 $isWin,
  224.                 $this->defender->getLevel(),
  225.                 $this->attacker->getLevel(),
  226.                 $attackerStats['inflicted'],
  227.                 $this->attacker->getIntel(),
  228.                 $this->defender->getRatio(true),
  229.                 $attackerStats['blocks'] + $defenderStats['blocks'],
  230.                 $attackerStats['attempts'] + $defenderStats['attempts'],
  231.                 1) // TODO: change when clans are in
  232.                 * ($this->getBuffExperience() / 100));
  233.  
  234.             if ($isWin) {
  235.                 if ($this->defender->isOnline()) {
  236.                     $this->kudosChange = max(1, round($this->defender->getKudos() / 15 * ($this->getBuffKudos() / 100)));
  237.                 } else {
  238.                     $this->kudosChange = max(1, round($this->defender->getKudos() / 3 * ($this->getBuffKudos() / 100)));
  239.                 }
  240.             } else {
  241.                 $this->kudosChange = -1 * max(1, round($this->attacker->getKudos() / 3));
  242.             }
  243.  
  244.             // Thief trophy
  245.             if ($this->kudosChange > 100000) {
  246.                 $this->attacker->getTrophies()->give(209);
  247.             }
  248.  
  249.             // Master Thief trophy
  250.             if ($this->kudosChange > 1000000) {
  251.                 $this->attacker->getTrophies()->give(210);
  252.             }
  253.  
  254.             // award target
  255.             $this->defenderExp = Formulas::getExperienceFight(
  256.                 !$isWin,
  257.                 $this->attacker->getLevel(),
  258.                 $this->defender->getLevel(),
  259.                 $defenderStats['inflicted'],
  260.                 $this->defender->getIntel(),
  261.                 $this->attacker->getRatio(true),
  262.                 $attackerStats['blocks'] + $defenderStats['blocks'],
  263.                 $attackerStats['attempts'] + $defenderStats['attempts'],
  264.                 1); // TODO: change when clans are in
  265.             global $meta;
  266.             $this->defender->award(-1 * $this->kudosChange, $this->defenderExp, $meta->getTime() + floor($attempts * $this->getAttemptDelayMillis() / 1000), 'fight', !$isWin);
  267.         }
  268.         $repairs = $this->attacker->getRepairs();
  269.         if ($repairs['cost'] <= $this->attacker->getKudos() + $this->kudosChange) {
  270.             $enough = true;
  271.             $this->attacker->repairAll($repairs['cost']);
  272.         } else {
  273.             $enough = false;
  274.         }
  275.         $this->attacker->commitEquipmentChanges();
  276.         $this->log->setRepairs(array(
  277.             'enough' => $enough,
  278.             'cost' => $repairs['cost'],
  279.             'trashed' => $repairs['trashed']
  280.         ));
  281.         $this->setRewards($attempts, $isWin, $enough ? $repairs['cost'] : 0);
  282.  
  283.         $this->log->setDelay($this->getAttemptDelayMillis());
  284.  
  285.         global $db;
  286.         $db->query('
  287.            UPDATE buffs_bots
  288.            SET duration = duration - 1
  289.            WHERE bot_id = ? AND active = true;
  290.        ', 'i', $this->attacker->getId());
  291.         $db->query('
  292.            DELETE
  293.            FROM buffs_bots
  294.            WHERE bot_id = ? AND duration = 0;
  295.        ', 'i', $this->attacker->getId());
  296.  
  297.         // Trashed trophy
  298.         $this->attacker->getEquipment()->trashedTrophy();
  299.     }
  300.  
  301.     private function checkDamageTrophies($bot, $damage) {
  302.         if ($damage >= 10) {
  303.             $bot->getTrophies()->give(108);
  304.         }
  305.         if ($damage >= 100) {
  306.             $bot->getTrophies()->give(109);
  307.         }
  308.         if ($damage >= 700) {
  309.             $bot->getTrophies()->give(110);
  310.         }
  311.         if ($damage >= 2000) {
  312.             $bot->getTrophies()->give(111);
  313.         }
  314.     }
  315.  
  316.     public function doConditionChecks($hitter, $hittee, $type, $isPrimary) {
  317.         $damage = array();
  318.  
  319.         switch ($type) {
  320.             case 'hit':
  321.                 // hitter's weapon
  322.                 $damage[] = $this->doConditionCheck($hitter, $isPrimary ? $hitter->getEquipment()->getPrimary() : $hitter->getEquipment()->getSecondary());
  323.                 // hittee's armor
  324.                 $damage[] = $this->doConditionCheck($hittee, $hittee->getEquipment()->getBody());
  325.                 $damage[] = $this->doConditionCheck($hittee, $hittee->getEquipment()->getHelmet());
  326.                 $damage[] = $this->doConditionCheck($hittee, $hittee->getEquipment()->getGloves());
  327.                 $damage[] = $this->doConditionCheck($hittee, $hittee->getEquipment()->getBoots());
  328.                 break;
  329.             case 'block':
  330.                 // hitter's weapon
  331.                 $damage[] = $this->doConditionCheck($hitter, $isPrimary ? $hitter->getEquipment()->getPrimary() : $hitter->getEquipment()->getSecondary());
  332.                 // hittee's shield
  333.                 if ($hittee->hasShield()) {
  334.                     $damage[] = $this->doConditionCheck($hittee, $hittee->getEquipment()->getSecondary());
  335.                 }
  336.                 break;
  337.             case 'miss':
  338.                 // nothing
  339.                 break;
  340.         }
  341.  
  342.         // remove nulls from array
  343.         return array_values(array_filter($damage, function ($val) { return isset($val); }));
  344.     }
  345.  
  346.     public function doConditionCheck($bot, $item) {
  347.         if (!isset($item) || $item->getName() === 'fist' || $item->getName() === 'none') {
  348.             return null;
  349.         } else {
  350.             if (mt_rand2() < $item->getDamageProbability($bot->getStr(), $bot->getDex())) {
  351.                 $item->doDamage();
  352.                 return array(
  353.                     'who' => $bot->getName(),
  354.                     'item' => $item->getName()
  355.                 );
  356.             } else {
  357.                 return null;
  358.             }
  359.         }
  360.     }
  361.  
  362.     public function setRewards($attempts, $isWin, $repairCost) {
  363.         global $meta;
  364.  
  365.         $this->levelBefore = $this->attacker->getLevel();
  366.         $this->attacker->award($this->kudosChange - $repairCost, $this->attackerExp, $meta->getTime() + floor($attempts * $this->getAttemptDelayMillis() / 1000), $this->type, $isWin);
  367.         $this->levelAfter = $this->attacker->getLevel();
  368.  
  369.         $this->log->setRewards(
  370.             $this->kudosChange,
  371.             $this->attacker->getKudos(),
  372.             $this->attackerExp,
  373.             $this->attacker->getXPTNL(),
  374.             $this->levelAfter - $this->levelBefore);
  375.     }
  376.  
  377.     /**
  378.      * @param array $attackerStats
  379.      * @param array $defenderStats
  380.      */
  381.     private function setSummary($attackerStats, $defenderStats) {
  382.         if ($attackerStats['attempts'] !== 0) {
  383.             $attackerStats['average'] = round($attackerStats['inflicted'] / $attackerStats['attempts'], 1);
  384.         } else {
  385.             $attackerStats['average'] = 0;
  386.         }
  387.  
  388.         if ($defenderStats['attempts'] !== 0) {
  389.             $defenderStats['average'] = round($defenderStats['inflicted'] / $defenderStats['attempts'], 1);
  390.         } else {
  391.             $defenderStats['average'] = 0;
  392.         }
  393.  
  394.         $this->log->setSummary($attackerStats, $defenderStats);
  395.     }
  396.  
  397.     /**
  398.      * @param Bot $hitter
  399.      * @param Bot $hittee
  400.      * @param bool $isPrimary Whether or not this attempt is being made with the primary weapon.
  401.      * @return array Returns an attempt array.
  402.      */
  403.     private function attempt($hitter, $hittee, $isPrimary) {
  404.         // check for miss
  405.         $hitChance = $hitter->getHitChance($hittee);
  406.         if (mt_rand2() > $hitChance) {
  407.             $attempt['type'] = 'miss';
  408.             return $attempt;
  409.         }
  410.  
  411.         // check for block
  412.         $blockChance = $hittee->getBlock();
  413.         if (mt_rand2() < $blockChance) {
  414.             $attempt['type'] = 'block';
  415.             return $attempt;
  416.         }
  417.  
  418.         // calculate damage
  419.         if ($isPrimary) {
  420.             $minDamage = $hitter->getPrimaryMinDamage();
  421.             $maxDamage = $hitter->getPrimaryMaxDamage();
  422.         } else {
  423.             $minDamage = $hitter->getSecondaryMinDamage();
  424.             $maxDamage = $hitter->getSecondaryMaxDamage();
  425.         }
  426.         $attempt['type'] = 'hit';
  427.         $attempt['damage'] = round(mt_rand($minDamage, $maxDamage) * (1 - $hittee->getAbsorb()));
  428.         return $attempt;
  429.     }
  430.  
  431.     private function setMetaData() {
  432.         $this->log->setAttacker(
  433.             $this->attacker->getName(),
  434.             $this->attacker->getColor(),
  435.             $this->attacker->getMaxHP(),
  436.             $this->attacker->getPrimaryName(),
  437.             $this->attacker->hasShield() ? null : $this->attacker->getSecondaryName());
  438.  
  439.         $this->log->setDefender(
  440.             $this->defender->getName(),
  441.             $this->defender->getColor(),
  442.             $this->defender->getPrimaryName(),
  443.             $this->defender->hasShield() ? null : $this->defender->getSecondaryName());
  444.     }
  445.  
  446.     private function getStage($HP, $maxHP) {
  447.         $percentage = $HP / $maxHP;
  448.         switch (true) {
  449.             case $percentage >= 1.0:
  450.                 return 0;
  451.             case $percentage >= 0.8:
  452.                 return 1;
  453.             case $percentage >= 0.6:
  454.                 return 2;
  455.             case $percentage >= 0.4:
  456.                 return 3;
  457.             case $percentage >= 0.2:
  458.                 return 4;
  459.             case $percentage > 0.0:
  460.                 return 5;
  461.             default:
  462.                 return 6;
  463.         }
  464.     }
  465.  
  466.     public function getLog($settings) {
  467.         return $this->log->toJSON($settings);
  468.     }
  469.  
  470.     public function getAttacker() {
  471.         return $this->attacker;
  472.     }
  473.  
  474.     public function getDefender() {
  475.         return $this->defender;
  476.     }
  477.  
  478.     public function getAttackerExp() {
  479.         return $this->attackerExp;
  480.     }
  481.  
  482.     public function getDefenderExp() {
  483.         return $this->defenderExp;
  484.     }
  485.  
  486.     // positive means attacker won, negative means defender won
  487.     public function getKudosChange() {
  488.         return $this->kudosChange;
  489.     }
  490.  
  491.     public function attackerWon() {
  492.         return $this->kudosChange > 0;
  493.     }
  494.  
  495.     public function getBuffBattleSpeed() {
  496.         if (!isset($this->buffBattleSpeed)) {
  497.             $this->buffBattleSpeed = 100;
  498.             foreach ($this->attacker->getBuffs() as $buff) {
  499.                 if (!$buff->isActive()) {
  500.                     continue;
  501.                 }
  502.                 foreach ($buff->getEffects() as $effect) {
  503.                     if ($effect->getName() === 'battle speed' && $effect->getValue() > $this->buffBattleSpeed) {
  504.                         $this->buffBattleSpeed = $effect->getValue();
  505.                     }
  506.                 }
  507.             }
  508.         }
  509.         return $this->buffBattleSpeed;
  510.     }
  511.  
  512.     // TODO: currently rounds to nearest 20 because battle code is rather fragile...should be able to remove this restriction eventually
  513.     public function getAttemptDelayMillis() {
  514.         $baseDelay = round(800 * 100 / $this->getBuffBattleSpeed());
  515.         return floor(($baseDelay + 20 - 1) / 20) * 20;
  516.     }
  517.  
  518.     public function getBuffExperience() {
  519.         if (!isset($this->buffExperience)) {
  520.             $this->buffExperience = 100;
  521.             foreach ($this->attacker->getBuffs() as $buff) {
  522.                 if (!$buff->isActive()) {
  523.                     continue;
  524.                 }
  525.                 foreach ($buff->getEffects() as $effect) {
  526.                     if ($effect->getName() === 'experience' && $effect->getValue() > $this->buffExperience) {
  527.                         $this->buffExperience = $effect->getValue();
  528.                     }
  529.                 }
  530.             }
  531.         }
  532.         return $this->buffExperience;
  533.     }
  534.  
  535.     public function getBuffKudos() {
  536.         if (!isset($this->buffKudos)) {
  537.             $this->buffKudos = 100;
  538.             foreach ($this->attacker->getBuffs() as $buff) {
  539.                 if (!$buff->isActive()) {
  540.                     continue;
  541.                 }
  542.                 foreach ($buff->getEffects() as $effect) {
  543.                     if ($effect->getName() === 'kudos' && $effect->getValue() > $this->buffKudos) {
  544.                         $this->buffKudos = $effect->getValue();
  545.                     }
  546.                 }
  547.             }
  548.         }
  549.         return $this->buffKudos;
  550.     }
  551.  
  552. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement