refactor: Refactor combat, fix TSL crashes
This commit is contained in:
parent
cd0772bb06
commit
127022a0d9
10 changed files with 228 additions and 118 deletions
|
@ -371,7 +371,10 @@ set(GAME_HEADERS
|
|||
src/game/characterutil.h
|
||||
src/game/collisiondetect.h
|
||||
src/game/console.h
|
||||
src/game/combat.h
|
||||
src/game/combat/attackresolver.h
|
||||
src/game/combat/attackresult.h
|
||||
src/game/combat/combat.h
|
||||
src/game/combat/damageresolver.h
|
||||
src/game/cursors.h
|
||||
src/game/dialog.h
|
||||
src/game/enginetype/effect.h
|
||||
|
@ -440,7 +443,6 @@ set(GAME_HEADERS
|
|||
src/game/rp/attributes.h
|
||||
src/game/rp/class.h
|
||||
src/game/rp/classes.h
|
||||
src/game/rp/damageresolver.h
|
||||
src/game/rp/factionutil.h
|
||||
src/game/rp/savingthrows.h
|
||||
src/game/rp/skills.h
|
||||
|
@ -477,7 +479,9 @@ set(GAME_SOURCES
|
|||
src/game/characterutil.cpp
|
||||
src/game/collisiondetect.cpp
|
||||
src/game/console.cpp
|
||||
src/game/combat.cpp
|
||||
src/game/combat/attackresolver.cpp
|
||||
src/game/combat/combat.cpp
|
||||
src/game/combat/damageresolver.cpp
|
||||
src/game/cursors.cpp
|
||||
src/game/dialog.cpp
|
||||
src/game/game.cpp
|
||||
|
@ -539,7 +543,6 @@ set(GAME_SOURCES
|
|||
src/game/rp/attributes.cpp
|
||||
src/game/rp/class.cpp
|
||||
src/game/rp/classes.cpp
|
||||
src/game/rp/damageresolver.cpp
|
||||
src/game/rp/factionutil.cpp
|
||||
src/game/rp/skills.cpp
|
||||
src/game/savedgame.cpp
|
||||
|
|
123
src/game/combat/attackresolver.cpp
Normal file
123
src/game/combat/attackresolver.cpp
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 The reone project contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "attackresolver.h"
|
||||
|
||||
#include "../../common/random.h"
|
||||
|
||||
#include "../object/creature.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace game {
|
||||
|
||||
static bool isMeleeWieldType(CreatureWieldType type) {
|
||||
switch (type) {
|
||||
case CreatureWieldType::SingleSword:
|
||||
case CreatureWieldType::DoubleBladedSword:
|
||||
case CreatureWieldType::DualSwords:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool isAttackSuccessful(AttackResultType type) {
|
||||
switch (type) {
|
||||
case AttackResultType::HitSuccessful:
|
||||
case AttackResultType::CriticalHit:
|
||||
case AttackResultType::AutomaticHit:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool isRangedWieldType(CreatureWieldType type) {
|
||||
switch (type) {
|
||||
case CreatureWieldType::BlasterPistol:
|
||||
case CreatureWieldType::DualPistols:
|
||||
case CreatureWieldType::BlasterRifle:
|
||||
case CreatureWieldType::HeavyWeapon:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
AttackResult AttackResolver::getAttackResult(const shared_ptr<Creature> &attacker, const shared_ptr<SpatialObject> &target, bool duel, AttackResultType fixedType) {
|
||||
AttackResult result;
|
||||
|
||||
if (fixedType != AttackResultType::Invalid) {
|
||||
result.type = fixedType;
|
||||
} else {
|
||||
int attack = random(1, 20);
|
||||
int defense = 10; // TODO: add armor bonus and dexterity modifier
|
||||
|
||||
if (attack == 20) {
|
||||
result.type = AttackResultType::AutomaticHit;
|
||||
} else if (attack >= defense) {
|
||||
result.type = AttackResultType::HitSuccessful;
|
||||
} else {
|
||||
result.type = AttackResultType::Miss;
|
||||
}
|
||||
}
|
||||
|
||||
CreatureWieldType attackerWield = attacker->getWieldType();
|
||||
CreatureWieldType targetWield = CreatureWieldType::None;
|
||||
|
||||
auto targetCreature = dynamic_pointer_cast<Creature>(target);
|
||||
if (targetCreature) {
|
||||
targetWield = targetCreature->getWieldType();
|
||||
}
|
||||
|
||||
if (duel) {
|
||||
if (isMeleeWieldType(attackerWield) && isMeleeWieldType(targetWield)) {
|
||||
result.attackerAnimation = CombatAnimation::MeleeDuelAttack;
|
||||
result.animationVariant = random(1, 5);
|
||||
result.targetAnimation = isAttackSuccessful(result.type) ? CombatAnimation::MeleeDuelDamage : CombatAnimation::MeleeDuelParry;
|
||||
} else if (isMeleeWieldType(attackerWield)) {
|
||||
result.attackerAnimation = CombatAnimation::MeleeAttack;
|
||||
result.animationVariant = random(1, 2);
|
||||
result.targetAnimation = isAttackSuccessful(result.type) ? CombatAnimation::MeleeDamage : CombatAnimation::MeleeDodge;
|
||||
} else if (isRangedWieldType(attackerWield)) {
|
||||
result.attackerAnimation = CombatAnimation::RangedAttack;
|
||||
result.targetAnimation = isAttackSuccessful(result.type) ? CombatAnimation::Damage : CombatAnimation::Dodge;
|
||||
} else {
|
||||
result.attackerAnimation = CombatAnimation::Attack;
|
||||
result.animationVariant = random(1, 2);
|
||||
result.targetAnimation = isAttackSuccessful(result.type) ? CombatAnimation::Damage : CombatAnimation::Dodge;
|
||||
}
|
||||
} else {
|
||||
if (isRangedWieldType(attackerWield)) {
|
||||
result.attackerAnimation = CombatAnimation::RangedAttack;
|
||||
} else {
|
||||
result.attackerAnimation = CombatAnimation::Attack;
|
||||
result.animationVariant = random(1, 2);
|
||||
}
|
||||
}
|
||||
|
||||
result.attackerWieldType = attackerWield;
|
||||
|
||||
return move(result);
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
|
||||
} // namespace reone
|
41
src/game/combat/attackresolver.h
Normal file
41
src/game/combat/attackresolver.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 The reone project contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "attackresult.h"
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace game {
|
||||
|
||||
class Creature;
|
||||
class SpatialObject;
|
||||
|
||||
/**
|
||||
* Encapsulates combat attack logic.
|
||||
*/
|
||||
class AttackResolver {
|
||||
public:
|
||||
AttackResult getAttackResult(const std::shared_ptr<Creature> &attacker, const std::shared_ptr<SpatialObject> &target, bool duel, AttackResultType fixedType = AttackResultType::Invalid);
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
|
||||
} // namespace reone
|
37
src/game/combat/attackresult.h
Normal file
37
src/game/combat/attackresult.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 The reone project contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../object/types.h"
|
||||
#include "../types.h"
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace game {
|
||||
|
||||
struct AttackResult {
|
||||
AttackResultType type { AttackResultType::Invalid };
|
||||
CreatureWieldType attackerWieldType { CreatureWieldType::None };
|
||||
CombatAnimation attackerAnimation { CombatAnimation::None };
|
||||
int animationVariant { 1 };
|
||||
CombatAnimation targetAnimation { CombatAnimation::None };
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
|
||||
} // namespace reone
|
|
@ -22,11 +22,11 @@
|
|||
|
||||
#include "glm/common.hpp"
|
||||
|
||||
#include "../common/log.h"
|
||||
#include "../common/random.h"
|
||||
#include "../../common/log.h"
|
||||
#include "../../common/random.h"
|
||||
|
||||
#include "game.h"
|
||||
#include "rp/factionutil.h"
|
||||
#include "../game.h"
|
||||
#include "../rp/factionutil.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -271,7 +271,7 @@ void Combat::updateRound(Round &round, float dt) {
|
|||
|
||||
switch (round.state) {
|
||||
case RoundState::Started:
|
||||
round.attackResult = determineAttackResult(attacker, round.target, duel, round.cutsceneAttackResult);
|
||||
round.attackResult = _attackResolver.getAttackResult(attacker, round.target, duel, round.cutsceneAttackResult);
|
||||
|
||||
attacker->face(*round.target);
|
||||
attacker->setMovementType(Creature::MovementType::None);
|
||||
|
@ -299,7 +299,7 @@ void Combat::updateRound(Round &round, float dt) {
|
|||
}
|
||||
if (duel) {
|
||||
auto targetCreature = static_pointer_cast<Creature>(round.target);
|
||||
round.attackResult = determineAttackResult(targetCreature, attacker, true);
|
||||
round.attackResult = _attackResolver.getAttackResult(targetCreature, attacker, true);
|
||||
|
||||
targetCreature->face(*attacker);
|
||||
targetCreature->playAnimation(round.attackResult.attackerAnimation, round.attackResult.attackerWieldType, round.attackResult.animationVariant);
|
||||
|
@ -424,97 +424,6 @@ void Combat::cutsceneAttack(
|
|||
addRound(round);
|
||||
}
|
||||
|
||||
static bool isMeleeWieldType(CreatureWieldType type) {
|
||||
switch (type) {
|
||||
case CreatureWieldType::SingleSword:
|
||||
case CreatureWieldType::DoubleBladedSword:
|
||||
case CreatureWieldType::DualSwords:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool isAttackSuccessful(AttackResultType type) {
|
||||
switch (type) {
|
||||
case AttackResultType::HitSuccessful:
|
||||
case AttackResultType::CriticalHit:
|
||||
case AttackResultType::AutomaticHit:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool isRangedWieldType(CreatureWieldType type) {
|
||||
switch (type) {
|
||||
case CreatureWieldType::BlasterPistol:
|
||||
case CreatureWieldType::DualPistols:
|
||||
case CreatureWieldType::BlasterRifle:
|
||||
case CreatureWieldType::HeavyWeapon:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Combat::AttackResult Combat::determineAttackResult(const shared_ptr<Creature> &attacker, const shared_ptr<SpatialObject> &target, bool duel, AttackResultType type) {
|
||||
AttackResult result;
|
||||
|
||||
if (type != AttackResultType::Invalid) {
|
||||
result.type = type;
|
||||
} else {
|
||||
int attack = random(1, 20);
|
||||
int defense = 10; // TODO: add armor bonus and dexterity modifier
|
||||
|
||||
if (attack == 20) {
|
||||
result.type = AttackResultType::AutomaticHit;
|
||||
} else if (attack >= defense) {
|
||||
result.type = AttackResultType::HitSuccessful;
|
||||
} else {
|
||||
result.type = AttackResultType::Miss;
|
||||
}
|
||||
}
|
||||
|
||||
CreatureWieldType attackerWield = attacker->getWieldType();
|
||||
CreatureWieldType targetWield = CreatureWieldType::None;
|
||||
|
||||
auto targetCreature = dynamic_pointer_cast<Creature>(target);
|
||||
if (targetCreature) {
|
||||
targetWield = targetCreature->getWieldType();
|
||||
}
|
||||
|
||||
if (duel) {
|
||||
if (isMeleeWieldType(attackerWield) && isMeleeWieldType(targetWield)) {
|
||||
result.attackerAnimation = CombatAnimation::MeleeDuelAttack;
|
||||
result.animationVariant = random(1, 5);
|
||||
result.targetAnimation = isAttackSuccessful(result.type) ? CombatAnimation::MeleeDuelDamage : CombatAnimation::MeleeDuelParry;
|
||||
} else if (isMeleeWieldType(attackerWield)) {
|
||||
result.attackerAnimation = CombatAnimation::MeleeAttack;
|
||||
result.animationVariant = random(1, 2);
|
||||
result.targetAnimation = isAttackSuccessful(result.type) ? CombatAnimation::MeleeDamage : CombatAnimation::MeleeDodge;
|
||||
} else if (isRangedWieldType(attackerWield)) {
|
||||
result.attackerAnimation = CombatAnimation::RangedAttack;
|
||||
result.targetAnimation = isAttackSuccessful(result.type) ? CombatAnimation::Damage : CombatAnimation::Dodge;
|
||||
} else {
|
||||
result.attackerAnimation = CombatAnimation::Attack;
|
||||
result.animationVariant = random(1, 2);
|
||||
result.targetAnimation = isAttackSuccessful(result.type) ? CombatAnimation::Damage : CombatAnimation::Dodge;
|
||||
}
|
||||
} else {
|
||||
if (isRangedWieldType(attackerWield)) {
|
||||
result.attackerAnimation = CombatAnimation::RangedAttack;
|
||||
} else {
|
||||
result.attackerAnimation = CombatAnimation::Attack;
|
||||
result.animationVariant = random(1, 2);
|
||||
}
|
||||
}
|
||||
|
||||
result.attackerWieldType = attackerWield;
|
||||
|
||||
return move(result);
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
|
||||
} // namespace reone
|
|
@ -23,12 +23,14 @@
|
|||
#include <queue>
|
||||
#include <set>
|
||||
|
||||
#include "../common/timer.h"
|
||||
#include "../../common/timer.h"
|
||||
|
||||
#include "enginetype/effect.h"
|
||||
#include "object/creature.h"
|
||||
#include "rp/damageresolver.h"
|
||||
#include "types.h"
|
||||
#include "../enginetype/effect.h"
|
||||
#include "../object/creature.h"
|
||||
#include "../types.h"
|
||||
|
||||
#include "attackresolver.h"
|
||||
#include "damageresolver.h"
|
||||
|
||||
namespace reone {
|
||||
|
||||
|
@ -76,14 +78,6 @@ private:
|
|||
|
||||
typedef std::map<uint32_t, std::shared_ptr<Combatant>> CombatantMap;
|
||||
|
||||
struct AttackResult {
|
||||
AttackResultType type { AttackResultType::Invalid };
|
||||
CreatureWieldType attackerWieldType { CreatureWieldType::None };
|
||||
CombatAnimation attackerAnimation { CombatAnimation::None };
|
||||
int animationVariant { 1 };
|
||||
CombatAnimation targetAnimation { CombatAnimation::None };
|
||||
};
|
||||
|
||||
struct Round {
|
||||
std::shared_ptr<Combatant> attacker;
|
||||
std::shared_ptr<SpatialObject> target;
|
||||
|
@ -101,6 +95,7 @@ private:
|
|||
|
||||
Game *_game;
|
||||
bool _active { false };
|
||||
AttackResolver _attackResolver;
|
||||
DamageResolver _damageResolver;
|
||||
|
||||
Timer _heartbeatTimer { 0.0f };
|
||||
|
@ -129,8 +124,6 @@ private:
|
|||
|
||||
// Attacks
|
||||
|
||||
AttackResult determineAttackResult(const std::shared_ptr<Creature> &attacker, const std::shared_ptr<SpatialObject> &target, bool duel, AttackResultType type = AttackResultType::Invalid);
|
||||
|
||||
void applyAttackResult(const std::shared_ptr<Creature> &attacker, const std::shared_ptr<SpatialObject> &target, AttackResult result, int damage = -1);
|
||||
|
||||
// END Attacks
|
|
@ -29,7 +29,6 @@
|
|||
#include "../../resource/gfffile.h"
|
||||
#include "../../resource/types.h"
|
||||
|
||||
#include "../combat.h"
|
||||
#include "../actionexecutor.h"
|
||||
#include "../camera/animatedcamera.h"
|
||||
#include "../camera/dialogcamera.h"
|
||||
|
@ -38,6 +37,7 @@
|
|||
#include "../camera/thirdperson.h"
|
||||
#include "../camera/types.h"
|
||||
#include "../collisiondetect.h"
|
||||
#include "../combat/combat.h"
|
||||
#include "../map.h"
|
||||
#include "../objectselect.h"
|
||||
#include "../pathfinder.h"
|
||||
|
|
|
@ -487,7 +487,11 @@ Variable Routines::applyEffectToObject(const VariablesList &args, ExecutionConte
|
|||
auto target = getSpatialObject(args, 2);
|
||||
float duration = getFloat(args, 3, 0.0f);
|
||||
|
||||
target->applyEffect(effect, durationType, duration);
|
||||
if (target) {
|
||||
target->applyEffect(effect, durationType, duration);
|
||||
} else {
|
||||
warn("Routines: applyEffectToObject: target is invalid");
|
||||
}
|
||||
|
||||
return Variable();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue