Add attack/defense/damage modifiers and invuln.
Adds attack, defense and damage modifiers for both "goodguy" (self) and "badguy" (target) as well as a handler for goodguy/badguy invulnurability. Modified the battle calculation to not recalculate if noone does damage as long as at least 1 buff is active. This prevets infinite loops.
This commit is contained in:
+173
-18
@@ -12,11 +12,12 @@ use LotGD\Core\{
|
||||
Exceptions\BattleNotOverException,
|
||||
Models\FighterInterface
|
||||
};
|
||||
use LotGD\Core\Models\BattleEvents\{
|
||||
BuffMessageEvent,
|
||||
CriticalHitEvent,
|
||||
DamageEvent,
|
||||
DeathEvent
|
||||
use LotGD\Core\Models\{
|
||||
Buff,
|
||||
BattleEvents\BuffMessageEvent,
|
||||
BattleEvents\CriticalHitEvent,
|
||||
BattleEvents\DamageEvent,
|
||||
BattleEvents\DeathEvent
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -41,6 +42,15 @@ class Battle
|
||||
protected $result = 0;
|
||||
protected $round = 0;
|
||||
|
||||
/**
|
||||
* Battle Configuration
|
||||
* @var type
|
||||
*/
|
||||
protected $configuration = [
|
||||
"riposteEnabled" => true,
|
||||
"levelAdjustementEnabled" => true,
|
||||
];
|
||||
|
||||
public function __construct(Game $game, FighterInterface $player, FighterInterface $monster)
|
||||
{
|
||||
$this->game = $game;
|
||||
@@ -65,14 +75,72 @@ class Battle
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the battle is over.
|
||||
* @return type
|
||||
* Disables ripostes
|
||||
*/
|
||||
public function isOver()
|
||||
public function disableRiposte()
|
||||
{
|
||||
$this->configuration["riposteEnabled"] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables ripostes
|
||||
*/
|
||||
public function enableRiposte()
|
||||
{
|
||||
$this->configuration["riposteEnabled"] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if ripostes are enabled
|
||||
* @return bool
|
||||
*/
|
||||
public function isRiposteEnabled(): bool
|
||||
{
|
||||
return $this->configuration["riposteEnabled"];
|
||||
}
|
||||
|
||||
public function enableLevelAdjustement()
|
||||
{
|
||||
$this->configuration["levelAdjustementEnabled"] = true;
|
||||
}
|
||||
|
||||
public function disableLevelAdjustement()
|
||||
{
|
||||
$this->configuration["levelAdjustementEnabled"] = false;
|
||||
}
|
||||
|
||||
public function isLevelAdjustementEnabled(): bool
|
||||
{
|
||||
return $this->configuration["levelAdjustementEnabled"];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the battle is over.
|
||||
* @return bool
|
||||
*/
|
||||
public function isOver(): bool
|
||||
{
|
||||
return $this->result !== self::RESULT_UNDECIDED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the player instance
|
||||
* @return FighterInterface
|
||||
*/
|
||||
public function getPlayer(): FighterInterface
|
||||
{
|
||||
return $this->player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the montser instance
|
||||
* @return FighterInterface
|
||||
*/
|
||||
public function getMonster(): FighterInterface
|
||||
{
|
||||
return $this->monster;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the winner of this fight
|
||||
* @return FighterInterface
|
||||
@@ -87,7 +155,7 @@ class Battle
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the looser of this fight
|
||||
* Returns the loser of this fight
|
||||
* @return FighterInterface
|
||||
*/
|
||||
public function getLoser(): FighterInterface
|
||||
@@ -136,15 +204,23 @@ class Battle
|
||||
{
|
||||
$damageHasBeenDone = false;
|
||||
|
||||
$playerBuffStartEvents = $this->player->getBuffs()->activate();
|
||||
$monsterBuffStartEvents = $this->monster->getBuffs()->activate();
|
||||
$this->player->getBuffs()->resetBuffUsage();
|
||||
$this->monster->getBuffs()->resetBuffUsage();
|
||||
|
||||
$playerBuffStartEvents = $this->player->getBuffs()->activate(Buff::ACTIVATE_ROUNDSTART);
|
||||
$monsterBuffStartEvents = $this->monster->getBuffs()->activate(Buff::ACTIVATE_ROUNDSTART);
|
||||
|
||||
do {
|
||||
$offenseTurnEvents = $firstDamageRound & self::DAMAGEROUND_PLAYER ? $this->turn($this->player, $this->monster) : new ArrayCollection();
|
||||
$defenseTurnEvents = $firstDamageRound & self::DAMAGEROUND_MONSTER ? $this->turn($this->monster, $this->player) : new ArrayCollection();
|
||||
|
||||
|
||||
$events = new ArrayCollection(array_merge($offenseTurnEvents->toArray(), $defenseTurnEvents->toArray()));
|
||||
$eventsToAdd = new ArrayCollection();
|
||||
|
||||
if ($this->player->getBuffs()->hasBuffsInUse() || $this->monster->getBuffs()->hasBuffsInUse()) {
|
||||
// If there are active buffs, we still need to count the round even if there has not been any damage done.
|
||||
$damageHasBeenDone = true;
|
||||
}
|
||||
|
||||
foreach($events as $event) {
|
||||
$event->apply();
|
||||
@@ -199,9 +275,42 @@ class Battle
|
||||
$attackersBuffs = $attacker->getBuffs();
|
||||
$defendersBuffs = $defender->getBuffs();
|
||||
|
||||
$attackersAttack = $attacker->getAttack($this->game);
|
||||
$defendersDefense = $defender->getDefense($this->game);
|
||||
// Adjustement makes fights versus monsters with lower level easier,
|
||||
// and more difficult if the monster has a higher level by adjusting
|
||||
// the monster's defense value.
|
||||
// For example, if a level 10 player attacks a level 9 monster, the
|
||||
// defenseAdjustement value for the monster is 0.81, reducing the monster's
|
||||
// defense by 20% and making it more likely for the player to land a hit.
|
||||
// On the other hand, the player's defense is increased by ~ 10%, making it
|
||||
// less likely for the enemy to hit the player.
|
||||
$adjustement = 1.0;
|
||||
$defenseAdjustement = 1.0;
|
||||
if ($attacker === $this->player && $this->isLevelAdjustementEnabled()) {
|
||||
if ($attacker->getLevel() > 1 && $defender->getLevel() > 1) {
|
||||
$adjustement = $attacker->getLevel() / $defender->getLevel();
|
||||
$defenseAdjustement = 1. / ($adjustement * $adjustement);
|
||||
}
|
||||
}
|
||||
elseif ($defender === $this->player && $this->isLevelAdjustementEnabled()) {
|
||||
if ($attacker->getLevel() > 1 && $defender->getLevel() > 1) {
|
||||
$adjustement = $defender->getLevel() / $attacker->getLevel();
|
||||
$defenseAdjustement = $adjustement;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply buff scaling for the attacker's attack - this needs to take into
|
||||
// account the attacker's goodguyAttackModifier and the defenders badguyAttackModifier
|
||||
$attackersAttack = $attacker->getAttack($this->game)
|
||||
* $attackersBuffs->getGoodguyAttackModifier()
|
||||
* $defendersBuffs->getBadguyAttackModifier();
|
||||
// It's the opposite for the defender's defense - it needs to take into account the
|
||||
// defender's goodguyDefenseModifier as well as the attacker's badguyDefenseModifier.
|
||||
$defendersDefense = $defender->getDefense($this->game)
|
||||
* $defendersBuffs->getGoodguyDefenseModifier()
|
||||
* $attackersBuffs->getBadguyDefenseModifier()
|
||||
* $defenseAdjustement;
|
||||
|
||||
// If the player is the attacker, we enable critical hits with a chance of 25%.
|
||||
if ($attacker === $this->game->getCharacter()) {
|
||||
// Players can land critical hits
|
||||
if ($this->game->getDiceBag()->chance(0.25)) {
|
||||
@@ -209,21 +318,67 @@ class Battle
|
||||
}
|
||||
}
|
||||
|
||||
// Conversion from float to int, since the random number generator takes int values.
|
||||
$attackersAttack = (int) round($attackersAttack, 0);
|
||||
$defendersDefense = (int) round($defendersDefense, 0);
|
||||
|
||||
// Lets roll the
|
||||
$attackersAtkRoll = $this->game->getDiceBag()->normal(0, $attackersAttack);
|
||||
$defendersDefRoll = $this->game->getDiceBag()->normal(0, $defendersDefense);
|
||||
$damage = $attackersAtkRoll - $defendersDefRoll;
|
||||
|
||||
if ($attackersAttack > $attacker->getAttack($this->game, true)) {
|
||||
// If the attacker's attack after modification is bigger than before,
|
||||
// we call it a critical hit and apply the CriticalHitEvent.
|
||||
if ($attackersAttack > $attacker->getAttack($this->game)) {
|
||||
$events->add(new CriticalHitEvent($attacker, $attackersAttack));
|
||||
}
|
||||
|
||||
if ($damage < 0) {
|
||||
// RIPOSTE are only half as damaging than normal attacks
|
||||
$damage /= 2;
|
||||
// Set damage to 0 if riposte has been disabled
|
||||
if ($this->isRiposteEnabled() === false && $damage < 0) {
|
||||
$damage = 0;
|
||||
}
|
||||
|
||||
// Here, we take invulnurable buffs into account. There are 4 possible values coming from the
|
||||
// 2 buff lists, so we must take care a bit.
|
||||
$attackerIsInvulnurable = $attackersBuffs->goodguyIsInvulnurable() || $defendersBuffs->badguyIsInvulnurable();
|
||||
$defenderIsInvulnurable = $defendersBuffs->goodguyIsInvulnurable() || $attackersBuffs->badguyIsInvulnurable();
|
||||
|
||||
if ($attackerIsInvulnurable && $defenderIsInvulnurable) {
|
||||
// Both are invulnurable, damage is 0.
|
||||
$damage = 0;
|
||||
}
|
||||
elseif ($attackerIsInvulnurable) {
|
||||
// Attaker is invulnurable, damage is always > 0 (there is no riposte)
|
||||
$damage = abs($damage);
|
||||
}
|
||||
elseif ($defenderIsInvulnurable) {
|
||||
// Defender is invulnurable, damage is always < 0 (defender always ripostes)
|
||||
$damage = - abs($damage);
|
||||
}
|
||||
|
||||
if ($damage < 0) {
|
||||
// If the damage is less then 0, it's a RIPOSTE. They are only half
|
||||
// as damaging than normal attacks.
|
||||
$damage /= 2;
|
||||
|
||||
// Apply damage modification. It's a RIPOSTE, so the defenders makes the
|
||||
// damage. Therefore, we take defender's goodguyDamageModifier into account,
|
||||
// and the attacker's badguyDamageModifier.
|
||||
$damage *= $defendersBuffs->getGoodguyDamageModifier()
|
||||
* $attackersBuffs->getBadguyDamageModifier();
|
||||
}
|
||||
else {
|
||||
// Apply damage modification. It's a normal attack - meaning the attacker does
|
||||
// the damage. Therefore, we take the attacker's goodguyDamageModifier and
|
||||
// the defender's badguyDamageModifier into account.
|
||||
$damage *= $attackersBuffs->getGoodguyDamageModifier()
|
||||
* $defendersBuffs->getBadguyDamageModifier();
|
||||
}
|
||||
|
||||
// Round the damage value and convert to int.
|
||||
$damage = (int)round($damage, 0);
|
||||
|
||||
// Add the damage event
|
||||
$events->add(new DamageEvent($attacker, $defender, $damage));
|
||||
|
||||
return $events;
|
||||
|
||||
+271
-30
@@ -8,12 +8,16 @@ use Doctrine\Common\Collections\{
|
||||
Collection
|
||||
};
|
||||
|
||||
use LotGD\Core\Exceptions\{
|
||||
ArgumentException
|
||||
};
|
||||
use LotGD\Core\Models\{
|
||||
Buff,
|
||||
Character,
|
||||
BattleEvents\BuffMessageEvent
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Description of BuffList
|
||||
*/
|
||||
@@ -21,27 +25,46 @@ class BuffList
|
||||
{
|
||||
protected $buffs;
|
||||
protected $buffsBySlot;
|
||||
protected $activeBuffs;
|
||||
protected $activeBuffs = [];
|
||||
/** @var Doctrine\Common\Collections\ArrayCollection */
|
||||
protected $usedBuffs;
|
||||
|
||||
protected $activated = false;
|
||||
/** @var boolean True of the modifiers have already been calculated */
|
||||
protected $modifiersCalculated = false;
|
||||
/** @var boolean True if the badguy is invulnurable */
|
||||
protected $badguyInvulnurable = false;
|
||||
protected $badguyDamageModifier = 1;
|
||||
protected $badguyAttackModifier = 1;
|
||||
protected $badguyDefenseModifier = 1;
|
||||
/** @var float */
|
||||
protected $badguyDamageModifier = 1.;
|
||||
/** @var float */
|
||||
protected $badguyAttackModifier = 1.;
|
||||
/** @var float */
|
||||
protected $badguyDefenseModifier = 1.;
|
||||
/** @var boolean True if the goodguy is invulnurable */
|
||||
protected $goodguyInvulnurable = false;
|
||||
protected $goodguyDamageModifier = 1;
|
||||
protected $goodguyAttackModifier = 1;
|
||||
protected $goodguyDefenseModifier = 1;
|
||||
/** @var float */
|
||||
protected $goodguyDamageModifier = 1.;
|
||||
/** @var float */
|
||||
protected $goodguyAttackModifier = 1.;
|
||||
/** @var float */
|
||||
protected $goodguyDefenseModifier = 1.;
|
||||
|
||||
protected $events;
|
||||
protected $loaded = false;
|
||||
|
||||
/**
|
||||
* Initiates some variables
|
||||
* @param Collection $buffs
|
||||
*/
|
||||
public function __construct(Collection $buffs)
|
||||
{
|
||||
$this->buffs = $buffs;
|
||||
$this->events = new ArrayCollection();
|
||||
$this->usedBuffs = new ArrayCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all buffs (since it's a lazy correlation)
|
||||
*/
|
||||
public function loadBuffs()
|
||||
{
|
||||
if ($this->loaded === false) {
|
||||
@@ -51,50 +74,126 @@ class BuffList
|
||||
}
|
||||
}
|
||||
|
||||
public function activate(): Collection
|
||||
/**
|
||||
* Returns true if the given buff has already been used this round.
|
||||
* @param Buff $buff
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasBuffBeenUsed(Buff $buff): bool
|
||||
{
|
||||
if ($this->activated === true) {
|
||||
throw new BuffListAlreadyActivatedException("You can activate the buff list only once.");
|
||||
if ($this->usedBuffs->contains($buff)) {
|
||||
$used = true;
|
||||
}
|
||||
else {
|
||||
$used = false;
|
||||
}
|
||||
|
||||
$this->activeBuffs = new ArrayCollection();
|
||||
return $used;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the given buff as used
|
||||
* @param Buff $buff
|
||||
*/
|
||||
protected function useBuff(Buff $buff)
|
||||
{
|
||||
$this->usedBuffs->add($buff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the buff's start or round message
|
||||
* @param Buff $buff
|
||||
* @return string
|
||||
*/
|
||||
protected function getBuffMessage(Buff $buff): string
|
||||
{
|
||||
$return = "";
|
||||
$used = $this->hasBuffBeenUsed($buff);
|
||||
if ($buff->hasBeenStarted() === false && $used === false) {
|
||||
$return = $buff->getStartMessage();
|
||||
$buff->setHasBeenStarted();
|
||||
}
|
||||
elseif($used === false) {
|
||||
$return = $buff->getRoundMessage();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the buff usage for a new round
|
||||
*/
|
||||
public function resetBuffUsage()
|
||||
{
|
||||
$this->activeBuffs = [];
|
||||
$this->usedBuffs = new ArrayCollection();
|
||||
$this->modifiersCalculated = false;
|
||||
}
|
||||
|
||||
public function hasBuffsInUse(): bool
|
||||
{
|
||||
return count($this->usedBuffs) > 0 ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates all buffs that activate upon the given activation parameter.
|
||||
* @param int $activation
|
||||
* @return Collection
|
||||
* @throws ArgumentException
|
||||
* @throws BuffListAlreadyActivatedException
|
||||
*/
|
||||
public function activate(int $activation): Collection
|
||||
{
|
||||
if ($activation%2 !== 0 && $activation !== 1) {
|
||||
throw new ArgumentException("You can only activate one activation type at a time.");
|
||||
}
|
||||
|
||||
if (!empty($this->activeBuffs[$activation])) {
|
||||
throw new BuffListAlreadyActivatedException("You can activate the buff list for the given activation step only once.");
|
||||
}
|
||||
|
||||
$this->activeBuffs[$activation] = new ArrayCollection();
|
||||
$activationEvents = new ArrayCollection();
|
||||
|
||||
foreach ($this->buffs as $buff) {
|
||||
// Only look at buffs that are activated in battle.
|
||||
if ($buff->getsActivatedAt(Buff::ACTIVATE_NONE)) {
|
||||
foreach ($this->iterateBuffList() as $buff) {
|
||||
// Continue to next buff if the activation is not in this round.
|
||||
if ($buff->getsActivatedAt($activation) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->activeBuffs->add($buff);
|
||||
|
||||
if ($buff->hasBeenStarted() === false) {
|
||||
$activationMessage = $buff->getStartMessage();
|
||||
if ($activationMessage !== "") {
|
||||
$activationEvents->add(new BuffMessageEvent($activationMessage));
|
||||
}
|
||||
$buff->setHasBeenStarted();
|
||||
$this->activeBuffs[$activation]->add($buff);
|
||||
|
||||
// Returns start or roundMessage if the buff has not been used yet.
|
||||
$buffMessage = $this->getBuffMessage($buff);
|
||||
if ($buffMessage !== "") {
|
||||
$activationEvents->add(new BuffMessageEvent($buffMessage));
|
||||
}
|
||||
else {
|
||||
$roundMessage = $buff->getRoundMessage();
|
||||
if ($roundMessage !== "") {
|
||||
$activationEvents->add(new BuffMessageEvent($roundMessage));
|
||||
}
|
||||
|
||||
// Needs to come at the end
|
||||
if ($this->hasBuffBeenUsed($buff) === false) {
|
||||
$this->useBuff($buff);
|
||||
}
|
||||
}
|
||||
|
||||
return $activationEvents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decreases the rounds left on all used buffs
|
||||
* @return Collection A Collection containing expire messages (if there are any)
|
||||
*/
|
||||
public function expireOneRound(): Collection
|
||||
{
|
||||
/* @var $endEvents Collection */
|
||||
$endEvents = new ArrayCollection();
|
||||
|
||||
foreach($this->activeBuffs as $buff) {
|
||||
foreach($this->usedBuffs as $buff) {
|
||||
/* @var $roundsLeft int */
|
||||
$roundsLeft = $buff->getRounds() - 1;
|
||||
$buff->setRounds($roundsLeft);
|
||||
|
||||
if ($roundsLeft === 0) {
|
||||
/* @var $endMessage string */
|
||||
$endMessage = $buff->getEndMessage();
|
||||
|
||||
if ($endMessage !== "") {
|
||||
@@ -108,13 +207,22 @@ class BuffList
|
||||
return $endEvents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a buff from the buff list.
|
||||
* @param Buff $buff
|
||||
*/
|
||||
public function remove(Buff $buff)
|
||||
{
|
||||
unset($this->buffsBySlot[$buff->getSlot()]);
|
||||
$this->buffs->removeElement($buff);
|
||||
$this->activeBuffs->removeElement($buff);
|
||||
$this->usedBuffs->removeElement($buff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a buff to the buff list, occupying the slot.
|
||||
* @param Buff $buff
|
||||
* @throws BuffSlotOccupiedException if the slot is already occupied. Use renew instead.
|
||||
*/
|
||||
public function add(Buff $buff)
|
||||
{
|
||||
$this->loadBuffs();
|
||||
@@ -128,6 +236,10 @@ class BuffList
|
||||
$this->buffsBySlot[$buff->getSlot()] = $buff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renews a buff.
|
||||
* @param Buff $buff
|
||||
*/
|
||||
public function renew(Buff $buff)
|
||||
{
|
||||
$this->loadBuffs();
|
||||
@@ -140,4 +252,133 @@ class BuffList
|
||||
$this->buffs->add($buff);
|
||||
$this->buffsBySlot[$buff->getSlot()] = $buff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates all total modifiers
|
||||
* @return type
|
||||
*/
|
||||
protected function calculateModifiers()
|
||||
{
|
||||
if ($this->modifiersCalculated === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->badguyAttackModifier = 1.;
|
||||
$this->badguyDamageModifier = 1.;
|
||||
$this->badguyDefenseModifier = 1.;
|
||||
$this->badguyInvulnurable = false;
|
||||
$this->goodguyAttackModifier = 1.;
|
||||
$this->goodguyDamageModifier = 1.;
|
||||
$this->goodguyDefenseModifier = 1.;
|
||||
$this->goodguyInvulnurable = false;
|
||||
|
||||
/* @var $buff \LotGD\Core\Model\Buff */
|
||||
foreach ($this->iterateBuffList() as $buff) {
|
||||
$this->badguyAttackModifier *= $buff->getBadguyAttackModifier();
|
||||
$this->badguyDefenseModifier *= $buff->getBadguyDefenseModifier();
|
||||
$this->badguyDamageModifier *= $buff->getBadguyDamageModifier();
|
||||
$this->badguyInvulnurable = $this->badguyInvulnurable || $buff->badguyIsInvulnurable();
|
||||
$this->goodguyAttackModifier *= $buff->getGoodguyAttackModifier();
|
||||
$this->goodguyDefenseModifier *= $buff->getGoodguyDefenseModifier();
|
||||
$this->goodguyDamageModifier *= $buff->getGoodguyDamageModifier();
|
||||
$this->goodguyInvulnurable = $this->goodguyInvulnurable || $buff->goodguyIsInvulnurable();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over every buff that gets activated at one point during a round.
|
||||
* @return Generator|\LotGD\Core\Model\Buff[]
|
||||
*/
|
||||
protected function iterateBuffList()
|
||||
{
|
||||
foreach ($this->buffs as $buff) {
|
||||
// Only look at buffs that are activated in battle.
|
||||
if ($buff->getsActivatedAt(Buff::ACTIVATE_NONE)) {
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
yield $buff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the badguy attack modifier calculated over the whole bufflist
|
||||
* @return float
|
||||
*/
|
||||
public function getBadguyAttackModifier(): float
|
||||
{
|
||||
$this->calculateModifiers();
|
||||
return $this->badguyAttackModifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the badguy defense modifier calculated over the whole bufflist
|
||||
* @return float
|
||||
*/
|
||||
public function getBadguyDefenseModifier(): float
|
||||
{
|
||||
$this->calculateModifiers();
|
||||
return $this->badguyDefenseModifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the badguy damage modifier calculated over the whole bufflist
|
||||
* @return float
|
||||
*/
|
||||
public function getBadguyDamageModifier(): float
|
||||
{
|
||||
$this->calculateModifiers();
|
||||
return $this->badguyDamageModifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the badguy is invulnurable
|
||||
* @return bool
|
||||
*/
|
||||
public function badguyIsInvulnurable(): bool
|
||||
{
|
||||
$this->calculateModifiers();
|
||||
return $this->badguyInvulnurable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the badguy attack modifier calculated over the whole bufflist
|
||||
* @return float
|
||||
*/
|
||||
public function getGoodguyAttackModifier(): float
|
||||
{
|
||||
$this->calculateModifiers();
|
||||
return $this->goodguyAttackModifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the badguy defense modifier calculated over the whole bufflist
|
||||
* @return float
|
||||
*/
|
||||
public function getGoodguyDefenseModifier(): float
|
||||
{
|
||||
$this->calculateModifiers();
|
||||
return $this->goodguyDefenseModifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the badguy damage modifier calculated over the whole bufflist
|
||||
* @return float
|
||||
*/
|
||||
public function getGoodguyDamageModifier(): float
|
||||
{
|
||||
$this->calculateModifiers();
|
||||
return $this->goodguyDamageModifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the goodguy is invulnurable
|
||||
* @return bool
|
||||
*/
|
||||
public function goodguyIsInvulnurable(): bool
|
||||
{
|
||||
$this->calculateModifiers();
|
||||
return $this->goodguyInvulnurable;
|
||||
}
|
||||
}
|
||||
|
||||
+21
-11
@@ -43,49 +43,49 @@ class Buff
|
||||
* @var string
|
||||
* @Column(type="text")
|
||||
*/
|
||||
private $startMessage;
|
||||
private $startMessage = "";
|
||||
/**
|
||||
* The message given every round
|
||||
* @var string
|
||||
* @Column(type="text")
|
||||
*/
|
||||
private $roundMessage;
|
||||
private $roundMessage = "";
|
||||
/**
|
||||
* The message given if the buff ends
|
||||
* @var string
|
||||
* @Column(type="text")
|
||||
*/
|
||||
private $endMessage;
|
||||
private $endMessage = "";
|
||||
/**
|
||||
* The message given if the effect has success
|
||||
* @var string
|
||||
* @Column(type="text")
|
||||
*/
|
||||
private $effectSucceedsMessage;
|
||||
private $effectSucceedsMessage = "";
|
||||
/**
|
||||
* The message given if the effect fails
|
||||
* @var string
|
||||
* @Column(type="text")
|
||||
*/
|
||||
private $effectFailsMessage;
|
||||
private $effectFailsMessage = "";
|
||||
/**
|
||||
* The message given if the effect has no effect
|
||||
* @var string
|
||||
* @Column(type="text")
|
||||
*/
|
||||
private $noEffectMessage;
|
||||
private $noEffectMessage = "";
|
||||
/**
|
||||
* Message that gets displayed every new day.
|
||||
* @var string
|
||||
* @Column(type="text")
|
||||
*/
|
||||
private $newDayMessage;
|
||||
private $newDayMessage = "";
|
||||
/**
|
||||
* A value determining when the buffs activates
|
||||
* @var int
|
||||
* @Column(type="integer")
|
||||
*/
|
||||
private $activateAt;
|
||||
private $activateAt = self::ACTIVATE_NONE;
|
||||
/**
|
||||
* True if the buff survives a new day
|
||||
* @var bool
|
||||
@@ -293,7 +293,12 @@ class Buff
|
||||
|
||||
case "float":
|
||||
if (is_float($value) === false) {
|
||||
throw new ArgumentException("{$attribute} needs to be a float.");
|
||||
// Convert to float if it is an integer.
|
||||
if (is_int($value) === false) {
|
||||
throw new ArgumentException("{$attribute} needs to be a float.");
|
||||
}
|
||||
|
||||
$value = (float)$value;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -444,7 +449,12 @@ class Buff
|
||||
*/
|
||||
public function getsActivatedAt(int $flag): bool
|
||||
{
|
||||
return ($flag === self::ACTIVATE_NONE ? $this->activateAt === self::ACTIVATE_NONE : $this->activateAt & $flag);
|
||||
if ($flag === self::ACTIVATE_NONE) {
|
||||
return $this->activateAt == self::ACTIVATE_NONE ? true : false;
|
||||
}
|
||||
else {
|
||||
return ($this->activateAt & $flag > 0) === 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -662,7 +672,7 @@ class Buff
|
||||
* Returns true if the goodguy is invulnurable
|
||||
* @return bool
|
||||
*/
|
||||
public function getGoodguyIsInvulnurable(): bool
|
||||
public function goodguyIsInvulnurable(): bool
|
||||
{
|
||||
return $this->goodguyInvulnurable;
|
||||
}
|
||||
|
||||
+329
-8
@@ -121,9 +121,9 @@ class BattleTest extends ModelTestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a fight which the player has to loose (lvl 1 vs lvl 100)
|
||||
* Tests a fight which the player has to lose (lvl 1 vs lvl 100)
|
||||
*/
|
||||
public function testPlayerLooseBattle()
|
||||
public function testPlayerLoseBattle()
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
|
||||
@@ -171,7 +171,7 @@ class BattleTest extends ModelTestCase
|
||||
/**
|
||||
* @expectedException LotGD\Core\Exceptions\BattleNotOverException
|
||||
*/
|
||||
public function testBattleNotOverExceptionFromLooser()
|
||||
public function testBattleNotOverExceptionFromLoser()
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
|
||||
@@ -196,25 +196,54 @@ class BattleTest extends ModelTestCase
|
||||
|
||||
$battle = new Battle($this->getMockGame($character), $character, $monster);
|
||||
|
||||
// Fighting for 99 rounds should be enough for determining a looser - and to
|
||||
// Fighting for 99 rounds should be enough for determining a loser - and to
|
||||
// throw the exception.
|
||||
for ($n = 0; $n < 99; $n++) {
|
||||
$battle->fightNRounds(1);
|
||||
}
|
||||
}
|
||||
|
||||
private function provideBuffBattleParticipants(Buff $buff): Battle
|
||||
private function provideBuffBattleParticipants(Buff $buff, int $participantsType): Battle
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
$em->clear();
|
||||
|
||||
$character = $em->getRepository(Character::class)->find(4);
|
||||
$monster = $em->getRepository(Monster::class)->find(3);
|
||||
switch ($participantsType) {
|
||||
default:
|
||||
case 0:
|
||||
// Fair Battle
|
||||
$character = $em->getRepository(Character::class)->find(1);
|
||||
$monster = $em->getRepository(Monster::class)->find(1);
|
||||
break;
|
||||
case 1:
|
||||
// very long battle
|
||||
$character = $em->getRepository(Character::class)->find(4);
|
||||
$monster = $em->getRepository(Monster::class)->find(3);
|
||||
break;
|
||||
case 2:
|
||||
// player should win battle
|
||||
$character = $em->getRepository(Character::class)->find(13);
|
||||
$monster = $em->getRepository(Monster::class)->find(11);
|
||||
break;
|
||||
case 3:
|
||||
// player should lose battle
|
||||
$character = $em->getRepository(Character::class)->find(11);
|
||||
$monster = $em->getRepository(Monster::class)->find(13);
|
||||
break;
|
||||
}
|
||||
|
||||
$character->addBuff($buff);
|
||||
|
||||
return new Battle($this->getMockGame($character), $character, $monster);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a certain BuffMessageEvent with a specific text is contained in the lst of events
|
||||
* @param Collection $events The list of events
|
||||
* @param string $battleEventText The text to test for
|
||||
* @param int $timesAtLeast Mininum number of times the message is expected to be in the event list
|
||||
* @param int? $timesAtMax Maximum number of times the message is expected to be in the event list, or $timesAtLeast if null.
|
||||
*/
|
||||
protected function assertBuffEventMessageExists(
|
||||
Collection $events,
|
||||
string $battleEventText,
|
||||
@@ -238,6 +267,10 @@ class BattleTest extends ModelTestCase
|
||||
$this->assertLessThanOrEqual($timesAtMax, $eventCounter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests normal buff messages - message upon start of the buff, message every
|
||||
* round (except when it's started), and the message displayed if the buff expires.
|
||||
*/
|
||||
public function testBattleBuffMessages()
|
||||
{
|
||||
$battle = $this->provideBuffBattleParticipants(new Buff([
|
||||
@@ -247,8 +280,9 @@ class BattleTest extends ModelTestCase
|
||||
"roundMessage" => "The buff is still activate",
|
||||
"endMessage" => "The buff is ending.",
|
||||
"activateAt" => Buff::ACTIVATE_ROUNDSTART,
|
||||
]));
|
||||
]), 1);
|
||||
|
||||
// We fight for 5 rounds - this ensures that the buff is started and expired.
|
||||
$battle->fightNRounds(5);
|
||||
|
||||
$this->assertBuffEventMessageExists($battle->getEvents(), "And this buff starts!", 1);
|
||||
@@ -278,4 +312,291 @@ class BattleTest extends ModelTestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function testBattleBuffPlayerGoodguyModifier()
|
||||
{
|
||||
// Get a battle ready
|
||||
$battle = $this->provideBuffBattleParticipants(new Buff([
|
||||
"slot" => "test",
|
||||
"rounds" => 99,
|
||||
"goodguyAttackModifier" => 0.0,
|
||||
"goodguyDefenseModifier" => 0.0,
|
||||
"activateAt" => Buff::ACTIVATE_ROUNDSTART,
|
||||
]), 2);
|
||||
|
||||
$rounds = $battle->fightNRounds(99);
|
||||
|
||||
$this->assertTrue($battle->isOver());
|
||||
$this->assertSame($battle->getPlayer(), $battle->getLoser());
|
||||
|
||||
// Get a battle that the player should lose and apply a buff that the player forces to win
|
||||
$battle = $this->provideBuffBattleParticipants(new Buff([
|
||||
"slot" => "test",
|
||||
"rounds" => 99,
|
||||
"goodguyAttackModifier" => 2,
|
||||
"goodguyDefenseModifier" => 2,
|
||||
"activateAt" => Buff::ACTIVATE_ROUNDSTART,
|
||||
]), 3);
|
||||
|
||||
$battle->fightNRounds(99);
|
||||
|
||||
$this->assertTrue($battle->isOver());
|
||||
$this->assertSame($battle->getPlayer(), $battle->getWinner());
|
||||
}
|
||||
|
||||
public function testBattleBuffPlayerBadguyModifier()
|
||||
{
|
||||
// Get a battle that the player should win and apply a buff that the player forces to lose.
|
||||
$battle = $this->provideBuffBattleParticipants(new Buff([
|
||||
"slot" => "test",
|
||||
"rounds" => 99,
|
||||
"badguyAttackModifier" => 10,
|
||||
"badguyDefenseModifier" => 10,
|
||||
"activateAt" => Buff::ACTIVATE_ROUNDSTART,
|
||||
]), 2);
|
||||
|
||||
$rounds = $battle->fightNRounds(99);
|
||||
|
||||
$this->assertTrue($battle->isOver());
|
||||
$this->assertSame($battle->getPlayer(), $battle->getLoser());
|
||||
|
||||
// Get a battle that the player should lose and apply a buff that the player forces to win
|
||||
$battle = $this->provideBuffBattleParticipants(new Buff([
|
||||
"slot" => "test",
|
||||
"rounds" => 99,
|
||||
"badguyAttackModifier" => 0,
|
||||
"badguyDefenseModifier" => 0,
|
||||
"activateAt" => Buff::ACTIVATE_ROUNDSTART,
|
||||
]), 3);
|
||||
|
||||
$battle->fightNRounds(99);
|
||||
|
||||
$this->assertTrue($battle->isOver());
|
||||
$this->assertSame($battle->getPlayer(), $battle->getWinner());
|
||||
}
|
||||
|
||||
public function testBattleBuffPlayerDamageModifier()
|
||||
{
|
||||
// Get a battle that the player should win and apply a buff that the player forces to lose
|
||||
$battle = $this->provideBuffBattleParticipants(new Buff([
|
||||
"slot" => "test",
|
||||
"rounds" => 99,
|
||||
"goodguyDamageModifier" => 0.0,
|
||||
"activateAt" => Buff::ACTIVATE_ROUNDSTART,
|
||||
]), 0);
|
||||
|
||||
$rounds = $battle->fightNRounds(10);
|
||||
|
||||
$this->assertSame($battle->getMonster()->getMaxHealth(), $battle->getMonster()->getHealth());
|
||||
|
||||
// Get a battle that the player should lose and apply a buff that the player forces to win
|
||||
$battle = $this->provideBuffBattleParticipants(new Buff([
|
||||
"slot" => "test",
|
||||
"rounds" => 99,
|
||||
"badguyDamageModifier" => 0.0,
|
||||
"activateAt" => Buff::ACTIVATE_ROUNDSTART,
|
||||
]), 0);
|
||||
|
||||
$battle->fightNRounds(10);
|
||||
|
||||
$this->assertSame($battle->getPlayer()->getMaxHealth(), $battle->getPlayer()->getHealth());
|
||||
}
|
||||
|
||||
public function testBattleBuffPlayerInvulnurability()
|
||||
{
|
||||
// Get a battle that the player should win and apply a buff that the player forces to lose
|
||||
$battle = $this->provideBuffBattleParticipants(new Buff([
|
||||
"slot" => "test",
|
||||
"rounds" => 99,
|
||||
"badguyInvulnurable" => true,
|
||||
"activateAt" => Buff::ACTIVATE_ROUNDSTART,
|
||||
]), 0);
|
||||
|
||||
$rounds = $battle->fightNRounds(99);
|
||||
|
||||
$this->assertSame($battle->getMonster()->getMaxHealth(), $battle->getMonster()->getHealth());
|
||||
$this->assertTrue($battle->isOver());
|
||||
$this->assertSame($battle->getMonster(), $battle->getWinner());
|
||||
|
||||
// Get a battle that the player should lose and apply a buff that the player forces to win
|
||||
$battle = $this->provideBuffBattleParticipants(new Buff([
|
||||
"slot" => "test",
|
||||
"rounds" => 99,
|
||||
"goodguyInvulnurable" => true,
|
||||
"activateAt" => Buff::ACTIVATE_ROUNDSTART,
|
||||
]), 0);
|
||||
|
||||
$rounds = $battle->fightNRounds(99);
|
||||
|
||||
$this->assertSame($battle->getPlayer()->getMaxHealth(), $battle->getPlayer()->getHealth());
|
||||
$this->assertTrue($battle->isOver());
|
||||
$this->assertSame($battle->getPlayer(), $battle->getWinner());
|
||||
}
|
||||
|
||||
public function testBufflistGoodguyAttackModifier()
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
$player = $em->getRepository(Character::class)->find(1);
|
||||
$game = $this->getMockGame($player);
|
||||
|
||||
$player->addBuff(new Buff([
|
||||
"slot" => "test1",
|
||||
"rounds" => 1,
|
||||
"goodguyAttackModifier" => 1.23,
|
||||
"activateAt" => Buff::ACTIVATE_ROUNDSTART,
|
||||
]));
|
||||
$player->addBuff(new Buff([
|
||||
"slot" => "test2",
|
||||
"rounds" => 1,
|
||||
"goodguyAttackModifier" => 0.126,
|
||||
"activateAt" => Buff::ACTIVATE_ROUNDSTART,
|
||||
]));
|
||||
$player->addBuff(new Buff([
|
||||
"slot" => "test3",
|
||||
"rounds" => 1,
|
||||
"goodguyAttackModifier" => 13.4,
|
||||
]));
|
||||
|
||||
$modifier = $player->getBuffs()->getGoodguyAttackModifier();
|
||||
$this->assertEquals(0.15498, $modifier, '', 0.001);
|
||||
}
|
||||
|
||||
public function testBufflistGoodguyDefenseModifier()
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
$player = $em->getRepository(Character::class)->find(1);
|
||||
$game = $this->getMockGame($player);
|
||||
|
||||
$player->addBuff(new Buff([
|
||||
"slot" => "test1",
|
||||
"rounds" => 1,
|
||||
"goodguyDefenseModifier" => 1.293,
|
||||
"activateAt" => Buff::ACTIVATE_ROUNDSTART,
|
||||
]));
|
||||
$player->addBuff(new Buff([
|
||||
"slot" => "test2",
|
||||
"rounds" => 1,
|
||||
"goodguyDefenseModifier" => 5.6,
|
||||
"activateAt" => Buff::ACTIVATE_ROUNDSTART,
|
||||
]));
|
||||
$player->addBuff(new Buff([
|
||||
"slot" => "test3",
|
||||
"rounds" => 1,
|
||||
"goodguyDefenseModifier" => 0,
|
||||
]));
|
||||
|
||||
$modifier = $player->getBuffs()->getGoodguyDefenseModifier();
|
||||
$this->assertEquals(7.2408, $modifier, '', 0.001);
|
||||
}
|
||||
|
||||
public function testBufflistGoodguyDamageModifier()
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
$player = $em->getRepository(Character::class)->find(1);
|
||||
$game = $this->getMockGame($player);
|
||||
|
||||
$player->addBuff(new Buff([
|
||||
"slot" => "test1",
|
||||
"rounds" => 1,
|
||||
"goodguyDamageModifier" => 10,
|
||||
"activateAt" => Buff::ACTIVATE_ROUNDSTART,
|
||||
]));
|
||||
$player->addBuff(new Buff([
|
||||
"slot" => "test2",
|
||||
"rounds" => 1,
|
||||
"goodguyDamageModifier" => 0.25,
|
||||
"activateAt" => Buff::ACTIVATE_ROUNDSTART,
|
||||
]));
|
||||
$player->addBuff(new Buff([
|
||||
"slot" => "test3",
|
||||
"rounds" => 1,
|
||||
"goodguyDamageModifier" => 3.5,
|
||||
]));
|
||||
|
||||
$modifier = $player->getBuffs()->getGoodguyDamageModifier();
|
||||
$this->assertEquals(2.5, $modifier, '', 0.001);
|
||||
}
|
||||
|
||||
public function testBufflistBadguyAttackModifier()
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
$player = $em->getRepository(Character::class)->find(1);
|
||||
$game = $this->getMockGame($player);
|
||||
|
||||
$player->addBuff(new Buff([
|
||||
"slot" => "test1",
|
||||
"rounds" => 1,
|
||||
"badguyAttackModifier" => 1.23,
|
||||
"activateAt" => Buff::ACTIVATE_ROUNDSTART,
|
||||
]));
|
||||
$player->addBuff(new Buff([
|
||||
"slot" => "test2",
|
||||
"rounds" => 1,
|
||||
"badguyAttackModifier" => 0.126,
|
||||
"activateAt" => Buff::ACTIVATE_ROUNDSTART,
|
||||
]));
|
||||
$player->addBuff(new Buff([
|
||||
"slot" => "test3",
|
||||
"rounds" => 1,
|
||||
"badguyAttackModifier" => 13.4,
|
||||
]));
|
||||
|
||||
$modifier = $player->getBuffs()->getBadguyAttackModifier();
|
||||
$this->assertEquals(0.15498, $modifier, '', 0.001);
|
||||
}
|
||||
|
||||
public function testBufflistBadguyDefenseModifier()
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
$player = $em->getRepository(Character::class)->find(1);
|
||||
$game = $this->getMockGame($player);
|
||||
|
||||
$player->addBuff(new Buff([
|
||||
"slot" => "test1",
|
||||
"rounds" => 1,
|
||||
"badguyDefenseModifier" => 1.293,
|
||||
"activateAt" => Buff::ACTIVATE_ROUNDSTART,
|
||||
]));
|
||||
$player->addBuff(new Buff([
|
||||
"slot" => "test2",
|
||||
"rounds" => 1,
|
||||
"badguyDefenseModifier" => 5.6,
|
||||
"activateAt" => Buff::ACTIVATE_ROUNDSTART,
|
||||
]));
|
||||
$player->addBuff(new Buff([
|
||||
"slot" => "test3",
|
||||
"rounds" => 1,
|
||||
"badguyDefenseModifier" => 0,
|
||||
]));
|
||||
|
||||
$modifier = $player->getBuffs()->getBadguyDefenseModifier();
|
||||
$this->assertEquals(7.2408, $modifier, '', 0.001);
|
||||
}
|
||||
|
||||
public function testBufflistBadguyDamageModifier()
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
$player = $em->getRepository(Character::class)->find(1);
|
||||
$game = $this->getMockGame($player);
|
||||
|
||||
$player->addBuff(new Buff([
|
||||
"slot" => "test1",
|
||||
"rounds" => 1,
|
||||
"badguyDamageModifier" => 10,
|
||||
"activateAt" => Buff::ACTIVATE_ROUNDSTART,
|
||||
]));
|
||||
$player->addBuff(new Buff([
|
||||
"slot" => "test2",
|
||||
"rounds" => 1,
|
||||
"badguyDamageModifier" => 0.25,
|
||||
"activateAt" => Buff::ACTIVATE_ROUNDSTART,
|
||||
]));
|
||||
$player->addBuff(new Buff([
|
||||
"slot" => "test3",
|
||||
"rounds" => 1,
|
||||
"badguyDamageModifier" => 3.5,
|
||||
]));
|
||||
|
||||
$modifier = $player->getBuffs()->getBadguyDamageModifier();
|
||||
$this->assertEquals(2.5, $modifier, '', 0.001);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,27 @@ characters:
|
||||
health: 500
|
||||
maxhealth: 500
|
||||
level: 0
|
||||
-
|
||||
id: 10
|
||||
name: "Level 10 Character"
|
||||
displayName: "Level 10 Character"
|
||||
health: 100
|
||||
maxhealth: 100
|
||||
level: 10
|
||||
-
|
||||
id: 11
|
||||
name: "Level 11 Character"
|
||||
displayName: "Level 11 Character"
|
||||
health: 110
|
||||
maxhealth: 110
|
||||
level: 11
|
||||
-
|
||||
id: 13
|
||||
name: "Level 13 Character"
|
||||
displayName: "Level 13 Character"
|
||||
health: 130
|
||||
maxhealth: 130
|
||||
level: 13
|
||||
monsters:
|
||||
-
|
||||
id: 1
|
||||
@@ -39,4 +60,16 @@ monsters:
|
||||
-
|
||||
id: 3
|
||||
name: "Stone"
|
||||
level: 1
|
||||
level: 1
|
||||
-
|
||||
id: 10
|
||||
name: "Level 10 Monster"
|
||||
level: 10
|
||||
-
|
||||
id: 11
|
||||
name: "Level 11 Monster"
|
||||
level: 11
|
||||
-
|
||||
id: 13
|
||||
name: "Level 13 Monster"
|
||||
level: 13
|
||||
Reference in New Issue
Block a user