How do buffs and skill work together in combat

There are many forms of fighting in online games, and the fighting logic of different games also varies greatly. But generally it involves a skill system and a buff system, and the two are related to each other. Skills can be buffed on the target, affecting the target. Buffs also affect the release of abilities, both of which are arguably the most important elements of a game’s combat system.

There are a wide variety of complex buffs and skills available in game combat, which are formed from a combination of basic buffs and skill effect mechanics. A variety of complex buffs and skills can be created by combining basic mechanic effects. These basic buff effects and skill effects mechanisms allow curators to influence the actual effects of these buffs and skills by configuring data in a profile.

Below, generally at the time of release skills will check if there is a silence or current impact skills release buff, according to the current buff, skills to calculate ratio, if not hit, may need to stack some buff, or make one buff disappear, or make an enemy, friendly increase gain debuffs buff, if hit, Damage is calculated based on the buffs’ buffs and buffs’ buffs of the current skill, and since abilities generally carry one or more effects, new buffs for those effects may be applied to the included friend or enemy (depending on the planning configuration and skill logic).

Here is my understanding of the buff system.

The design of the buff

The source of the buff may be the ability cast by the combat unit, it may be the buff effect that generates a new buff, it may be the equipment worn at the beginning of the battle.

Buffs have various effects such as increase/decrease stats, drain/restore health for a certain amount of time, mana, stun, Silence, shield, taunt, reverse damage, increase damage on attack, decrease damage on hit, damage on death, health regeneration on hit, etc. The same combat unit can have multiple buffs.

To increase the usability and flexibility of the buff system, we can split a buff into one or more base buff effects, and then flexibly combine them to form a variety of buffs by implementing base buff effects. For example, a buff that gives a hero a shield and restores 10% of the hero’s health when the buff is removed has two basic effects, one is a shield and the other is a buff that restores health when the buff is removed. A buff can have one or more buffs, and a hero can have multiple buffs.

In order to manage the many buffs that exist in combat units, you need a Buff manager to manage the entire life cycle of adding, enabling, and removing buffs. The only interface for external use is a buffManger, such as adding buffs, sending buff lists, checking buff status, Called at trigger point to perform buff effects, etc. Buff Base effects, buff, buff manager relationships are as follows:

Buffs manage one or more buff class objects. Each Buff class contains a pointer to the current Buff effect (effect ID, Buff configuration data, Buff generation time, Buff type, Buff removal flag, etc.), a pointer to which object the Buff is acting on, a pointer to the Buff reliever, and the number of stacks the Buff has.

Struct Buff {Effect* pEffect; // Victimtunit * victimerUnit; FightUnit* caster; // Uint8_t leftRound; int modValue_1; // modValue_2; // Uint8_t castRound; // public: void calcModValue(FightUnit* pUnit, FightUnit* pVictimer, FightAttack* pFa, uint32_t harmValue = 0); };Copy the code

Struct Effect{public: uint32_t effectId; Bool isbadeFFect; // Gain or overflow buff bool isaddAfterhit; //0 Uint32_t groupNo; Uint32_t priority = uint32_t priority; // Priority EFFECTTARGETSEL ets; uint8_t targetselecttypepara; uint32_t addRatio; bool isCalcRatio; EffectCalc ec1; EffectCalc ec2; uint8_t effectcontrol; Uint8_t round; Public: static map<uint32_t, Effect> effectMap; static int loadEffect(char* szFileName); static Effect* getEffect(uint32_t effectId); static void getTargets(FightUnit* pUnit, FightForce* pForce, FightAttack* pFa, Effect* pEffect, list<FightUnit*>& targetList); }; Struct FightUnit{public: FightUnit(); virtual ~FightUnit(); public: bool isNpc; union { PlayerPartner* pHero; NpcPartner* pNpcHero; }Hero; uint32_t maxHp; uint8_t orgChakra; // Temporary copy of Chakra FightProp partnerProp; PHALANX_POS pos; FightForce* myForce; bool isAttack; // Whether the attacker is BuffManger bufs; Buffmng list<Buff> castBufs; // Uint8_t fightUnitType; // Uint64_t playerid; Public: void havePassiveSkill(uint32_t psTypeId, int psTbl); void getPsParam(uint32_t psTypeId, int psTbl); uint8_t getTriggerPsCount(uint32_t psId); void setTriggerPsCount(uint32_t psId, uint8_t triggerCount); FightProp* getFightProp(); public: bool canAttack(); Buff* hasBuf(uint8_t et); Buff* hasInBuf(uint32_t effectId); void doBuf(FightAttack* pFa); void delBuf(uint32_t effectId); void replaceBuf(Buff* pOrgBuf, Buff* pDestBuff); void playBuf(Buff* pBuff, FightAttack* pFa, bool bCrit = false); void addBufTo(FightUnit* pVctimer, Effect* pEffect, FightAttack* pFa, SkillTem* pSkill); uint32_t getPropValue(uint16_t propId, uint32_t harmValue = 0); uint32_t calcHit(FightUnit* pVictimer, SkillTem* pSkill); uint32_t calcCrit(FightUnit* pVictimer, SkillTem* pSkill); uint32_t calcEffectCrit(SkillTem* pSkill); uint32_t calcHarm(FightUnit* pVictimer, SkillTem* pSkill); uint32_t calcCritHarm(uint32_t harm, FightUnit* pVictimer); uint32_t calcEffectCritHarm(uint32_t harm); uint32_t calcBlock(FightUnit* pVictimer); int calcBufRatio(FightUnit* pVictmer); uint32_t applyHarm(int& harm); void clearBuf(FightAttack* pFa); void caclPvpLevelSuppress(int& harmValue); void clearRoundBuff(FightAttack* fa); Uint32_t calcBuffNumByType(uint8_t type); Private: map<uint32_t, uint8_t> psTriggerCountMap; // The number of times a passive skill triggers};Copy the code

BuffManger Saves the set of buffs, the combat unit that buffManger belongs to, and so on. Controls the entire life cycle of the buff

class BuffManger{ public: buffManger(FightUnit* _owner); virtual ~buffManger(); // Implement some functions, such as add/remove buff functions, timetick function, buff trigger function, check buff effects function, etc. std::vector<Buff*> vecBuffs; unsigned short buffPerformStatus[eBuffPerfrom_Max]; }Copy the code

Buffs can be in various states, such as stun. When a combat unit moves or casts a skill, check that the buffManger of the combat unit traverses all the buffs of all the vecBuffs to see if there is any stun. If there is any that cannot move or casts a skill. It is also possible to add attribute values, which are recalculated based on the buff effect when adding or removing a buff. Or trigger. For example, drain may drain health per turn for turn-based games and per second for non-turn-based games. Trigger there are many points that trigger, such as when you die, when you hit, when you take a hit, when you crit, when you block, etc., so every time you get to the trigger point, you have to go through all the mechanics of the buff to find the ones that are eligible to execute the buff.

To reduce the number of traversals, you can add the buffPerformStatus array variable to the buffManger, which mimics only Pointers to do one count. Each member represents the buffManger’s buff count. If there is no buff count, the count is zero. If there is a buff when adding a buff, the count is increased by one. This way we don’t have to go through all the buffs when checking for certain buff states, such as stun. When triggered, iterate if there is a corresponding buff.

In this way, the buff system model can be used for most battles and can be used for different battles with minor modifications.

How is the Skill system designed

In the combat system of online games, skill is a series of skill effects caused by the release of combat units on the target. A combat unit can have multiple skills, according to the player operation or automatic, determine the release of skills to meet the conditions (CD, magic and other conditions) release skills, at this time according to the skills to generate a skill Action object, the final effect on the target.

Each combat unit needs a skillManger object to manage the skills it owns (skill object). The Skill object stores the basic configuration data of the skill, the last time the skill was released, and other information, which is used to generate skillAction objects. SkillAction objects, on the other hand, execute according to the timeline, ultimately producing skill effects on the target.

Combat units release skills into a skill Action, which can be divided into: start, chant, shoot, fly, activate, and end. The duration of each stage is controlled by the configuration table. The duration of each stage is consistent with the front-end playing action time. At the beginning of each stage, the back-end sends a message to the front-end to inform the front-end of the skill stage and duration, and then the front-end plays the corresponding action according to the stage.

If the skill is remote shooting bullets, the release stage is over, generating bullets to fly, can be called the bullet stage, according to the time interval to pick points, check the impact effect, bullets disappear, enter the end stage, the entire skill Action ends.

Skill class

class skill{ public:skill(const cSkillCfg* _cfg, unsigned int _skillId, unsigned short _skillLv); Virtual ~ Skill () {}// Check the CD time of a skill to the millisecond bool checkSkillCD(unsigned Long Long currentTm) const; Void onUseSkill(unsigned long long currentTm) void onUseSkill(unsigned long long currentTm); // Add handlers as needed public: const cSkillCfg* CFG; // Unsigned int skillId; // Skill ID unsigned short skillLv; // Skill level unsigned long long lastUseTime; // The last time the skill was released was used to calculate CD};Copy the code

SkillManger manages combat unit skills

class skillManager{ public: skillManager(FightUnit* _owner); virtual ~skillManager(); // Initialize the skills table based on the combat unit data, and save the generated skills table void setup(); Skill * getSkill(unsigned short skillIndex); Public: FightUnit* owner; public: FightUnit* owner; STD ::vector<skill*> skillList; // Skill list};Copy the code

Skill release depends on how skill generates skillAction processing skills

class skillAction{ public: skillAction(fightScene* _scene, skill* _skill, fightUnit* _fighter); Virtual ~skillAction() {} void doSkillOnTimer(unsigned long long currentTm);} void doSkillOnTimer(unsigned long long currentTm); // Define a set of basic functions for skill effect according to skill effect number, save function pointer to the list, call public according to the effect number: fightScene* scene; skill* s; fightUnit* fighter; eSkillActionStage skillStage; // Unsigned long long stageEndTime; // Unsigned long stageEndTime; // End time of current phase}Copy the code

There are one or more effective effects of skills. To form different skills through combination, it is also necessary to realize some basic skill mechanism effects and form different skills through combination. A skill that inflicts damage and adds a debug to the target is made up of two basic skill mechanics: skill damage + bonus buff. In this way, we only need to implement the basic skill effect mechanism, the pointer of the different skill mechanism effect function is stored in the array, and the function is called according to the skill mechanism effect number to achieve the skill effect.

The next chapter will introduce you to the core logic of turn-based combat systems.