Implement duration ramp and war cry with new AA system

Rampage also correctly does a full attack round for classes
other than monk and ranger
This commit is contained in:
Michael Cook (mackal) 2015-06-21 02:01:48 -04:00
parent d5098a56e0
commit d34b4a786b
8 changed files with 102 additions and 141 deletions

View File

@ -828,7 +828,7 @@ void Client::SendAlternateAdvancementRank(int aa_id, int level) {
if(!CanUseAlternateAdvancementRank(rank)) { if(!CanUseAlternateAdvancementRank(rank)) {
return; return;
} }
int size = sizeof(AARankInfo_Struct) + (sizeof(AARankEffect_Struct) * rank->effects.size()) + (sizeof(AARankPrereq_Struct) * rank->prereqs.size()); int size = sizeof(AARankInfo_Struct) + (sizeof(AARankEffect_Struct) * rank->effects.size()) + (sizeof(AARankPrereq_Struct) * rank->prereqs.size());
EQApplicationPacket *outapp = new EQApplicationPacket(OP_SendAATable, size); EQApplicationPacket *outapp = new EQApplicationPacket(OP_SendAATable, size);
AARankInfo_Struct *aai = (AARankInfo_Struct*)outapp->pBuffer; AARankInfo_Struct *aai = (AARankInfo_Struct*)outapp->pBuffer;
@ -996,7 +996,7 @@ void Client::PurchaseAlternateAdvancementRank(int rank_id) {
if(!CanPurchaseAlternateAdvancementRank(rank, true)) { if(!CanPurchaseAlternateAdvancementRank(rank, true)) {
return; return;
} }
if(rank->base_ability->charges > 0) { if(rank->base_ability->charges > 0) {
uint32 charges = 0; uint32 charges = 0;
GetAA(rank_id, &charges); GetAA(rank_id, &charges);
@ -1004,7 +1004,7 @@ void Client::PurchaseAlternateAdvancementRank(int rank_id) {
if(charges > 0) { if(charges > 0) {
return; return;
} }
SetAA(rank_id, rank->current_value, rank->base_ability->charges); SetAA(rank_id, rank->current_value, rank->base_ability->charges);
} else { } else {
SetAA(rank_id, rank->current_value, 0); SetAA(rank_id, rank->current_value, 0);
@ -1022,10 +1022,10 @@ void Client::PurchaseAlternateAdvancementRank(int rank_id) {
SendAlternateAdvancementStats(); SendAlternateAdvancementStats();
if(rank->prev) { if(rank->prev) {
Message_StringID(15, AA_IMPROVE, Message_StringID(15, AA_IMPROVE,
std::to_string(rank->title_sid).c_str(), std::to_string(rank->title_sid).c_str(),
std::to_string(rank->prev->current_value).c_str(), std::to_string(rank->prev->current_value).c_str(),
std::to_string(rank->cost).c_str(), std::to_string(rank->cost).c_str(),
std::to_string(AA_POINTS).c_str()); std::to_string(AA_POINTS).c_str());
/* QS: Player_Log_AA_Purchases */ /* QS: Player_Log_AA_Purchases */
@ -1034,9 +1034,9 @@ void Client::PurchaseAlternateAdvancementRank(int rank_id) {
QServ->PlayerLogEvent(Player_Log_AA_Purchases, CharacterID(), event_desc); QServ->PlayerLogEvent(Player_Log_AA_Purchases, CharacterID(), event_desc);
} }
} else { } else {
Message_StringID(15, AA_GAIN_ABILITY, Message_StringID(15, AA_GAIN_ABILITY,
std::to_string(rank->title_sid).c_str(), std::to_string(rank->title_sid).c_str(),
std::to_string(rank->cost).c_str(), std::to_string(rank->cost).c_str(),
std::to_string(AA_POINTS).c_str()); std::to_string(AA_POINTS).c_str());
/* QS: Player_Log_AA_Purchases */ /* QS: Player_Log_AA_Purchases */
if (RuleB(QueryServ, PlayerLogAAPurchases)){ if (RuleB(QueryServ, PlayerLogAAPurchases)){
@ -1125,7 +1125,7 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
if(!IsValidSpell(rank->spell)) { if(!IsValidSpell(rank->spell)) {
return; return;
} }
if(!CanUseAlternateAdvancementRank(rank)) { if(!CanUseAlternateAdvancementRank(rank)) {
return; return;
} }
@ -1448,7 +1448,7 @@ bool Mob::CanPurchaseAlternateAdvancementRank(AA::Rank *rank, bool check_price)
if(!ability) if(!ability)
return false; return false;
if(!CanUseAlternateAdvancementRank(rank)) { if(!CanUseAlternateAdvancementRank(rank)) {
return false; return false;
} }
@ -1474,7 +1474,7 @@ bool Mob::CanPurchaseAlternateAdvancementRank(AA::Rank *rank, bool check_price)
} }
//if expendable only let us purchase if we have no charges already //if expendable only let us purchase if we have no charges already
//not quite sure on how this functions client side atm //not quite sure on how this functions client side atm
//I intend to look into it later to make sure the behavior is right //I intend to look into it later to make sure the behavior is right
if(ability->charges > 0 && current_charges > 0) { if(ability->charges > 0 && current_charges > 0) {
return false; return false;
@ -1565,7 +1565,7 @@ void Zone::LoadAlternateAdvancement() {
} }
bool ZoneDatabase::LoadAlternateAdvancementAbilities(std::unordered_map<int, std::unique_ptr<AA::Ability>> &abilities, bool ZoneDatabase::LoadAlternateAdvancementAbilities(std::unordered_map<int, std::unique_ptr<AA::Ability>> &abilities,
std::unordered_map<int, std::unique_ptr<AA::Rank>> &ranks) std::unordered_map<int, std::unique_ptr<AA::Rank>> &ranks)
{ {
Log.Out(Logs::General, Logs::Status, "Loading Alternate Advancement Abilities..."); Log.Out(Logs::General, Logs::Status, "Loading Alternate Advancement Abilities...");
abilities.clear(); abilities.clear();
@ -1727,3 +1727,18 @@ void Mob::GrantAlternateAdvancementAbility(int aa_id, int points) {
c->CalcBonuses(); c->CalcBonuses();
} }
} }
bool Mob::CheckAATimer(int timer)
{
if (timer >= aaTimerMax)
return false;
if (aa_timers[timer].Enabled()) {
if (aa_timers[timer].Check(false)) {
aa_timers[timer].Disable();
return false;
} else {
return true;
}
}
return false;
}

View File

@ -5056,3 +5056,39 @@ void NPC::SetAttackTimer()
TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true, true); TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true, true);
} }
} }
void Client::DoAttackRounds(Mob *target, int hand, bool IsFromSpell)
{
if (!target)
return;
Attack(target, hand, false, false, IsFromSpell);
if (CanThisClassDoubleAttack()) {
CheckIncreaseSkill(SkillDoubleAttack, target, -10);
if (CheckDoubleAttack())
Attack(target, hand, false, false, IsFromSpell);
if (hand == MainPrimary && GetLevel() >= 60 &&
(GetClass() == MONK || GetClass() == WARRIOR || GetClass() == RANGER || GetClass() == BERSERKER) &&
CheckDoubleAttack(true))
Attack(target, hand, false, false, IsFromSpell);
}
if (hand == MainPrimary) {
auto flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance;
if (flurrychance && zone->random.Roll(flurrychance)) {
Message_StringID(MT_NPCFlurry, YOU_FLURRY);
Attack(target, hand, false, false, IsFromSpell);
Attack(target, hand, false, false, IsFromSpell);
}
auto extraattackchance = aabonuses.ExtraAttackChance + spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance;
if (extraattackchance) {
auto wpn = GetInv().GetItem(MainPrimary);
if (wpn && (wpn->GetItem()->ItemType == ItemType2HBlunt ||
wpn->GetItem()->ItemType == ItemType2HSlash ||
wpn->GetItem()->ItemType == ItemType2HPiercing))
if (zone->random.Roll(extraattackchance))
Attack(target, hand, false, false, IsFromSpell);
}
}
}

View File

@ -226,6 +226,7 @@ public:
virtual int32 GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating); virtual int32 GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating);
virtual void SetAttackTimer(); virtual void SetAttackTimer();
float GetQuiverHaste(); float GetQuiverHaste();
void DoAttackRounds(Mob *target, int hand, bool IsFromSpell = false);
void AI_Init(); void AI_Init();
void AI_Start(uint32 iMoveDelay = 0); void AI_Start(uint32 iMoveDelay = 0);
@ -774,7 +775,7 @@ public:
void AddAAPoints(uint32 points) { m_pp.aapoints += points; SendAlternateAdvancementStats(); } void AddAAPoints(uint32 points) { m_pp.aapoints += points; SendAlternateAdvancementStats(); }
int GetAAPoints() { return m_pp.aapoints; } int GetAAPoints() { return m_pp.aapoints; }
int GetSpentAA() { return m_pp.aapoints_spent; } int GetSpentAA() { return m_pp.aapoints_spent; }
//old AA methods that we still use //old AA methods that we still use
void ResetAA(); void ResetAA();
void RefundAA(); void RefundAA();

View File

@ -391,74 +391,12 @@ bool Client::Process() {
} }
else if (auto_attack_target->GetHP() > -10) // -10 so we can watch people bleed in PvP else if (auto_attack_target->GetHP() > -10) // -10 so we can watch people bleed in PvP
{ {
//old aa
//if(CheckAAEffect(aaEffectRampage))
//{
// entity_list.AEAttack(this, 30);
//} else {
Attack(auto_attack_target, MainPrimary); // Kaiyodo - added attacking hand to arguments
//}
ItemInst *wpn = GetInv().GetItem(MainPrimary); ItemInst *wpn = GetInv().GetItem(MainPrimary);
TryWeaponProc(wpn, auto_attack_target, MainPrimary); TryWeaponProc(wpn, auto_attack_target, MainPrimary);
bool tripleAttackSuccess = false; DoAttackRounds(auto_attack_target, MainPrimary);
if( auto_attack_target && CanThisClassDoubleAttack() ) { if (CheckAATimer(aaTimerRampage))
entity_list.AEAttack(this, 30);
CheckIncreaseSkill(SkillDoubleAttack, auto_attack_target, -10);
if(CheckDoubleAttack()) {
//should we allow rampage on double attack?
//if(CheckAAEffect(aaEffectRampage)) {
// entity_list.AEAttack(this, 30);
//} else {
Attack(auto_attack_target, MainPrimary, false);
//}
}
//triple attack: rangers, monks, warriors, berserkers over level 60
if((((GetClass() == MONK || GetClass() == WARRIOR || GetClass() == RANGER || GetClass() == BERSERKER)
&& GetLevel() >= 60) || GetSpecialAbility(SPECATK_TRIPLE))
&& CheckDoubleAttack(true))
{
tripleAttackSuccess = true;
Attack(auto_attack_target, MainPrimary, false);
}
//quad attack, does this belong here??
if(GetSpecialAbility(SPECATK_QUAD) && CheckDoubleAttack(true))
{
Attack(auto_attack_target, MainPrimary, false);
}
}
//Live AA - Flurry, Rapid Strikes ect (Flurry does not require Triple Attack).
int16 flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance;
if (auto_attack_target && flurrychance)
{
if(zone->random.Int(0, 99) < flurrychance)
{
Message_StringID(MT_NPCFlurry, YOU_FLURRY);
Attack(auto_attack_target, MainPrimary, false);
Attack(auto_attack_target, MainPrimary, false);
}
}
int16 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance + aabonuses.ExtraAttackChance;
if (auto_attack_target && ExtraAttackChanceBonus) {
ItemInst *wpn = GetInv().GetItem(MainPrimary);
if(wpn){
if(wpn->GetItem()->ItemType == ItemType2HSlash ||
wpn->GetItem()->ItemType == ItemType2HBlunt ||
wpn->GetItem()->ItemType == ItemType2HPiercing )
{
if(zone->random.Int(0, 99) < ExtraAttackChanceBonus)
{
Attack(auto_attack_target, MainPrimary, false);
}
}
}
}
} }
} }
@ -499,23 +437,11 @@ bool Client::Process() {
float random = zone->random.Real(0, 1); float random = zone->random.Real(0, 1);
CheckIncreaseSkill(SkillDualWield, auto_attack_target, -10); CheckIncreaseSkill(SkillDualWield, auto_attack_target, -10);
if (random < DualWieldProbability){ // Max 78% of DW if (random < DualWieldProbability) { // Max 78% of DW
//if(CheckAAEffect(aaEffectRampage)) {
// entity_list.AEAttack(this, 30, MainSecondary);
//} else {
Attack(auto_attack_target, MainSecondary); // Single attack with offhand
//}
ItemInst *wpn = GetInv().GetItem(MainSecondary); ItemInst *wpn = GetInv().GetItem(MainSecondary);
TryWeaponProc(wpn, auto_attack_target, MainSecondary); TryWeaponProc(wpn, auto_attack_target, MainSecondary);
if( CanThisClassDoubleAttack() && CheckDoubleAttack()) { DoAttackRounds(auto_attack_target, MainSecondary);
//if(CheckAAEffect(aaEffectRampage)) {
// entity_list.AEAttack(this, 30, MainSecondary);
//} else {
// if(auto_attack_target && auto_attack_target->GetHP() > -10)
Attack(auto_attack_target, MainSecondary); // Single attack with offhand
//}
}
} }
} }
} }

View File

@ -878,7 +878,7 @@ void EntityList::AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool aff
caster->CastToClient()->CheckSongSkillIncrease(spell_id); caster->CastToClient()->CheckSongSkillIncrease(spell_id);
} }
//Dook- Rampage and stuff for clients. // Rampage and stuff for clients. Normal and Duration rampages
//NPCs handle it differently in Mob::Rampage //NPCs handle it differently in Mob::Rampage
void EntityList::AEAttack(Mob *attacker, float dist, int Hand, int count, bool IsFromSpell) { void EntityList::AEAttack(Mob *attacker, float dist, int Hand, int count, bool IsFromSpell) {
//Dook- Will need tweaking, currently no pets or players or horses //Dook- Will need tweaking, currently no pets or players or horses
@ -896,7 +896,10 @@ void EntityList::AEAttack(Mob *attacker, float dist, int Hand, int count, bool I
&& curmob->GetRace() != 216 && curmob->GetRace() != 472 /* dont attack horses */ && curmob->GetRace() != 216 && curmob->GetRace() != 472 /* dont attack horses */
&& (DistanceSquared(curmob->GetPosition(), attacker->GetPosition()) <= dist2) && (DistanceSquared(curmob->GetPosition(), attacker->GetPosition()) <= dist2)
) { ) {
attacker->Attack(curmob, Hand, false, false, IsFromSpell); if (!attacker->IsClient() || attacker->GetClass() == MONK || attacker->GetClass() == RANGER)
attacker->Attack(curmob, Hand, false, false, IsFromSpell);
else
attacker->CastToClient()->DoAttackRounds(curmob, Hand, IsFromSpell);
hit++; hit++;
if (count != 0 && hit >= count) if (count != 0 && hit >= count)
return; return;

View File

@ -971,6 +971,7 @@ public:
void CalcAABonuses(StatBonuses* newbon); void CalcAABonuses(StatBonuses* newbon);
void ApplyAABonuses(const AA::Rank &rank, StatBonuses* newbon); void ApplyAABonuses(const AA::Rank &rank, StatBonuses* newbon);
void GrantAlternateAdvancementAbility(int aa_id, int points); void GrantAlternateAdvancementAbility(int aa_id, int points);
bool CheckAATimer(int timer);
protected: protected:
void CommonDamage(Mob* other, int32 &damage, const uint16 spell_id, const SkillUseTypes attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic); void CommonDamage(Mob* other, int32 &damage, const uint16 spell_id, const SkillUseTypes attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic);

View File

@ -638,42 +638,23 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
snprintf(effect_desc, _EDLEN, "Group Fear Immunity"); snprintf(effect_desc, _EDLEN, "Group Fear Immunity");
#endif #endif
//Added client messages to give some indication this effect is active. //Added client messages to give some indication this effect is active.
uint32 group_id_caster = 0; // Is there a message generated? Too disgusted by raids.
uint32 time = spell.base[i]*10; uint32 time = spell.base[i] * 10 * 1000;
if(caster->IsClient()) if (caster->IsClient()) {
{ if (caster->IsGrouped()) {
if(caster->IsGrouped()) auto group = caster->GetGroup();
{ for (int i = 0; i < 6; ++i)
group_id_caster = GetGroup()->GetID(); if (group->members[i])
} group->members[i]->aa_timers[aaTimerWarcry].Start(time);
else if(caster->IsRaidGrouped()) } else if (caster->IsRaidGrouped()) {
{ auto raid = caster->GetRaid();
group_id_caster = (GetRaid()->GetGroup(CastToClient()) == 0xFFFF) ? 0 : (GetRaid()->GetGroup(CastToClient()) + 1); uint32 gid = raid->GetGroup(caster->CastToClient());
if (gid < 12)
for (int i = 0; i < MAX_RAID_MEMBERS; ++i)
if (raid->members[i].member && raid->members[i].GroupNumber == gid)
raid->members[i].member->aa_timers[aaTimerWarcry].Start(time);
} }
} }
//old aa
//if(group_id_caster){
// Group *g = entity_list.GetGroupByID(group_id_caster);
// uint32 time = spell.base[i]*10;
// if(g){
// for(int gi=0; gi < 6; gi++){
// if(g->members[gi] && g->members[gi]->IsClient())
// {
// g->members[gi]->CastToClient()->EnableAAEffect(aaEffectWarcry , time);
// if (g->members[gi]->GetID() != caster->GetID())
// g->members[gi]->Message(13, "You hear the war cry.");
// else
// Message(13, "You let loose a fierce war cry.");
// }
// }
// }
//}
//
//else{
// CastToClient()->EnableAAEffect(aaEffectWarcry , time);
// Message(13, "You let loose a fierce war cry.");
//}
break; break;
} }
@ -2238,9 +2219,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
#ifdef SPELL_EFFECT_SPAM #ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Duration Rampage"); snprintf(effect_desc, _EDLEN, "Duration Rampage");
#endif #endif
//if (caster && caster->IsClient()) { // will tidy this up later so that NPCs can duration ramp from spells too aa_timers[aaTimerRampage].Start(effect_value * 10 * 1000); // Live bug, was suppose to be 1 second per value
// CastToClient()->DurationRampage(effect_value*12);
//}
break; break;
} }

View File

@ -2280,7 +2280,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
if(rank && rank->base_ability) { if(rank && rank->base_ability) {
ExpendAlternateAdvancementCharge(rank->base_ability->id); ExpendAlternateAdvancementCharge(rank->base_ability->id);
} }
} }
else if(spell_id == casting_spell_id && casting_spell_timer != 0xFFFFFFFF) else if(spell_id == casting_spell_id && casting_spell_timer != 0xFFFFFFFF)
{ {
//aa new todo: aa expendable charges here //aa new todo: aa expendable charges here
@ -4124,13 +4124,13 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
} }
return true; return true;
} }
//else if (IsClient() && CastToClient()->CheckAAEffect(aaEffectWarcry)) //old aa else if (CheckAATimer(aaTimerWarcry))
//{ {
// Message(13, "Your are immune to fear."); Message(13, "Your are immune to fear.");
// Log.Out(Logs::Detail, Logs::Spells, "Clients has WarCry effect, immune to fear!"); Log.Out(Logs::Detail, Logs::Spells, "Clients has WarCry effect, immune to fear!");
// caster->Message_StringID(MT_Shout, IMMUNE_FEAR); caster->Message_StringID(MT_Shout, IMMUNE_FEAR);
// return true; return true;
//} }
} }
if(IsCharmSpell(spell_id)) if(IsCharmSpell(spell_id))