Abstract Stats Collection
Abstract the stats collection portions of DPClass so that additional providers might easily be substituted in the future. Initial abstract is weak and can expand when an additional provider is actually found.
This commit is contained in:
parent
8f50d70fa5
commit
4124cd55f3
8 changed files with 285 additions and 200 deletions
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
include_once(__DIR__ . '/libs/dpclass.php');
|
||||
include_once(__DIR__ . '/libs/stats.php');
|
||||
include_once(__DIR__ . '/libs/diabloclass.php');
|
||||
|
||||
if($_POST['submit']) {
|
||||
$diabloProgressUrl = trim($_POST['url']);
|
||||
|
@ -8,15 +9,14 @@ if($_POST['submit']) {
|
|||
die('Bad URL. Please enter the entire diablo progress URL.<br/><br/>Example: http://www.diabloprogress.com/hero/celanian-1548/HsuMing/21706367');
|
||||
}
|
||||
|
||||
$curl = curl_init();
|
||||
curl_setopt($curl, CURLOPT_URL, $diabloProgressUrl);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
|
||||
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
$contents = curl_exec($curl);
|
||||
curl_close($curl);
|
||||
|
||||
$character = DPClassFactory::createClassObject($contents);
|
||||
$stats = StatsFactory::createStatsObject($diabloProgressUrl);
|
||||
|
||||
if($stats === FALSE) {
|
||||
die('Bad provider. Either your provider could not be detected or we do not support your provider at this time.');
|
||||
}
|
||||
|
||||
$character = DiabloClassFactory::createClassObject($stats->class, $stats);
|
||||
|
||||
if($character === FALSE) {
|
||||
die('Bad class. Either your class could not be detected or we do not support your class at this time.');
|
||||
|
|
|
@ -1,2 +1,8 @@
|
|||
<?php
|
||||
class Barbarian extends DPClass { }
|
||||
class Barbarian extends DiabloClass {
|
||||
function __construct($stats) {
|
||||
$this->class = $stats->class;
|
||||
|
||||
parent::__construct($stats);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,13 @@
|
|||
<?php
|
||||
class DemonHunter extends DPClass {
|
||||
class DemonHunter extends DiabloClass {
|
||||
function __construct($stats) {
|
||||
$this->class = $stats->class;
|
||||
|
||||
parent::__construct($stats);
|
||||
}
|
||||
|
||||
function EHPScore() {
|
||||
$ehp = $this->getStat('EHP Unbuffed');
|
||||
$ehp = $this->stats->getStat('EHP Unbuffed');
|
||||
|
||||
if($ehp <= 500000) {
|
||||
return $ehp / 10000;
|
||||
|
@ -15,20 +21,20 @@ class DemonHunter extends DPClass {
|
|||
}
|
||||
|
||||
function sustainScore() {
|
||||
$effectiveLs = $this->getStat('DPS Unbuffed') *
|
||||
$this->getStat('Life Steal') * .2;
|
||||
$mitigation = $this->getStat('EHP Unbuffed') / $this->getStat('Life');
|
||||
$effectiveLs = $this->stats->getStat('DPS Unbuffed') *
|
||||
$this->stats->getStat('Life Steal') * .2;
|
||||
$mitigation = $this->stats->getStat('EHP Unbuffed') / $this->stats->getStat('Life');
|
||||
|
||||
return 1 + $mitigation * ($this->getStat('Life on Hit') *
|
||||
($this->getStat('Attacks per Second') + 1) / 2 +
|
||||
$this->getStat('Life per Second') + $effectiveLs) /
|
||||
($this->getStat('Life') * $this->EHPScore() * 10000 /
|
||||
$this->getStat('EHP Unbuffed'));
|
||||
return 1 + $mitigation * ($this->stats->getStat('Life on Hit') *
|
||||
($this->stats->getStat('Attacks per Second') + 1) / 2 +
|
||||
$this->stats->getStat('Life per Second') + $effectiveLs) /
|
||||
($this->stats->getStat('Life') * $this->EHPScore() * 10000 /
|
||||
$this->stats->getStat('EHP Unbuffed'));
|
||||
}
|
||||
|
||||
function miscScore() {
|
||||
return 1 + ($this->getStat('+Maximum Discipline') / 2 +
|
||||
$this->getStat('+Hatred Regenerated per Second') * 2 +
|
||||
$this->getStat('+Discipline Regenerated per Second') * 15) / 100;
|
||||
return 1 + ($this->stats->getStat('+Maximum Discipline') / 2 +
|
||||
$this->stats->getStat('+Hatred Regenerated per Second') * 2 +
|
||||
$this->stats->getStat('+Discipline Regenerated per Second') * 15) / 100;
|
||||
}
|
||||
}
|
88
libs/diabloclass.php
Normal file
88
libs/diabloclass.php
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
class DiabloClassFactory {
|
||||
function createClassObject($class, $stats) {
|
||||
if(!DiabloClassFactory::isValidClass($class)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
include_once(__DIR__ . "/$class.php");
|
||||
return new $class($stats);
|
||||
}
|
||||
|
||||
private
|
||||
function isValidClass($class) {
|
||||
$classes = array(
|
||||
'barbarian',
|
||||
'demonhunter'
|
||||
);
|
||||
|
||||
return in_array($class, $classes);
|
||||
}
|
||||
}
|
||||
|
||||
class DiabloClass {
|
||||
var $class;
|
||||
var $stats = array();
|
||||
|
||||
function __construct($stats) {
|
||||
$this->stats = $stats;
|
||||
}
|
||||
|
||||
function hallScore() {
|
||||
return $this->DPSScore() * $this->EHPScore() * $this->sustainScore()
|
||||
* $this->moveScore() * $this->paragonScore() * $this->miscScore();
|
||||
}
|
||||
|
||||
function DPSScore() {
|
||||
return $this->stats->getStat('DPS Unbuffed') / 1000;
|
||||
}
|
||||
|
||||
function EHPScore() {
|
||||
$ehp = $this->stats->getStat('EHP Unbuffed');
|
||||
|
||||
if($ehp < 1000000) {
|
||||
return $ehp / 10000;
|
||||
} elseif(1000000 <= $ehp && $ehp <= 2000000) {
|
||||
return 100 + ($ehp - 1000000) / 20000;
|
||||
} elseif(2000000 <= $ehp && $ehp <= 5000000) {
|
||||
return 150 + ($ehp - 2000000) / 40000;
|
||||
} elseif($ehp >= 5000000) {
|
||||
return 225 + ($ehp - 5000000) / 100000;
|
||||
}
|
||||
}
|
||||
|
||||
function sustainScore() {
|
||||
$effectiveLS = $this->stats->getStat('DPS Unbuffed') *
|
||||
$this->stats->getStat('Life Steal') * .5;
|
||||
$mitigation = $this->stats->getStat('EHP Unbuffed') / $this->stats->getStat('Life');
|
||||
|
||||
$rawSustainScore = 1 + $mitigation * ($this->stats->getStat('Life on Hit') *
|
||||
(1 + ($this->stats->getStat('Attacks per Second') - 1) / 2) +
|
||||
$effectiveLS + $this->stats->getStat('Life per Second')) /
|
||||
($this->stats->getStat('Life') * $this->EHPScore() * 10000 /
|
||||
$this->stats->getStat('EHP Unbuffed'));
|
||||
|
||||
if($rawSustainScore <= 1.5) {
|
||||
return $rawSustainScore;
|
||||
} elseif(1.5 < $rawSustainScore && $rawSustainScore <= 2) {
|
||||
return 1.5 + ($rawSustainScore - 1.5) / 2;
|
||||
} elseif(2 < $rawSustainScore && $rawSustainScore <= 3) {
|
||||
return 1.75 + ($rawSustainScore - 2) / 4;
|
||||
} else {
|
||||
return 2 + ($rawSustainScore - 3) / 10;
|
||||
}
|
||||
}
|
||||
|
||||
function moveScore() {
|
||||
return ($this->stats->getStat('Movement Speed') > .25) ? 1.25 : 1 + $this->stats->getStat('Movement Speed');
|
||||
}
|
||||
|
||||
function paragonScore() {
|
||||
return 1 + $this->stats->getStat('Paragon Level') / 2 / 100;
|
||||
}
|
||||
|
||||
function miscScore() {
|
||||
return 1;
|
||||
}
|
||||
}
|
90
libs/diabloprogress_stats.php
Normal file
90
libs/diabloprogress_stats.php
Normal file
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
class DiabloProgressStats extends Stats {
|
||||
function parse() {
|
||||
$this->class = $this->findClass();
|
||||
$this->parseStats();
|
||||
}
|
||||
|
||||
protected
|
||||
function isParagonMaxed() {
|
||||
return $this->getStat('Paragon Level') == 100;
|
||||
}
|
||||
|
||||
private
|
||||
function findClass() {
|
||||
preg_match('{<span class="diablo_.*?">(.*?)</span>}', $this->html, $class);
|
||||
|
||||
return str_replace(' ', '', strtolower($class[1]));
|
||||
}
|
||||
|
||||
function parseStats() {
|
||||
preg_match_all('{<div class="char_attr"><nobr><span class="char_attr_name">(.*?):<\/span> <span class="char_attr_value">(.*?)<\/span><\/nobr><\/div>}', $this->html, $attributes);
|
||||
|
||||
for($i = 0; $i < count($attributes[0]); $i++) {
|
||||
$attributes[2][$i] = str_replace(',', '', $attributes[2][$i]);
|
||||
if(preg_match('/%/', $attributes[2][$i]) > 0) {
|
||||
$attributes[2][$i] = str_replace('%', '', $attributes[2][$i]);
|
||||
$attributes[2][$i] /= 100;
|
||||
}
|
||||
$this->stats[$attributes[1][$i]] = $attributes[2][$i];
|
||||
}
|
||||
|
||||
$this->stats['Gem Life'] = $this->calculateGemLife();
|
||||
$this->modifyExpBonus();
|
||||
|
||||
$this->stats['All Elemental Damage'] = $this->elementalDamage();
|
||||
|
||||
$this->modifyDPSUnbuffed();
|
||||
$this->modifyEHP();
|
||||
$this->modifyHP();
|
||||
}
|
||||
|
||||
function elementalDamage() {
|
||||
$totalElemental = 0;
|
||||
foreach($this->stats as $stat => $value) {
|
||||
if(preg_match('/\+DPS \(.*\)/', $stat) > 0) {
|
||||
$totalElemental += $value;
|
||||
}
|
||||
}
|
||||
|
||||
return ($totalElemental > 0) ? $totalElemental : 0;
|
||||
}
|
||||
|
||||
function calculateGemLife() {
|
||||
if($this->isParagonMaxed()) return 0;
|
||||
|
||||
switch($this->getStat('Exp Bonus')) {
|
||||
case .19: return .12;
|
||||
case .21: return .14;
|
||||
case .25: return .15;
|
||||
case .27: return .16;
|
||||
case .29: return .17;
|
||||
case .31: return .18;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function modifyExpBonus() {
|
||||
if($this->getStat('Exp Bonus') >= .35) {
|
||||
$this->stats['Exp Bonus'] = $this->getStat('Exp Bonus') - .35;
|
||||
}
|
||||
}
|
||||
|
||||
function modifyDPSUnbuffed() {
|
||||
$this->stats['DPS Unbuffed'] = $this->getStat('DPS Unbuffed') *
|
||||
max(1, 1 + ($this->getStat('+DPS Against Elites') / 2));
|
||||
}
|
||||
|
||||
function modifyEHP() {
|
||||
$this->stats['EHP Unbuffed'] = $this->getStat('EHP Unbuffed') *
|
||||
(1 + $this->getStat('Life Bonus') + $this->getStat('Gem Life')) /
|
||||
(1 + $this->getStat('Life Bonus'));
|
||||
}
|
||||
|
||||
function modifyHP() {
|
||||
$this->stats['Life'] = $this->getStat('Life') *
|
||||
(1 + $this->getStat('Life Bonus') + $this->getStat('Gem Life')) /
|
||||
(1 + $this->getStat('Life Bonus'));
|
||||
}
|
||||
}
|
177
libs/dpclass.php
177
libs/dpclass.php
|
@ -1,177 +0,0 @@
|
|||
<?php
|
||||
|
||||
class DPClassFactory {
|
||||
function createClassObject($characterPage) {
|
||||
$class = DPClassFactory::findClass($characterPage);
|
||||
|
||||
include_once(__DIR__ . "/$class.php");
|
||||
|
||||
switch($class) {
|
||||
case 'barbarian':
|
||||
return new Barbarian($characterPage);
|
||||
case 'demonhunter':
|
||||
return new DemonHunter($characterPage);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private
|
||||
function findClass($characterPage) {
|
||||
preg_match('{<span class="diablo_.*?">(.*?)</span>}', $characterPage, $class);
|
||||
|
||||
return str_replace(' ', '', strtolower($class[1]));
|
||||
}
|
||||
}
|
||||
|
||||
class DPClass {
|
||||
var $dpHTML;
|
||||
var $class;
|
||||
var $stats = array();
|
||||
var $items = array();
|
||||
|
||||
function __construct($characterPage) {
|
||||
$this->dpHTML = $characterPage;
|
||||
|
||||
$this->class = get_class($this);
|
||||
|
||||
$this->parseStats();
|
||||
}
|
||||
|
||||
function hallScore() {
|
||||
return $this->DPSScore() * $this->EHPScore() * $this->sustainScore()
|
||||
* $this->moveScore() * $this->paragonScore() * $this->miscScore();
|
||||
}
|
||||
|
||||
function DPSScore() {
|
||||
return $this->getStat('DPS Unbuffed') / 1000;
|
||||
}
|
||||
|
||||
function EHPScore() {
|
||||
$ehp = $this->getStat('EHP Unbuffed');
|
||||
|
||||
if($ehp < 1000000) {
|
||||
return $ehp / 10000;
|
||||
} elseif(1000000 <= $ehp && $ehp <= 2000000) {
|
||||
return 100 + ($ehp - 1000000) / 20000;
|
||||
} elseif(2000000 <= $ehp && $ehp <= 5000000) {
|
||||
return 150 + ($ehp - 2000000) / 40000;
|
||||
} elseif($ehp >= 5000000) {
|
||||
return 225 + ($ehp - 5000000) / 100000;
|
||||
}
|
||||
}
|
||||
|
||||
function sustainScore() {
|
||||
$effectiveLS = $this->getStat('DPS Unbuffed') *
|
||||
$this->getStat('Life Steal') * .5;
|
||||
$mitigation = $this->getStat('EHP Unbuffed') / $this->getStat('Life');
|
||||
|
||||
$rawSustainScore = 1 + $mitigation * ($this->getStat('Life on Hit') *
|
||||
(1 + ($this->getStat('Attacks per Second') - 1) / 2) +
|
||||
$effectiveLS + $this->getStat('Life per Second')) /
|
||||
($this->getStat('Life') * $this->EHPScore() * 10000 /
|
||||
$this->getStat('EHP Unbuffed'));
|
||||
|
||||
if($rawSustainScore <= 1.5) {
|
||||
return $rawSustainScore;
|
||||
} elseif(1.5 < $rawSustainScore && $rawSustainScore <= 2) {
|
||||
return 1.5 + ($rawSustainScore - 1.5) / 2;
|
||||
} elseif(2 < $rawSustainScore && $rawSustainScore <= 3) {
|
||||
return 1.75 + ($rawSustainScore - 2) / 4;
|
||||
} else {
|
||||
return 2 + ($rawSustainScore - 3) / 10;
|
||||
}
|
||||
}
|
||||
|
||||
function moveScore() {
|
||||
return ($this->getStat('Movement Speed') > .25) ? 1.25 : 1 + $this->getStat('Movement Speed');
|
||||
}
|
||||
|
||||
function paragonScore() {
|
||||
return 1 + $this->stats['Paragon Level'] / 2 / 100;
|
||||
}
|
||||
|
||||
function miscScore() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
protected
|
||||
function isParagonMaxed() {
|
||||
return $this->getStat('Paragon Level') == 100;
|
||||
}
|
||||
|
||||
function getStat($name) {
|
||||
return (isset($this->stats[$name])) ? $this->stats[$name] : 0;
|
||||
}
|
||||
|
||||
private
|
||||
function parseStats() {
|
||||
preg_match_all('{<div class="char_attr"><nobr><span class="char_attr_name">(.*?):<\/span> <span class="char_attr_value">(.*?)<\/span><\/nobr><\/div>}', $this->dpHTML, $attributes);
|
||||
|
||||
for($i = 0; $i < count($attributes[0]); $i++) {
|
||||
$attributes[2][$i] = str_replace(',', '', $attributes[2][$i]);
|
||||
if(preg_match('/%/', $attributes[2][$i]) > 0) {
|
||||
$attributes[2][$i] = str_replace('%', '', $attributes[2][$i]);
|
||||
$attributes[2][$i] /= 100;
|
||||
}
|
||||
$this->stats[$attributes[1][$i]] = $attributes[2][$i];
|
||||
}
|
||||
|
||||
$this->stats['Gem Life'] = $this->calculateGemLife();
|
||||
$this->modifyExpBonus();
|
||||
|
||||
$this->stats['All Elemental Damage'] = $this->elementalDamage();
|
||||
|
||||
$this->modifyDPSUnbuffed();
|
||||
$this->modifyEHP();
|
||||
$this->modifyHP();
|
||||
}
|
||||
|
||||
function elementalDamage() {
|
||||
$totalElemental = 0;
|
||||
foreach($this->stats as $stat => $value) {
|
||||
if(preg_match('/\+DPS \(.*\)/', $stat) > 0) {
|
||||
$totalElemental += $value;
|
||||
}
|
||||
}
|
||||
|
||||
return ($totalElemental > 0) ? $totalElemental : 0;
|
||||
}
|
||||
|
||||
function calculateGemLife() {
|
||||
if($this->isParagonMaxed()) return 0;
|
||||
|
||||
switch($this->getStat('Exp Bonus')) {
|
||||
case .19: return .12;
|
||||
case .21: return .14;
|
||||
case .25: return .15;
|
||||
case .27: return .16;
|
||||
case .29: return .17;
|
||||
case .31: return .18;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function modifyExpBonus() {
|
||||
if($this->getStat('Exp Bonus') >= .35) {
|
||||
$this->stats['Exp Bonus'] = $this->getStat('Exp Bonus') - .35;
|
||||
}
|
||||
}
|
||||
|
||||
function modifyDPSUnbuffed() {
|
||||
$this->stats['DPS Unbuffed'] = $this->getStat('DPS Unbuffed') *
|
||||
max(1, 1 + ($this->getStat('+DPS Against Elites') / 2));
|
||||
}
|
||||
|
||||
function modifyEHP() {
|
||||
$this->stats['EHP Unbuffed'] = $this->getStat('EHP Unbuffed') *
|
||||
(1 + $this->getStat('Life Bonus') + $this->getStat('Gem Life')) /
|
||||
(1 + $this->getStat('Life Bonus'));
|
||||
}
|
||||
|
||||
function modifyHP() {
|
||||
$this->stats['Life'] = $this->getStat('Life') *
|
||||
(1 + $this->getStat('Life Bonus') + $this->getStat('Gem Life')) /
|
||||
(1 + $this->getStat('Life Bonus'));
|
||||
}
|
||||
}
|
63
libs/stats.php
Normal file
63
libs/stats.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
class StatsFactory {
|
||||
function createStatsObject($characterPageUrl) {
|
||||
$provider = StatsFactory::findProvider($characterPageUrl);
|
||||
|
||||
if(!StatsFactory::isValidProvider($provider)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
include_once(__DIR__ . '/' . $provider . '_stats.php');
|
||||
$class = $provider . 'Stats';
|
||||
return new $class($characterPageUrl);
|
||||
}
|
||||
|
||||
private
|
||||
function findProvider($characterPageUrl) {
|
||||
preg_match('{.*\.(.*)\..*/.*}', $characterPageUrl, $provider);
|
||||
|
||||
return strtolower($provider[1]);
|
||||
}
|
||||
|
||||
function isValidProvider($provider) {
|
||||
$providers = array(
|
||||
'diabloprogress'
|
||||
);
|
||||
|
||||
return in_array($provider, $providers);
|
||||
}
|
||||
}
|
||||
|
||||
class Stats {
|
||||
private $url;
|
||||
protected $html;
|
||||
public $stats = array();
|
||||
public $class;
|
||||
|
||||
function __construct($characterPageUrl) {
|
||||
$this->url = $characterPageUrl;
|
||||
$this->html = $this->getPageContents();
|
||||
|
||||
$this->parse();
|
||||
}
|
||||
|
||||
function getStat($name) {
|
||||
return (isset($this->stats[$name])) ? $this->stats[$name] : 0;
|
||||
}
|
||||
|
||||
private
|
||||
function getPageContents() {
|
||||
$curl = curl_init();
|
||||
curl_setopt($curl, CURLOPT_URL, $this->url);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
|
||||
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
$contents = curl_exec($curl);
|
||||
curl_close($curl);
|
||||
|
||||
//handle httpCodes and other failures
|
||||
|
||||
return $contents;
|
||||
}
|
||||
}
|
9
stats_test.php
Normal file
9
stats_test.php
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
include_once(__DIR__ . '/libs/stats.php');
|
||||
|
||||
$stats = StatsFactory::createStatsObject('http://www.diabloprogress.com/hero/celanian-1548/HsuMing/21706367');
|
||||
|
||||
print_r($stats->class);
|
||||
echo "\n\n";
|
||||
print_r($stats->stats);
|
||||
echo "\n\n";
|
Loading…
Reference in a new issue