diff --git a/changelog.txt b/changelog.txt index fde7ca6b2..4e5866474 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,12 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 05/17/2014 == +Secrets: Identified the opcode/struct for guild ranks in Rain of Fear+ clients. +Secrets: Implemented a work-around for Rain of Fear clients to have all guild permissions until proper database support is added for newer ranks. + +== 05/14/2014 == +Kayen: Rooted NPC's will no longer target players with Divine Aura effect if they are the closest target and other targets exist on the hate list. + == 05/12/2014 == Uleat: Re-arranged functions in Item.cpp to somewhat match the order they are declared in Item.h. Should make finding their location a little easier when using declarations as a guide. (This will facilitate readability of an upcoming change...) diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index e0c4a1563..674ed15b7 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -3163,6 +3163,26 @@ struct GuildUpdateURLAndChannel_Struct /*4176*/ }; +//Case 5 in Rain of Fear and higher clients for guild permissions. +//RankID is the internal guild rank. There are 8 in Rain of Fear as opposed to the 3 in Titanium. +//PermissionID is the type of permission. There are 32 total, with some unused. Live May 2014 sends and uses 26 of them. Varies through client version. +//Permission value is a char that is either 0 or 1. Enabled for that rank/disabled for that rank. +//The client sends this struct on changing a guild rank. The server sends each rank in 32 or less packets upon zonein if you are in a guild. +struct GuildUpdateRanks_Struct +{ +/*0000*/ uint32 Action; // 0 = Update URL, 1 = Update Channel, 5 = RoF Ranks +/*0004*/ uint32 Unknown0004; //Seen 00 00 00 00 +/*0008*/ uint32 Unknown0008; //Seen 96 29 00 00 +/*0008*/ char Unknown0012[64]; //Seen "CharacterName" +/*0076*/ uint32 GuildID; //Guild ID of "CharacterName" +/*0080*/ uint32 RankID; +/*0084*/ uint32 PermissionID; +/*0088*/ char PermissionVal; +/*0089*/ char Unknown0089[3]; //Seen 2c 01 00 ? +/*0092*/ +}; + + struct GuildStatus_Struct { /*000*/ char Name[64]; diff --git a/utils/sql/git/optional/2014_04_23_FocusComabtProcs.sql b/utils/sql/git/optional/2014_04_23_FocusComabtProcs.sql new file mode 100644 index 000000000..76f9c157a --- /dev/null +++ b/utils/sql/git/optional/2014_04_23_FocusComabtProcs.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:FocusCombatProcs', 'false', 'Allow all combat procs to receive focus (ignores focus limit).'); diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index d0e25ca3c..96a99e56e 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -2149,28 +2149,40 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_LimitHPPercent: { - if(newbon->HPPercCap != 0 && newbon->HPPercCap > effect_value) - newbon->HPPercCap = effect_value; - else if(newbon->HPPercCap == 0) - newbon->HPPercCap = effect_value; - + if(newbon->HPPercCap[0] != 0 && newbon->HPPercCap[0] > effect_value){ + newbon->HPPercCap[0] = effect_value; + newbon->HPPercCap[1] = base2; + } + else if(newbon->HPPercCap[0] == 0){ + newbon->HPPercCap[0] = effect_value; + newbon->HPPercCap[1] = base2; + } break; } case SE_LimitManaPercent: { - if(newbon->ManaPercCap != 0 && newbon->ManaPercCap > effect_value) - newbon->ManaPercCap = effect_value; - else if(newbon->ManaPercCap == 0) - newbon->ManaPercCap = effect_value; + if(newbon->ManaPercCap[0] != 0 && newbon->ManaPercCap[0] > effect_value){ + newbon->ManaPercCap[0] = effect_value; + newbon->ManaPercCap[1] = base2; + } + else if(newbon->ManaPercCap[0] == 0) { + newbon->ManaPercCap[0] = effect_value; + newbon->ManaPercCap[1] = base2; + } break; } case SE_LimitEndPercent: { - if(newbon->EndPercCap != 0 && newbon->EndPercCap > effect_value) - newbon->EndPercCap = effect_value; - else if(newbon->EndPercCap == 0) - newbon->EndPercCap = effect_value; + if(newbon->EndPercCap[0] != 0 && newbon->EndPercCap[0] > effect_value) { + newbon->EndPercCap[0] = effect_value; + newbon->EndPercCap[1] = base2; + } + + else if(newbon->EndPercCap[0] == 0){ + newbon->EndPercCap[0] = effect_value; + newbon->EndPercCap[1] = base2; + } break; } diff --git a/zone/bot.cpp b/zone/bot.cpp index 946d3b68b..1bf4dedc2 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -10649,10 +10649,10 @@ int32 Bot::CalcMaxHP() { if (cur_hp > max_hp) cur_hp = max_hp; - int hp_perc_cap = spellbonuses.HPPercCap; + int hp_perc_cap = spellbonuses.HPPercCap[0]; if(hp_perc_cap) { int curHP_cap = (max_hp * hp_perc_cap) / 100; - if (cur_hp > curHP_cap) + if (cur_hp > curHP_cap || (spellbonuses.HPPercCap[1] && cur_hp > spellbonuses.HPPercCap[1])) cur_hp = curHP_cap; } @@ -10671,10 +10671,10 @@ int32 Bot::CalcMaxEndurance() cur_end = max_end; } - int end_perc_cap = spellbonuses.EndPercCap; + int end_perc_cap = spellbonuses.EndPercCap[0]; if(end_perc_cap) { int curEnd_cap = (max_end * end_perc_cap) / 100; - if (cur_end > curEnd_cap) + if (cur_end > curEnd_cap || (spellbonuses.EndPercCap[1] && cur_end > spellbonuses.EndPercCap[1])) cur_end = curEnd_cap; } diff --git a/zone/client.h b/zone/client.h index a97671c20..9e2503df2 100644 --- a/zone/client.h +++ b/zone/client.h @@ -621,6 +621,7 @@ public: void SendGuildURL(); void SendGuildChannel(); void SendGuildSpawnAppearance(); + void SendGuildRanks(); void SendGuildMembers(); void SendGuildList(); void SendGuildJoin(GuildJoin_Struct* gj); diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index 4093f4af4..981c916f6 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -255,10 +255,10 @@ int32 Client::CalcMaxHP() { if (cur_hp > max_hp) cur_hp = max_hp; - int hp_perc_cap = spellbonuses.HPPercCap; + int hp_perc_cap = spellbonuses.HPPercCap[0]; if(hp_perc_cap) { int curHP_cap = (max_hp * hp_perc_cap) / 100; - if (cur_hp > curHP_cap) + if (cur_hp > curHP_cap || (spellbonuses.HPPercCap[1] && cur_hp > spellbonuses.HPPercCap[1])) cur_hp = curHP_cap; } @@ -950,10 +950,10 @@ int32 Client::CalcMaxMana() cur_mana = max_mana; } - int mana_perc_cap = spellbonuses.ManaPercCap; + int mana_perc_cap = spellbonuses.ManaPercCap[0]; if(mana_perc_cap) { int curMana_cap = (max_mana * mana_perc_cap) / 100; - if (cur_mana > curMana_cap) + if (cur_mana > curMana_cap || (spellbonuses.ManaPercCap[1] && cur_mana > spellbonuses.ManaPercCap[1])) cur_mana = curMana_cap; } @@ -1890,10 +1890,10 @@ void Client::CalcMaxEndurance() cur_end = max_end; } - int end_perc_cap = spellbonuses.EndPercCap; + int end_perc_cap = spellbonuses.EndPercCap[0]; if(end_perc_cap) { int curEnd_cap = (max_end * end_perc_cap) / 100; - if (cur_end > curEnd_cap) + if (cur_end > curEnd_cap || (spellbonuses.EndPercCap[1] && cur_end > spellbonuses.EndPercCap[1])) cur_end = curEnd_cap; } } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 4a995dd5e..a0e93ef50 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1267,7 +1267,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) PlayerPositionUpdateServer_Struct* ppu = (PlayerPositionUpdateServer_Struct*)outapp->pBuffer; MakeSpawnUpdate(ppu); if (gmhideme) - entity_list.QueueClientsStatus(this,outapp,true,(uint8)Admin(),250); + entity_list.QueueClientsStatus(this,outapp,true,Admin(),250); else entity_list.QueueCloseClients(this,outapp,true,300,nullptr,false); safe_delete(outapp); @@ -3416,8 +3416,8 @@ void Client::Handle_OP_Sneak(const EQApplicationPacket *app) else { CheckIncreaseSkill(SkillSneak, nullptr, 5); } - float hidechance = ((GetSkill(SkillSneak)/300.0f) + .25f) * 1000.f; - float random = (float)MakeRandomFloat(0, 99); + float hidechance = ((GetSkill(SkillSneak)/300.0f) + .25) * 100; + float random = MakeRandomFloat(0, 99); if(!was && random < hidechance) { sneaking = true; } @@ -3458,8 +3458,8 @@ void Client::Handle_OP_Hide(const EQApplicationPacket *app) int reuse = HideReuseTime - GetAA(209); p_timers.Start(pTimerHide, reuse-1); - float hidechance = ((GetSkill(SkillHide)/250.0f) + .25f) * 100.0f; - float random = (float)MakeRandomFloat(0, 100); + float hidechance = ((GetSkill(SkillHide)/250.0f) + .25) * 100; + float random = MakeRandomFloat(0, 100); CheckIncreaseSkill(SkillHide, nullptr, 5); if (random < hidechance) { EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); @@ -4010,7 +4010,7 @@ void Client::Handle_OP_GuildManageBanker(const EQApplicationPacket *app) bool NewBankerStatus = gmb->enabled & 0x01; - bool NewAltStatus = (gmb->enabled & 0x02) != 0; + bool NewAltStatus = gmb->enabled & 0x02; if((IsCurrentlyABanker != NewBankerStatus) && !guild_mgr.IsGuildLeader(GuildID(), CharacterID())) { @@ -4668,7 +4668,7 @@ void Client::Handle_OP_DeleteItem(const EQApplicationPacket *app) if(GetClientVersion() < EQClientSoD) IntoxicationIncrease = (200 - AlcoholTolerance) * 30 / 200 + 10; else - IntoxicationIncrease = (int16)((270 - AlcoholTolerance) * 0.111111108 + 10.0); + IntoxicationIncrease = (270 - AlcoholTolerance) * 0.111111108 + 10; if(IntoxicationIncrease < 0) IntoxicationIncrease = 1; @@ -5242,7 +5242,7 @@ void Client::Handle_OP_LFGCommand(const EQApplicationPacket *app) } LFGFromLevel = lfg->FromLevel; LFGToLevel = lfg->ToLevel; - LFGMatchFilter = lfg->MatchFilter != 0; + LFGMatchFilter = lfg->MatchFilter; strcpy(LFGComments, lfg->Comments); break; default: @@ -5573,8 +5573,8 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) } if(tmpmer_used && (mp->quantity > prevcharges || item->MaxCharges > 1)) { - if((int32)prevcharges > item->MaxCharges && item->MaxCharges > 1) - mp->quantity =(uint8)item->MaxCharges; + if(prevcharges > item->MaxCharges && item->MaxCharges > 1) + mp->quantity = item->MaxCharges; else mp->quantity = prevcharges; } @@ -5597,9 +5597,9 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) int SinglePrice = 0; if (RuleB(Merchant, UsePriceMod)) - SinglePrice = (int)(item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate * Client::CalcPriceMod(tmp, false)); + SinglePrice = (item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate * Client::CalcPriceMod(tmp, false)); else - SinglePrice = (int)(item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate); + SinglePrice = (item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate); if(item->MaxCharges > 1) mpo->price = SinglePrice; @@ -5801,10 +5801,10 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app) if(charges > 0 && (freeslot = zone->SaveTempItem(vendor->CastToNPC()->MerchantType, vendor->GetNPCTypeID(),itemid,charges,true)) > 0){ ItemInst* inst2 = inst->Clone(); if (RuleB(Merchant, UsePriceMod)){ - inst2->SetPrice((uint32)(item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate*Client::CalcPriceMod(vendor,false))); + inst2->SetPrice(item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate*Client::CalcPriceMod(vendor,false)); } else - inst2->SetPrice((uint32)(item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate)); + inst2->SetPrice(item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate); inst2->SetMerchantSlot(freeslot); uint32 MerchantQuantity = zone->GetTempMerchantQuantity(vendor->GetNPCTypeID(), freeslot); @@ -7970,10 +7970,10 @@ void Client::Handle_OP_GMFind(const EQApplicationPacket *app) Mob* gt = entity_list.GetMob(request->charname); if (gt != 0) { foundplayer->success=1; - foundplayer->x=gt->GetX(); - foundplayer->y=gt->GetY(); + foundplayer->x=(int32)gt->GetX(); + foundplayer->y=(int32)gt->GetY(); - foundplayer->z=gt->GetZ(); + foundplayer->z=(int32)gt->GetZ(); foundplayer->zoneID=zone->GetZoneID(); } //Send the packet... @@ -8979,7 +8979,7 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) { database.LoadBuffs(this); uint32 max_slots = GetMaxBuffSlots(); - for(uint32 i = 0; i < max_slots; i++) { + for(int i = 0; i < max_slots; i++) { if(buffs[i].spellid != SPELL_UNKNOWN) { m_pp.buffs[i].spellid = buffs[i].spellid; m_pp.buffs[i].bard_modifier = 10; @@ -9134,7 +9134,7 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) { m_pp.timeentitledonaccount = database.GetTotalTimeEntitledOnAccount(AccountID()) / 1440; - if(m_pp.RestTimer > (uint32)RuleI(Character, RestRegenTimeToActivate)) + if(m_pp.RestTimer > RuleI(Character, RestRegenTimeToActivate)) m_pp.RestTimer = 0; //This checksum should disappear once dynamic structs are in... each struct strategy will do it @@ -9603,7 +9603,7 @@ void Client::CompleteConnect() if(IsInAGuild()) { - guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), zone->GetZoneID(), (uint32)time(nullptr)); + guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), zone->GetZoneID(), time(nullptr)); guild_mgr.RequestOnlineGuildMembers(this->CharacterID(), this->GuildID()); } @@ -9798,7 +9798,7 @@ void Client::Handle_OP_BankerChange(const EQApplicationPacket *app) cp/=10; m_pp.gold=cp%10; cp/=10; - m_pp.platinum=(int32)cp; + m_pp.platinum=cp; cp = static_cast(m_pp.copper_bank) + (static_cast(m_pp.silver_bank) * 10) + @@ -9811,7 +9811,7 @@ void Client::Handle_OP_BankerChange(const EQApplicationPacket *app) cp/=10; m_pp.gold_bank=cp%10; cp/=10; - m_pp.platinum_bank=(int32)cp; + m_pp.platinum_bank=cp; bc->copper=m_pp.copper; bc->silver=m_pp.silver; @@ -11263,7 +11263,7 @@ void Client::Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app) } else { - if((uint32)inst->GetCharges() < ams_in->charges) + if(inst->GetCharges() < ams_in->charges) { ams_in->charges = inst->GetCharges(); } @@ -11475,9 +11475,9 @@ void Client::Handle_OP_SetStartCity(const EQApplicationPacket *app) if(zoneid == StartCity) { ValidCity = true; - x = (float)atof(row[2]); - y = (float)atof(row[3]); - z = (float)atof(row[4]); + x = atof(row[2]); + y = atof(row[3]); + z = atof(row[4]); } } @@ -11649,7 +11649,7 @@ void Client::Handle_OP_GMSearchCorpse(const EQApplicationPacket *app) EscSearchString, MaxResults), errbuf, &Result)) { - int NumberOfRows = (int)mysql_num_rows(Result); + int NumberOfRows = mysql_num_rows(Result); if(NumberOfRows == MaxResults) Message(clientMessageError, "Your search found too many results; some are not displayed."); @@ -11679,14 +11679,14 @@ void Client::Handle_OP_GMSearchCorpse(const EQApplicationPacket *app) uint32 ZoneID = atoi(Row[1]); - float CorpseX = (float)atof(Row[2]); - float CorpseY = (float)atof(Row[3]); - float CorpseZ = (float)atof(Row[4]); + float CorpseX = atof(Row[2]); + float CorpseY = atof(Row[3]); + float CorpseZ = atof(Row[4]); strn0cpy(TimeOfDeath, Row[5], sizeof(TimeOfDeath)); - bool CorpseRezzed = atoi(Row[6]) != 0; - bool CorpseBuried = atoi(Row[7]) != 0; + bool CorpseRezzed = atoi(Row[6]); + bool CorpseBuried = atoi(Row[7]); sprintf(Buffer, "%s%s%8.0f%8.0f%8.0f%s%s%s", CharName, StaticGetZoneName(ZoneID), CorpseX, CorpseY, CorpseZ, TimeOfDeath, @@ -12331,7 +12331,7 @@ void Client::Handle_OP_ClearBlockedBuffs(const EQApplicationPacket *app) return; } - bool Pet = app->pBuffer[0] != 0; + bool Pet = app->pBuffer[0]; if(Pet) PetBlockedBuffs.clear(); @@ -12860,7 +12860,7 @@ void Client::Handle_OP_AltCurrencySell(const EQApplicationPacket *app) { } else { - if((uint32)inst->GetCharges() < sell->charges) + if(inst->GetCharges() < sell->charges) { sell->charges = inst->GetCharges(); } @@ -13302,7 +13302,7 @@ void Client::Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app) return; } - XTargetAutoAddHaters = app->ReadUInt8(0) != 0; + XTargetAutoAddHaters = app->ReadUInt8(0); } void Client::Handle_OP_ItemPreview(const EQApplicationPacket *app) diff --git a/zone/common.h b/zone/common.h index d720f3d43..d2737e4a5 100644 --- a/zone/common.h +++ b/zone/common.h @@ -310,9 +310,9 @@ struct StatBonuses { int16 IncreaseBlockChance; // overall block chance modifier uint16 PersistantCasting; // chance to continue casting through a stun int XPRateMod; //i - int HPPercCap; //Spell effect that limits you to being healed/regening beyond a % of your max - int ManaPercCap; // ^^ - int EndPercCap; // ^^ + int HPPercCap[2]; //Spell effect that limits you to being healed/regening beyond a % of your max + int ManaPercCap[2]; // ^^ 0 = % Cap 1 = Flat Amount Cap + int EndPercCap[2]; // ^^ bool BlockNextSpell; // Indicates whether the client can block a spell or not //uint16 BlockSpellEffect[EFFECT_COUNT]; // Prevents spells with certain effects from landing on you *no longer used bool ImmuneToFlee; // Bypass the fleeing flag diff --git a/zone/guild.cpp b/zone/guild.cpp index 66ff4946c..21e350cdf 100644 --- a/zone/guild.cpp +++ b/zone/guild.cpp @@ -115,6 +115,42 @@ void Client::SendGuildChannel() } } +void Client::SendGuildRanks() +{ + if(GetClientVersion() < EQClientRoF) + return; + + int permissions = 30 + 1; //Static number of permissions in all EQ clients as of May 2014 + int ranks = 8 + 1; // Static number of RoF+ ranks as of May 2014 + int j = 1; + int i = 1; + if(IsInAGuild()) + { + while(j < ranks) + { + while(i < permissions) + { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GuildUpdateURLAndChannel, sizeof(GuildUpdateRanks_Struct)); + GuildUpdateRanks_Struct *guuacs = (GuildUpdateRanks_Struct*) outapp->pBuffer; + //guuacs->Unknown0008 = this->GuildID(); + strncpy(guuacs->Unknown0012, this->GetCleanName(), 64); + guuacs->Action = 5; + guuacs->RankID = j; + guuacs->GuildID = this->GuildID(); + guuacs->PermissionID = i; + guuacs->PermissionVal = 1; + guuacs->Unknown0089[0] = 0x2c; + guuacs->Unknown0089[1] = 0x01; + guuacs->Unknown0089[2] = 0x00; + FastQueuePacket(&outapp); + i++; + } + j++; + i = 1; + } + } +} + void Client::SendGuildSpawnAppearance() { if (!IsInAGuild()) { // clear guildtag diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index f1bd03fa1..a70208f8e 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -163,7 +163,7 @@ Mob* HateList::GetClosest(Mob *hater) { ++iterator; } - if (close == 0 && hater->IsNPC()) + if (close == 0 && hater->IsNPC() || close->DivineAura()) close = hater->CastToNPC()->GetHateTop(); return close; diff --git a/zone/merc.cpp b/zone/merc.cpp index c4fec62c2..659b9aee3 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -917,10 +917,10 @@ int32 Merc::CalcMaxHP() { if (cur_hp > max_hp) cur_hp = max_hp; - int hp_perc_cap = spellbonuses.HPPercCap; + int hp_perc_cap = spellbonuses.HPPercCap[0]; if(hp_perc_cap) { int curHP_cap = (max_hp * hp_perc_cap) / 100; - if (cur_hp > curHP_cap) + if (cur_hp > curHP_cap || (spellbonuses.HPPercCap[1] && cur_hp > spellbonuses.HPPercCap[1])) cur_hp = curHP_cap; } @@ -959,10 +959,10 @@ int32 Merc::CalcMaxMana() cur_mana = max_mana; } - int mana_perc_cap = spellbonuses.ManaPercCap; + int mana_perc_cap = spellbonuses.ManaPercCap[0]; if(mana_perc_cap) { int curMana_cap = (max_mana * mana_perc_cap) / 100; - if (cur_mana > curMana_cap) + if (cur_mana > curMana_cap || (spellbonuses.ManaPercCap[1] && cur_mana > spellbonuses.ManaPercCap[1])) cur_mana = curMana_cap; } @@ -1054,10 +1054,10 @@ void Merc::CalcMaxEndurance() cur_end = max_end; } - int end_perc_cap = spellbonuses.EndPercCap; + int end_perc_cap = spellbonuses.EndPercCap[0]; if(end_perc_cap) { int curEnd_cap = (max_end * end_perc_cap) / 100; - if (cur_end > curEnd_cap) + if (cur_end > curEnd_cap || (spellbonuses.EndPercCap[1] && cur_end > spellbonuses.EndPercCap[1])) cur_end = curEnd_cap; } }