Skip to content

Commit

Permalink
feat: min delay between conditions (#2079)
Browse files Browse the repository at this point in the history
Simple (optional) feature to suppress conditions if they had been
inflicted onto the player in short succession. This can be used, for
example, to make paralyze healing less annoying.
  • Loading branch information
luan authored Jan 23, 2024
1 parent 7cb804c commit efa1817
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 22 deletions.
2 changes: 2 additions & 0 deletions config.lua.dist
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,8 @@ coinPacketSize = 25
coinImagesURL = "http://127.0.0.1/images/store/"
classicAttackSpeed = false
showScriptsLogInConsole = false
-- time to suppress negative conditions after being affected by them (ms)
minDelayBetweenConditions = 0
-- configure maximum value of critical imbuement
criticalChance = 10
inventoryGlowOnFiveBless = false
Expand Down
1 change: 1 addition & 0 deletions src/config/config_definitions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ enum ConfigKey_t : uint16_t {
METRICS_ENABLE_PROMETHEUS,
METRICS_OSTREAM_INTERVAL,
METRICS_PROMETHEUS_ADDRESS,
MIN_DELAY_BETWEEN_CONDITIONS,
MIN_ELEMENTAL_RESISTANCE,
MOMENTUM_CHANCE_FORMULA_A,
MOMENTUM_CHANCE_FORMULA_B,
Expand Down
1 change: 1 addition & 0 deletions src/config/configmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ bool ConfigManager::load() {
loadBoolConfig(L, CLEAN_PROTECTION_ZONES, "cleanProtectionZones", false);
loadBoolConfig(L, GLOBAL_SERVER_SAVE_SHUTDOWN, "globalServerSaveShutdown", true);
loadBoolConfig(L, PUSH_WHEN_ATTACKING, "pushWhenAttacking", false);
loadIntConfig(L, MIN_DELAY_BETWEEN_CONDITIONS, "minDelayBetweenConditions", 0);

loadBoolConfig(L, WEATHER_RAIN, "weatherRain", false);
loadBoolConfig(L, WEATHER_THUNDER, "thunderEffect", false);
Expand Down
2 changes: 1 addition & 1 deletion src/creatures/combat/combat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -744,7 +744,7 @@ void Combat::CombatConditionFunc(std::shared_ptr<Creature> caster, std::shared_p

// TODO: infight condition until all aggressive conditions has ended
if (target) {
target->addCombatCondition(conditionCopy);
target->addCombatCondition(conditionCopy, caster && caster->getPlayer() != nullptr);
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/creatures/combat/condition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1644,7 +1644,9 @@ bool ConditionDamage::getNextDamage(int32_t &damage) {
}

bool ConditionDamage::doDamage(std::shared_ptr<Creature> creature, int32_t healthChange) {
if (creature->isSuppress(getType())) {
auto attacker = g_game().getPlayerByGUID(owner) ? g_game().getPlayerByGUID(owner)->getCreature() : g_game().getCreatureByID(owner);
bool isPlayer = attacker && attacker->getPlayer();
if (creature->isSuppress(getType(), isPlayer)) {
return true;
}

Expand All @@ -1653,7 +1655,6 @@ bool ConditionDamage::doDamage(std::shared_ptr<Creature> creature, int32_t healt
damage.primary.value = healthChange;
damage.primary.type = Combat::ConditionToDamageType(conditionType);

std::shared_ptr<Creature> attacker = g_game().getCreatureByID(owner);
if (field && creature->getPlayer() && attacker && attacker->getPlayer()) {
damage.primary.value = static_cast<int32_t>(std::round(damage.primary.value / 2.));
}
Expand Down
12 changes: 7 additions & 5 deletions src/creatures/creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1334,12 +1334,14 @@ bool Creature::setMaster(std::shared_ptr<Creature> newMaster, bool reloadCreatur
return true;
}

bool Creature::addCondition(std::shared_ptr<Condition> condition) {
bool Creature::addCondition(std::shared_ptr<Condition> condition, bool attackerPlayer /* = false*/) {
metrics::method_latency measure(__METHOD_NAME__);
if (condition == nullptr) {
return false;
}

if (isSuppress(condition->getType(), attackerPlayer)) {
return false;
}
std::shared_ptr<Condition> prevCond = getCondition(condition->getType(), condition->getId(), condition->getSubId());
if (prevCond) {
prevCond->addCondition(getCreature(), condition);
Expand All @@ -1355,11 +1357,11 @@ bool Creature::addCondition(std::shared_ptr<Condition> condition) {
return false;
}

bool Creature::addCombatCondition(std::shared_ptr<Condition> condition) {
bool Creature::addCombatCondition(std::shared_ptr<Condition> condition, bool attackerPlayer /* = false*/) {
// Caution: condition variable could be deleted after the call to addCondition
ConditionType_t type = condition->getType();

if (!addCondition(condition)) {
if (!addCondition(condition, attackerPlayer)) {
return false;
}

Expand Down Expand Up @@ -1490,7 +1492,7 @@ void Creature::executeConditions(uint32_t interval) {

bool Creature::hasCondition(ConditionType_t type, uint32_t subId /* = 0*/) const {
metrics::method_latency measure(__METHOD_NAME__);
if (isSuppress(type)) {
if (isSuppress(type, false)) {
return false;
}

Expand Down
6 changes: 3 additions & 3 deletions src/creatures/creature.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -388,8 +388,8 @@ class Creature : virtual public Thing, public SharedObject {
return SPEECHBUBBLE_NONE;
}

bool addCondition(std::shared_ptr<Condition> condition);
bool addCombatCondition(std::shared_ptr<Condition> condition);
bool addCondition(std::shared_ptr<Condition> condition, bool attackerPlayer = false);
bool addCombatCondition(std::shared_ptr<Condition> condition, bool attackerPlayer = false);
void removeCondition(ConditionType_t conditionType, ConditionId_t conditionId, bool force = false);
void removeCondition(ConditionType_t type);
void removeCondition(std::shared_ptr<Condition> condition);
Expand All @@ -406,7 +406,7 @@ class Creature : virtual public Thing, public SharedObject {
virtual bool isImmune(ConditionType_t type) const {
return false;
}
virtual bool isSuppress(ConditionType_t type) const {
virtual bool isSuppress(ConditionType_t type, bool attackerPlayer) const {
return false;
};

Expand Down
22 changes: 22 additions & 0 deletions src/creatures/creatures_definitions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,28 @@ enum ConditionType_t : uint8_t {
CONDITION_COUNT = 39
};

// constexpr definiting suppressible conditions
constexpr bool IsConditionSuppressible(ConditionType_t condition) {
constexpr ConditionType_t suppressibleConditions[] = {
CONDITION_POISON,
CONDITION_FIRE,
CONDITION_ENERGY,
CONDITION_BLEEDING,
CONDITION_PARALYZE,
CONDITION_DROWN,
CONDITION_FREEZING,
CONDITION_CURSED,
};

for (const auto &suppressibleCondition : suppressibleConditions) {
if (condition == suppressibleCondition) {
return true;
}
}

return false;
}

enum ConditionParam_t {
CONDITION_PARAM_OWNER = 1,
CONDITION_PARAM_TICKS = 2,
Expand Down
12 changes: 10 additions & 2 deletions src/creatures/players/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,12 @@ std::shared_ptr<Item> Player::getInventoryItem(Slots_t slot) const {
return inventory[slot];
}

bool Player::isSuppress(ConditionType_t conditionType) const {
bool Player::isSuppress(ConditionType_t conditionType, bool attackerPlayer) const {
auto minDelay = g_configManager().getNumber(MIN_DELAY_BETWEEN_CONDITIONS, __FUNCTION__);
if (IsConditionSuppressible(conditionType) && checkLastConditionTimeWithin(conditionType, minDelay)) {
return true;
}

return m_conditionSuppressions[static_cast<size_t>(conditionType)];
}

Expand Down Expand Up @@ -450,7 +455,7 @@ float Player::getDefenseFactor() const {
uint32_t Player::getClientIcons() {
uint32_t icons = 0;
for (const auto &condition : conditions) {
if (!isSuppress(condition->getType())) {
if (!isSuppress(condition->getType(), false)) {
icons |= condition->getIcons();
}
}
Expand Down Expand Up @@ -4481,6 +4486,9 @@ void Player::onAddCondition(ConditionType_t type) {
}

void Player::onAddCombatCondition(ConditionType_t type) {
if (IsConditionSuppressible(type)) {
updateLastConditionTime(type);
}
switch (type) {
case CONDITION_POISON:
sendTextMessage(MESSAGE_FAILURE, "You are poisoned.");
Expand Down
30 changes: 21 additions & 9 deletions src/creatures/players/player.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,25 @@ class Player final : public Creature, public Cylinder, public Bankable {
return lastAttackBlockType;
}

uint64_t getLastConditionTime(ConditionType_t type) const {
if (!lastConditionTime.contains(static_cast<uint8_t>(type))) {
return 0;
}
return lastConditionTime.at(static_cast<uint8_t>(type));
}

void updateLastConditionTime(ConditionType_t type) {
lastConditionTime[static_cast<uint8_t>(type)] = OTSYS_TIME();
}

bool checkLastConditionTimeWithin(ConditionType_t type, uint32_t interval) const {
if (!lastConditionTime.contains(static_cast<uint8_t>(type))) {
return false;
}
auto last = lastConditionTime.at(static_cast<uint8_t>(type));
return last > 0 && ((OTSYS_TIME() - last) < interval);
}

uint64_t getLastAttack() const {
return lastAttack;
}
Expand Down Expand Up @@ -924,13 +943,6 @@ class Player final : public Creature, public Cylinder, public Bankable {
lastAggressiveAction = OTSYS_TIME();
}

uint64_t getLastFocusLost() const {
return lastFocusLost;
}
void setLastFocusLost(uint64_t time) {
lastFocusLost = time;
}

std::unordered_set<std::string> getNPCSkips();

std::shared_ptr<Item> getWeapon(Slots_t slot, bool ignoreAmmo) const;
Expand Down Expand Up @@ -2731,8 +2743,8 @@ class Player final : public Creature, public Cylinder, public Bankable {
uint64_t experience = 0;
uint64_t manaSpent = 0;
uint64_t lastAttack = 0;
std::unordered_map<uint8_t, uint64_t> lastConditionTime;
uint64_t lastAggressiveAction = 0;
uint64_t lastFocusLost = 0;
uint64_t bankBalance = 0;
uint64_t lastQuestlogUpdate = 0;
uint64_t preyCards = 0;
Expand Down Expand Up @@ -2953,7 +2965,7 @@ class Player final : public Creature, public Cylinder, public Bankable {
return skillLoss ? static_cast<uint64_t>(experience * getLostPercent()) : 0;
}

bool isSuppress(ConditionType_t conditionType) const override;
bool isSuppress(ConditionType_t conditionType, bool attackerPlayer) const override;
void addConditionSuppression(const std::array<ConditionType_t, ConditionType_t::CONDITION_COUNT> &addConditions);

uint16_t getLookCorpse() const override;
Expand Down

0 comments on commit efa1817

Please sign in to comment.