diff --git a/changelog.txt b/changelog.txt index b58111ee8..07c89cf11 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,13 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 04/01/2013 == +demonstar55: AA reuse timers now start when you hit the button and are reset upon failure +demonstar55: Instant Cast bard AAs can now be used while singing a song + +== 03/30/2013 == +demonstar55: Fixed most of the pet talking, all use StringIDs now. Pet now informs you when it taunts. + == 03/23/2013 == demonstar55: Fix issues with escape not always working and fixed SE_FadingMemories to have the message since the message isn't part of the spell data. Escape now uses just the spell and not the AA Actoin diff --git a/zone/AA.cpp b/zone/AA.cpp index fdf5d6a95..05ffbb862 100644 --- a/zone/AA.cpp +++ b/zone/AA.cpp @@ -257,7 +257,7 @@ void Client::ActivateAA(aaID activate){ if(caa->action != aaActionNone) { if(caa->mana_cost > 0) { if(GetMana() < caa->mana_cost) { - Message(0, "Not enough mana to use this skill."); + Message_StringID(13, INSUFFICIENT_MANA); return; } SetMana(GetMana() - caa->mana_cost); @@ -271,8 +271,7 @@ void Client::ActivateAA(aaID activate){ p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime); } p_timers.Start(AATimerID + pTimerAAStart, timer_base); - time_t timestamp = time(NULL); - SendAATimer(AATimerID, static_cast(timestamp), static_cast(timestamp)); + SendAATimer(AATimerID, 0, 0); } } @@ -282,13 +281,30 @@ void Client::ActivateAA(aaID activate){ if(caa->reuse_time > 0) { uint32 timer_base = CalcAAReuseTimer(caa); + SendAATimer(AATimerID, 0, 0); + p_timers.Start(AATimerID + pTimerAAStart, timer_base); if(activate == aaImprovedHarmTouch || activate == aaLeechTouch) { p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime); } - - if(!CastSpell(caa->spell_id, target_id, 10, -1, -1, 0, -1, AATimerID + pTimerAAStart, timer_base, 1)) - return; + // Bards can cast instant cast AAs while they are casting another song + if (spells[caa->spell_id].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) { + if(!SpellFinished(caa->spell_id, entity_list.GetMob(target_id), 10, -1, -1, spells[caa->spell_id].ResistDiff, false)) { + //Reset on failed cast + SendAATimer(AATimerID, 0, 0xFFFFFF); + Message_StringID(15,ABILITY_FAILED); + p_timers.Clear(&database, AATimerID + pTimerAAStart); + return; + } + } else { + if(!CastSpell(caa->spell_id, target_id, 10, -1, -1, 0, -1, AATimerID + pTimerAAStart, timer_base, 1)) { + //Reset on failed cast + SendAATimer(AATimerID, 0, 0xFFFFFF); + Message_StringID(15,ABILITY_FAILED); + p_timers.Clear(&database, AATimerID + pTimerAAStart); + return; + } + } } else { diff --git a/zone/StringIDs.h b/zone/StringIDs.h index d95245b62..6c44203b1 100644 --- a/zone/StringIDs.h +++ b/zone/StringIDs.h @@ -10,6 +10,7 @@ #define SPELL_DOES_NOT_WORK_PLANE 107 //This spell does not work on this plane. #define CANT_SEE_TARGET 108 //You cannot see your target. #define MGB_STRING 113 //The next group buff you cast will hit all targets in range. +#define ABILITY_FAILED 116 //Your ability failed. Timer has been reset. #define ESCAPE 114 //You escape from combat, hiding yourself from view. #define TARGET_TOO_FAR 124 //Your target is too far away, get closer! #define PROC_TOOLOW 126 //Your will is not sufficient to command this weapon. @@ -125,6 +126,7 @@ #define OTHER_HIT_NONMELEE 434 //%1 was hit by non-melee for %2 points of damage. #define SPELL_WORN_OFF_OF 436 //Your %1 spell has worn off of %2. #define SPELL_WORN_OFF 437 //Your %1 spell has worn off. +#define PET_TAUNTING 438 //Taunting attacker, Master. #define INTERRUPT_SPELL 439 //Your spell is interrupted. #define LOSE_LEVEL 442 //You LOST a level! You are now level %1! #define GAIN_ABILITY_POINT 446 //You have gained an ability point! You now have %1 ability point%2. @@ -137,6 +139,8 @@ #define FACTION_BEST 471 //Your faction standing with %1 could not possibly get any better. #define FACTION_BETTER 472 //Your faction standing with %1 got better. #define PET_REPORT_HP 488 //I have %1 percent of my hit points left. +#define PET_NO_TAUNT 489 //No longer taunting attackers, Master. +#define PET_DO_TAUNT 490 //Taunting attackers as normal, Master. #define CORPSE_DECAY1 495 //This corpse will decay in %1 minute(s) %2 seconds. #define DISC_LEVEL_ERROR 503 //You must be a level %1 ... to use this discipline. #define DISCIPLINE_CANUSEIN 504 //You can use a new discipline in %1 minutes %2 seconds. @@ -176,6 +180,8 @@ #define PET_GETLOST_STRING 1135 //As you wish, oh great one. #define PET_LEADERIS 1136 //My leader is %3. #define I_FOLLOW_NOONE 1137 //I follow no one. +#define PET_ON_HOLD 1138 //Waiting for your order to attack, Master. +#define NOT_LEGAL_TARGET 1139 //I beg forgiveness, Master. That is not a legal target. #define MERCHANT_BUSY 1143 //I'm sorry, I am busy right now. #define MERCHANT_GREETING 1144 //Welcome to my shop, %3. #define MERCHANT_HANDY_ITEM1 1145 //Hello there, %3. How about a nice %4? @@ -273,6 +279,10 @@ #define STRIKETHROUGH_STRING 9078 //You strike through your opponent's defenses! #define SPELL_REFLECT 9082 //%1's spell has been reflected by %2. #define NEW_SPELLS_AVAIL 9149 //You have new spells available to you. Check the merchants near your guild master. +#define PET_NOW_FOCUSING 9254 //Focusing on one target, Master. +#define PET_NOT_FOCUSING 9263 //No longer focusing on one target, Master. +#define PET_NOT_CASTING 9264 //Not casting spells, Master. +#define PET_CASTING 9291 //Casting spells normally, Master. #define AE_RAMPAGE 11015 //%1 goes on a WILD RAMPAGE! #define FACE_ACCEPTED 12028 //Facial features accepted. #define SPELL_LEVEL_TO_LOW 12048 //You will have to achieve level %1 before you can scribe the %2. diff --git a/zone/attack.cpp b/zone/attack.cpp index 117254df3..1bbb6efa8 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1714,8 +1714,10 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool //Check that we can attack before we calc heading and face our target if (!IsAttackAllowed(other)) { if (this->GetOwnerID()) - entity_list.MessageClose(this, 1, 200, 10, "%s says, 'That is not a legal target master.'", this->GetCleanName()); + this->Say_StringID(NOT_LEGAL_TARGET); if(other) { + if (other->IsClient()) + other->CastToClient()->RemoveXTarget(this, false); RemoveFromHateList(other); mlog(COMBAT__ATTACKS, "I am not allowed to attack %s", other->GetName()); } @@ -4339,4 +4341,4 @@ int32 Mob::RuneAbsorb(int32 damage, uint16 type) } return damage; } -} \ No newline at end of file +} diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index bc74cc148..52d35dd71 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -6459,15 +6459,6 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) } } - // Remove the merc from the old group - if (GetMerc()) - { - if(GetMerc()->GetGroup()) - { - Merc::RemoveMercFromGroup(GetMerc(), GetMerc()->GetGroup()); - } - } - Group* group = entity_list.GetGroupByClient(inviter->CastToClient()); if(!group){ @@ -6539,8 +6530,9 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) // Add the merc back into the new group if (GetMerc()) { - if (GetMerc()->AddMercToGroup(GetMerc(), group)) { + if (Merc::AddMercToGroup(GetMerc(), group)) { database.SetGroupID(GetMerc()->GetName(), group->GetID(), inviter->CastToClient()->CharacterID(), true); + database.RefreshGroupFromDB(this); } } @@ -6667,8 +6659,6 @@ void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app) Merc* memberMerc = memberToDisband->CastToClient()->GetMerc(); if(memberClient && memberMerc && group) { - Merc::RemoveMercFromGroup(memberMerc, group); - if(!memberMerc->IsGrouped() && !memberClient->IsGrouped()) { Group *g = new Group(memberClient); @@ -6713,13 +6703,18 @@ void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app) return; } - if(GetMerc()->AddMercToGroup(GetMerc(), g)) { + if(Merc::AddMercToGroup(GetMerc(), g)) { database.SetGroupLeaderName(g->GetID(), this->GetName()); g->SaveGroupLeaderAA(); database.SetGroupID(this->GetName(), g->GetID(), this->CharacterID()); database.SetGroupID(GetMerc()->GetName(), g->GetID(), this->CharacterID(), true); database.RefreshGroupFromDB(this); } + else + { + if(GetMerc()) + GetMerc()->Depop(); + } } } } @@ -7082,7 +7077,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) } zone->AddAggroMob(); mypet->AddToHateList(GetTarget(), 1); - Message_StringID(10, PET_ATTACKING, mypet->GetCleanName(), GetTarget()->GetCleanName()); + Message_StringID(MT_PetResponse, PET_ATTACKING, mypet->GetCleanName(), GetTarget()->GetCleanName()); } } break; @@ -7098,7 +7093,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) break; } case PET_HEALTHREPORT: { - Message_StringID(10, PET_REPORT_HP, ConvertArrayF(mypet->GetHPRatio(), val1)); + Message_StringID(MT_PetResponse, PET_REPORT_HP, ConvertArrayF(mypet->GetHPRatio(), val1)); mypet->ShowBuffList(this); //Message(10,"%s tells you, 'I have %d percent of my hit points left.'",mypet->GetName(),(uint8)mypet->GetHPRatio()); break; @@ -7155,14 +7150,14 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) } case PET_TAUNT: { if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { - Message(0,"%s says, 'Now taunting foes, Master!",mypet->GetCleanName()); + Message_StringID(MT_PetResponse, PET_DO_TAUNT); mypet->CastToNPC()->SetTaunting(true); } break; } case PET_NOTAUNT: { if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { - Message(0,"%s says, 'No longer taunting foes, Master!",mypet->GetCleanName()); + Message_StringID(MT_PetResponse, PET_NO_TAUNT); mypet->CastToNPC()->SetTaunting(false); } break; @@ -7219,7 +7214,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF - mypet->Say("I will hold until given an order, master."); + mypet->Say_StringID(PET_ON_HOLD); mypet->WipeHateList(); mypet->SetHeld(true); } @@ -7230,10 +7225,10 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if (mypet->IsFeared()) break; if (mypet->IsNoCast()) { - Message(0,"%s says, 'I will now cast spells, Master!",mypet->GetCleanName()); + Message_StringID(MT_PetResponse, PET_CASTING); mypet->CastToNPC()->SetNoCast(false); } else { - Message(0,"%s says, 'I will no longer cast spells, Master!",mypet->GetCleanName()); + Message_StringID(MT_PetResponse, PET_NOT_CASTING); mypet->CastToNPC()->SetNoCast(true); } } @@ -7244,10 +7239,10 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if (mypet->IsFeared()) break; if (mypet->IsFocused()) { - Message(0,"%s says, 'I am no longer focused, Master!",mypet->GetCleanName()); + Message_StringID(MT_PetResponse, PET_NOT_FOCUSING); mypet->CastToNPC()->SetFocused(false); } else { - Message(0,"%s says, 'I will now focus my attention, Master!",mypet->GetCleanName()); + Message_StringID(MT_PetResponse, PET_NOW_FOCUSING); mypet->CastToNPC()->SetFocused(true); } } @@ -7257,10 +7252,8 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if(GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { if (mypet->IsFeared()) break; - if (mypet->IsFocused()) { - Message(0,"%s says, 'I am already focused, Master!",mypet->GetCleanName()); - } else { - Message(0,"%s says, 'I will now focus my attention, Master!",mypet->GetCleanName()); + if (!mypet->IsFocused()) { + Message_StringID(MT_PetResponse, PET_NOW_FOCUSING); mypet->CastToNPC()->SetFocused(true); } } @@ -7271,10 +7264,8 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if (mypet->IsFeared()) break; if (mypet->IsFocused()) { - Message(0,"%s says, 'I am no longer focused, Master!",mypet->GetCleanName()); + Message_StringID(MT_PetResponse, PET_NOT_FOCUSING); mypet->CastToNPC()->SetFocused(false); - } else { - Message(0,"%s says, 'I am already not focused, Master!",mypet->GetCleanName()); } } break; diff --git a/zone/merc.cpp b/zone/merc.cpp index cffe21e02..cdb63beac 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -50,7 +50,6 @@ Merc::Merc(const NPCType* d, float x, float y, float z, float heading) _lost_confidence = false; _hatedCount = 0; - ourNPCData = d; memset(equipment, 0, sizeof(equipment)); SetMercID(0); @@ -74,7 +73,6 @@ Merc::Merc(const NPCType* d, float x, float y, float z, float heading) Merc::~Merc() { AI_Stop(); - safe_delete(ourNPCData); //Since mercs are dynamically alloc'd we should probably safe_delete the data they were made from. I'm not entirely sure this is safe to delete a const. entity_list.RemoveMerc(this->GetID()); UninitializeBuffSlots(); } @@ -1915,9 +1913,9 @@ void Merc::AI_Start(int32 iMoveDelay) { AIautocastspell_timer->Start(RandomTimer(0, 2000), false); } - if (ourNPCData) { + if (NPCTypedata_ours) { //AI_AddNPCSpells(ourNPCData->npc_spells_id); - NPCSpecialAttacks(ourNPCData->npc_attacks,0); + NPCSpecialAttacks(NPCTypedata_ours->npc_attacks,0); } SendTo(GetX(), GetY(), GetZ()); @@ -5836,16 +5834,15 @@ bool Merc::AddMercToGroup(Merc* merc, Group* group) { if(merc->HasGroup()) { Merc::RemoveMercFromGroup(merc, merc->GetGroup()); } - // Add merc to this group - if(group->AddMember(merc)) { - merc->SetFollowID(merc->GetMercOwner()->GetID()); - Result = true; + //Try and add the member, followed by checking if the merc owner exists. + if(group->AddMember(merc) && merc->GetMercOwner() != NULL) { + merc->SetFollowID(merc->GetMercOwner()->GetID()); + Result = true; } - else - { + else { + //Suspend it if the member is not added and the merc's owner is not valid. merc->Suspend(); } - } return Result; diff --git a/zone/merc.h b/zone/merc.h index b76bb566d..dadba3fc1 100644 --- a/zone/merc.h +++ b/zone/merc.h @@ -379,8 +379,6 @@ private: bool _lost_confidence; int _hatedCount; uint32 owner_char_id; - const NPCType* ourNPCData; - Timer endupkeep_timer; Timer rest_timer; Timer confidence_timer; diff --git a/zone/perlparser.cpp b/zone/perlparser.cpp index 148e50154..871551b10 100644 --- a/zone/perlparser.cpp +++ b/zone/perlparser.cpp @@ -158,6 +158,14 @@ void PerlXSParser::SendCommands(const char * pkgprefix, const char *event, uint3 //now call the requested sub perl->dosub(std::string(pkgprefix).append("::").append(event).c_str()); +#ifdef EMBPERL_XS_CLASSES + std::string eval_str = (std::string)"$" + (std::string)pkgprefix + (std::string)"::client = undef;"; + eval_str += (std::string)"$" + (std::string)pkgprefix + (std::string)"::npc = undef;"; + eval_str += (std::string)"$" + (std::string)pkgprefix + (std::string)"::questitem = undef;"; + eval_str += (std::string)"$" + (std::string)pkgprefix + (std::string)"::entity_list = undef;"; + perl->eval(eval_str.c_str()); +#endif + } catch(const char * err) { //try to reduce some of the console spam... diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 8d0a33ca0..1560e5592 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -1397,6 +1397,7 @@ void NPC::DoClassAttacks(Mob *target) { //general stuff, for all classes.... //only gets used when their primary ability get used too if (taunting && HasOwner() && target->IsNPC() && target->GetBodyType() != BT_Undead && taunt_time) { + this->GetOwner()->Message_StringID(MT_PetResponse, PET_TAUNTING); Taunt(target->CastToNPC(), false); } diff --git a/zone/spells.cpp b/zone/spells.cpp index 5c85b4445..6ff8bb3b8 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -707,23 +707,26 @@ void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid) CastToNPC()->AI_Event_SpellCastFinished(false, casting_spell_slot); } - if(casting_spell_type == 1 && IsClient()) //Rest AA Timer on failed cast - CastToClient()->GetPTimers().Clear(&database, casting_spell_timer); - + if(casting_spell_type == 1 && IsClient()) { //Rest AA Timer on failed cast + CastToClient()->SendAATimer(casting_spell_timer - pTimerAAStart, 0, 0xFFFFFF); + CastToClient()->Message_StringID(15,ABILITY_FAILED); + CastToClient()->GetPTimers().Clear(&database, casting_spell_timer); + } + ZeroCastingVars(); // resets all the state keeping stuff - + mlog(SPELLS__CASTING, "Spell %d has been interrupted.", spellid); - + if(!spellid) return; - + if (bardsong || IsBardSong(casting_spell_id)) _StopSong(); if(bard_song_mode) { return; } - + if(!message) message = IsBardSong(spellid) ? SONG_ENDS_ABRUPTLY : INTERRUPT_SPELL; @@ -1971,11 +1974,6 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 { CastToClient()->GetPTimers().Start(casting_spell_timer, casting_spell_timer_duration); mlog(SPELLS__CASTING, "Spell %d: Setting custom reuse timer %d to %d", spell_id, casting_spell_timer, casting_spell_timer_duration); - if(casting_spell_type == 1) //AA - { - time_t timestamp = time(NULL); - CastToClient()->SendAATimer((casting_spell_timer - pTimerAAStart), timestamp, timestamp); - } } else if(spells[spell_id].recast_time > 1000) { int recast = spells[spell_id].recast_time/1000;