/** * Verifies character data, ensures all fields are valid. */ public function Attack(Being $AnEnemy, $Spell = true) { $Result = array(); $Result['Rounds'] = array(); $Result['Masteries'] = array(); $MonsterMasteryChance = 0; //Modify AnEnemy if is monster. Make it like a character. if (get_class($AnEnemy) == 'Entities\\Monster') { //Give our monster some gear! $AnEnemy->Equipment = array(); for ($Index = 0; $Index < 2; $Index++) { $Item = new \Entities\Item(); if ($AnEnemy->Strength > $AnEnemy->Intelligence) { $Item->SlotType = 0; $Item->MasteryType = 2; $Item->ItemClass = $AnEnemy->WeaponClass; } else { $Item->SlotType = 3; if (mt_rand(1, 100) < 25) { $Item->MasteryType = 11; //Heal } else { $Item->MasteryType = 6; //Fire } $Item->ItemClass = $AnEnemy->SpellClass; } $AnEnemy->Equipment[$Index] = $Item; } if ($AnEnemy->MasteryBonus) { $MonsterMasteryChance = $AnEnemy->MasteryBonus; } $Armor = new \Entities\Item(); $Armor->SlotType = 1; $Armor->ItemClass = $AnEnemy->ArmorClass; $AnEnemy->Equipment[2] = $Armor; } //0 is the player, 1 is the enemy. Loop through to create "Rounds", or rows in the result set. for ($Index = 0; $Index < 2; $Index++) { $NumWeapons = 0; $EnemyArmorClass = 0; $Being = null; $EnemyBeing = null; $DamageType = 0; //If this is the enemy... if ($Index == 1) { $Being = $AnEnemy; $EnemyBeing = $this; if ($AnEnemy->Strength > $AnEnemy->Intelligence) { $DamageType = 0; } else { $DamageType = 1; } } else { $EnemyBeing = $AnEnemy; $Being = $this; $DamageType = $Spell; } //Get Armor Class for the attacked foreach ($EnemyBeing->Equipment as $AnItem) { if ($AnItem->SlotType == 1) { $EnemyArmorClass += $AnItem->ItemClass; } } //Get the attacker's weapons. foreach ($Being->Equipment as $AnItem) { if (!$Spell) { if ($AnItem->SlotType == 0) { $NumWeapons++; } } else { if ($AnItem->SlotType == 3) { $NumWeapons++; } } } //Fill the "Round" with attack data. $PlayerRow = array(); foreach ($Being->Equipment as $AnItem) { $DamageStat = 0; $HitStat = 0; $MissStat = 0; $IsWeapon = false; $IsHeal = false; $AttackType = $DamageType; if (!$DamageType) { //If is a weapon if ($AnItem->SlotType == 0) { if ($AnItem->MasteryType != 0) { $AttackType = 0; $IsWeapon = true; $DamageStat = $Being->Strength; $HitStat = $Being->Dexterity; $MissStat = $EnemyBeing->Dexterity; } } } else { //If is a spell if ($AnItem->SlotType == 3) { $DamageStat = $Being->Intelligence; $HitStat = $Being->Wisdom; $MissStat = $EnemyBeing->Wisdom; if ($AnItem->MasteryType != 11) { $AttackType = 1; $IsWeapon = true; } else { $AttackType = 2; $IsHeal = true; } } } //Initiative - shared between all damage types $Initiative = 0; if ($IsWeapon || $IsHeal) { $InitStat = max($Being->Dexterity, $Being->Wisdom); $EnemyInitStat = max($EnemyBeing->Dexterity, $EnemyBeing->Wisdom); $Initiative = \gauss_ms($InitStat, $InitStat * 0.2) - \gauss_ms($EnemyInitStat, $EnemyInitStat * 0.2); //Damage/Heal calculations $ActualDamage = 0; $IsCritical = false; if ($IsWeapon) { $ActualDamage = 0; $ChanceToHitBonus = 1; $Mastery = 0; if (get_class($Being) == 'Entities\\Character') { $Mastery = $Being->Masteries[$AnItem->MasteryType]['Value']; } $ChanceToHit = $HitStat / $MissStat * 50 * (1 + $Mastery / 100) * $ChanceToHitBonus; if (mt_rand(1, 100) < $ChanceToHit) { $EnemyArmorMastery = 0; if (get_class($EnemyBeing) == 'Entities\\Character') { $EnemyArmorMastery = $EnemyBeing->Masteries[0]['Value']; } $ItemClassBonus = $Being->WeaponClassBonus; if ($DamageType) { $ItemClassBonus = $Being->SpellClassBonus; } $BaseDamage = pow(1.15, $AnItem->ItemClass + $ItemClassBonus - ($EnemyArmorClass + $EnemyBeing->ArmorClassBonus + round($EnemyArmorMastery / 5))); $ActualDamage = \gauss_ms($DamageStat / 3, $DamageStat / 3 * 0.1) * $BaseDamage; $ActualDamage = round($ActualDamage * (1 / max(pow($NumWeapons, 1.5), 1)) / (2 / 3)); $CritChance = mt_rand(1, 20 + $Mastery / 10); if ($CritChance > 19) { $IsCritical = true; $ActualDamage = round($ActualDamage * \gauss_ms(3, 0.5)); } } } else { if ($IsHeal) { $BaseHeal = $Being->Intelligence / 10; $ActualDamage = round(\gauss_ms($BaseHeal, $BaseHeal * 0.2) * $AnItem->ItemClass / 5); } } //Insert Damage/Heal into Result array ordered by initiative $Inserted = false; $PlayerRow = array('Damage' => $ActualDamage, 'Actor' => $Index, 'Type' => $AttackType, 'MasteryType' => $AnItem->MasteryType, 'Initiative' => $Initiative, 'IsCritical' => $IsCritical); for ($ArrayIndex = 0; $ArrayIndex < count($Result); $ArrayIndex++) { if ($Initiative > @$Result['Rounds'][$ArrayIndex]['Initiative']) { array_splice($Result['Rounds'], $ArrayIndex, 0, array($PlayerRow)); $Inserted = true; break; } } if (!$Inserted) { array_push($Result['Rounds'], $PlayerRow); } } } } $PlayerWins = false; $EnemyWins = false; $Index = 0; //Clean up and finalize the Result while ($Index < count($Result['Rounds'])) { if (isset($Result['Rounds'][$Index])) { if ($EnemyWins || $PlayerWins) { $Run = true; for ($Index2 = count($Result['Rounds']) - 1; $Index2 >= $Index; $Index2--) { array_pop($Result['Rounds']); } break; } else { unset($Result['Rounds'][$Index]['Initiative']); $ArrayItem = $Result['Rounds'][$Index]; if ($ArrayItem['Actor'] == 0) { if ($ArrayItem['Type'] == 0 || $ArrayItem['Type'] == 1) { $AnEnemy->Health = max($AnEnemy->Health - $ArrayItem['Damage'], 0); } else { if ($ArrayItem['Type'] == 2) { $this->Health = min($ArrayItem['Damage'] + $this->Health, $this->Vitality); } } //Mastery Gain Check if ($this->Masteries[$ArrayItem['MasteryType']]['Value'] < $this->RacialMasteries[$ArrayItem['MasteryType']]['Max']) { $MasteryCheck = 0 >= $AnEnemy->Health ? 20 + $MonsterMasteryChance : 20; if (mt_rand(1, max(20 + $this->Masteries[$ArrayItem['MasteryType']]['Value'] * 50, 50)) < $MasteryCheck) { $this->Masteries[$ArrayItem['MasteryType']]['Value']++; $Result['Masteries'][] = $ArrayItem['MasteryType']; } } } else { if ($ArrayItem['Type'] == 0 || $ArrayItem['Type'] == 1) { $this->Health = max($this->Health - $ArrayItem['Damage'], 0); //Armor Mastery Gain Check if ($ArrayItem['Damage'] > 0) { if ($this->Masteries[$ArrayItem['MasteryType']]['Value'] < $this->RacialMasteries[$ArrayItem['MasteryType']]['Max']) { $MasteryCheck = 20 + $ArrayItem['Damage'] / $this->Vitality * 20; if (mt_rand(1, max(20 + $this->Masteries[0]['Value'] * 50, 50)) < $MasteryCheck) { $this->Masteries[0]['Value']++; $Result['Masteries'][] = 0; } } } } else { if ($ArrayItem['Type'] == 2) { $AnEnemy->Health = min($ArrayItem['Damage'] + $AnEnemy->Health, $AnEnemy->Vitality); } } } } if ($AnEnemy->Health <= 0) { $PlayerWins = true; } else { if ($this->Health <= 0) { $EnemyWins = true; } } } $Index++; } if (property_exists($AnEnemy, 'Special')) { $Result['Special'] = $AnEnemy->Special; } if ($PlayerWins) { $Result['Winner'] = 0; if (get_class($AnEnemy) == 'Entities\\Monster') { $this->Experience += $AnEnemy->EXPGiven; $this->Gold += $AnEnemy->GoldGiven; $Result['Gold'] = $AnEnemy->GoldGiven; $Result['Experience'] = $AnEnemy->EXPGiven; if ($AnEnemy->AlignGood || $AnEnemy->AlignOrder) { if (mt_rand(0, 2500) < 5 + 2500 * $AnEnemy->AlignChanceBonus) { if ($AnEnemy->AlignGood > 0) { $this->AlignGood -= 1; $Result['AlignGood'] = $this->AlignGood; } else { if ($AnEnemy->AlignGood < 0) { $this->AlignGood += 1; $Result['AlignGood'] = $this->AlignGood; } } if ($AnEnemy->AlignOrder > 0) { $this->AlignOrder -= 1; $Result['AlignOrder'] = $this->AlignOrder; } else { if ($AnEnemy->AlignOrder < 0) { $this->AlignOrder += 1; $Result['AlignOrder'] = $this->AlignOrder; } } } } } else { $this->Gold += $AnEnemy->Gold; $Result['Gold'] = $AnEnemy->Gold; $AnEnemy->Gold = 0; if ($AnEnemy->AlignGood >= 0) { $this->AlignGood -= 1; $Result['AlignGood'] = $this->AlignGood; } else { if ($AnEnemy->AlignGood < 0) { $this->AlignGood += 1; $Result['AlignGood'] = $this->AlignGood; } } if ($AnEnemy->AlignOrder >= 0) { $this->AlignOrder -= 1; $Result['AlignOrder'] = $this->AlignOrder; } else { if ($AnEnemy->AlignOrder < 0) { $this->AlignOrder += 1; $Result['AlignOrder'] = $this->AlignOrder; } } } $Result['LevelUp'] = $this->LevelUp(); unset($_SESSION['CurrentFight']); } else { if ($EnemyWins) { $Result['Winner'] = 1; $LastReviveTime = 0; if (isset($_SESSION['LastReviveTime'])) { $LastReviveTime = $_SESSION['LastReviveTime']; } $RevivePenaltyMultiplier = 0; if ($LastReviveTime + 60 * 5 >= time()) { if (isset($_SESSION['RevivePenaltyMultiplier'])) { $_SESSION['RevivePenaltyMultiplier']++; } else { $_SESSION['RevivePenaltyMultiplier'] = 1; } } else { unset($_SESSION['RevivePenaltyMultiplier']); } $this->Gold = 0; unset($_SESSION['CurrentFight']); } } return $Result; }
function random_normal($sine = 0, $percentage = 1) { /* Chebyshev rules at least 1 - 1/k2 of the values are within k standard deviations from the mean ( ex. 7s 99.99999999974 % ). */ $max = 4; do { $value = gauss_ms(0, 1); } while (abs($value) > $max); if ($sine == 0) { $value = abs($value); } if ($percentage > 0) { return $value / $max; } else { return $value; } }
/** * Generate Stats * * function to generate stats for a mob based on level and bonuses. * */ public function GenerateStats() { $StatSeed = 7; $Stats = pow($this->Level + 2, log($this->Level + 3, 42.75)) * 10; $StatsHigh = $Stats * 1.1; $StdDev = $StatsHigh - $Stats; $this->StatBonus = 1; $StrengthBonus = 1; $DexterityBonus = 1; $HealthBonus = 1; $IntelligenceBonus = 1; $WisdomBonus = 1; $this->Special = 0; $this->AlignChanceBonus = 0; if (mt_rand(0, 100) > 95) { $this->Special = $this->RandomSpecial(); } $this->Strength = round(\gauss_ms($Stats, $StdDev) * $StrengthBonus * $this->StatBonus); $this->Dexterity = round(\gauss_ms($Stats, $StdDev) * $DexterityBonus * $this->StatBonus); $this->Intelligence = round(\gauss_ms($Stats, $StdDev) * $IntelligenceBonus * $this->StatBonus); $this->Wisdom = round(\gauss_ms($Stats, $StdDev) * $WisdomBonus * $this->StatBonus); $this->Vitality = round(\gauss_ms($Stats, $StdDev) * $HealthBonus * $this->StatBonus); $this->Health = $this->Vitality; $ExpSeed = 1.1; $GoldSeed = 1.01; $this->GoldGiven = (pow($this->Level + 8, log($this->Level + 8, 125)) + 7) * $this->GoldBonus; $EXPMultiplier = 5; $this->EXPGiven = round(pow($this->Level, log($this->Level + 1, 50)) * $EXPMultiplier * $this->ExperienceBonus); $GoldStdDev = $this->GoldGiven - $this->GoldGiven * 0.9; $ExpStdDev = $this->EXPGiven - $this->EXPGiven * 0.9; $this->GoldGiven = round(\gauss_ms($this->GoldGiven, $GoldStdDev)); $this->EXPGiven = round(\gauss_ms($this->EXPGiven, $ExpStdDev)); }
public static function gaussianWeightedRandom($LowValue, $maxRand, $mean = 0.0, $stddev = 2.0) { // Adjust a gaussian random value to fit within our specified range // by 'trimming' the extreme values as the distribution curve // approaches +/- infinity $rand_val = $LowValue + $maxRand; while ($rand_val < $LowValue || $rand_val >= $LowValue + $maxRand) { $rand_val = floor(gauss_ms($mean, $stddev) * $maxRand) + $LowValue; $rand_val = ($rand_val + $maxRand) / 2; } return $rand_val; }