Index: ScriptMgr.cpp =================================================================== --- ScriptMgr.cpp (revision 136) +++ ScriptMgr.cpp (working copy) @@ -393,6 +393,7 @@ //Wailing caverns //Western plaguelands //Westfall +extern void AddSC_npc_DaphneStillwell(); //Wetlands //Winterspring //Zangramarsh @@ -782,6 +783,7 @@ //Wailing caverns //Western plaguelands //Westfall + AddSC_npc_DaphneStillwell(); //Wetlands //Winterspring //Zangramarsh Index: scripts/creature/EventAI.cpp =================================================================== --- scripts/creature/EventAI.cpp (revision 0) +++ scripts/creature/EventAI.cpp (revision 0) @@ -0,0 +1,374 @@ +/* Copyright (C) 2006,2007 ScriptDev2 +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "EventAI.h" +#include "../../../../shared/WorldPacket.h" +#include "../../../../game/GameObject.h" +#include "../../../../game/TargetedMovementGenerator.h" + +void ScriptedEvent::Talk(char *txt, uint32 Type, uint32 snd, uint8 lang) +{ + text_data = txt; data1 = snd; data2 = lang; data3 = Type ; + type = 1; +} + +void ScriptedEvent::Waypoint(float X, float Y, float Z, float O, bool run) +{ + x = X; y = Y; z = Z; o = O; data1 = run; + type = 2; +} + + +void ScriptedEvent::Emote(uint32 emo, bool State) +{ + data1 = emo; data2 = State; + type = 3; +} + +void ScriptedEvent::Summon(uint32 id, uint32 type, uint32 despawn, float X, float Y, float Z, float O) +{ + data1 = id; x = X; y = Y; z = Z; o = O; + type = 4; +} + +void ScriptedEvent::Cast(uint32 id, Object *tar, uint32 triggered) +{ + data1 = id; target = tar; data2 = triggered; + type = 5; +} + +void ScriptedEvent::RemoveAura(uint32 id) +{ + data1 = id; + type = 6; +} + +void ScriptedEvent::Equip(uint32 model, uint32 data, uint32 slot, uint32 id) +{ + if(id > 2) + return; + + data1 = model; data2 = data; data3 = slot; + x = id; //So I don't use an extra variable + type = 7; +} + +void ScriptedEvent::Fight(Unit *who, bool fight_back) +{ + target = who; + data1 = fight_back; + + type = 8; +} + +void ScriptedEvent::ExitCombat() +{ + type = 9; +} + +void ScriptedEvent::SetObjectState(Object *which, uint32 state, bool return_on_expire) +{ + if(!which || which->GetTypeId() != TYPEID_GAMEOBJECT) + return; + + target = which; data1 = state; data2 = return_on_expire; + type = 10; +} + +void ScriptedEvent::AllowMovement(bool allow) +{ + data1 = allow; + type = 11; +} + +void ScriptedEvent::SetFlag(uint32 index, uint32 flag, bool set) +{ + data1 = index; + data2 = flag; + data3 = set; + type = 12; +} + +void ScriptedEvent::ChangeFaction(uint32 faction) +{ + data1 = faction; + type = 13; +} + +void ScriptedEvent::QuestComplete(uint32 quest_id, Player *Target) +{ + target = Target; + data1 = quest_id; + type = 14; +} + +void ScriptedEvent::Ambush(uint32 ambusher_id, uint32 ambusher_count, uint32 spawn_radius, float X, float Y, float Z) +{ + data1 = ambusher_id; + data2 = ambusher_count; + data3 = spawn_radius; + + x = X;y = Y;z = Z; + + type = 15; +} + +void ScriptedEvent::Execute() +{ + started = true; + switch(type) + { + case 1: + switch(data3) + { + case 1: + user->Say(text_data, data1, NULL); + break; + case 2: + user->Yell(text_data, data1, NULL); + break; + case 3: + user->Whisper(target->GetGUID(), text_data); + break; + } + if(data1) + { + WorldPacket data(4); + data.SetOpcode(SMSG_PLAY_SOUND); + data << uint32(data1); + user->SendMessageToSet(&data,false); + } + + break; + case 2: + if(!duration) + { + float speed = user->GetSpeed(data1 ? MOVE_RUN : MOVE_WALK); + if(speed <= 0) + speed = 2.5f; + + duration = (uint32)(sqrt(user->GetDistanceSq(x,y,z))/speed * 1000); + } + + user->SendMonsterMove(x, y, z, 0, data1, duration); + + break; + case 3: + if(data2) + user->SetUInt32Value(UNIT_NPC_EMOTESTATE,data1); + else + user->HandleEmoteCommand(data1); + break; + case 4: + { + Creature *summon = user->SummonCreature(data1, x, y, z, o, (TempSummonType)data2, data3); + + break; + } + case 5: + user->CastSpell((Unit*)target, data1, data2); + + break; + case 6: + ((Unit*)user)->RemoveAurasDueToSpell(data1); + break; + case 7: + { + user->SetUInt32Value( UNIT_VIRTUAL_ITEM_SLOT_DISPLAY + (int)x, data1); + user->SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + (int)2*x, data2); + user->SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + (int)2*x + 1, data3); + + break; + } + case 8: + user->AI()->AttackStart((Unit*)target); + if(data2 && target->GetTypeId() == TYPEID_UNIT) + ((Creature*)target)->AI()->AttackStart(user); + + break; + case 9: + user->CombatStop(true); + user->AI()->EnterEvadeMode(); + + break; + case 10: + if(data2) + data3 = ((GameObject*)target)->GetUInt32Value(GAMEOBJECT_STATE); + ((GameObject*)target)->SetUInt32Value(GAMEOBJECT_STATE, data1); + break; + case 11: + if(data1) + { + if(user->getVictim()) + { + user->GetMotionMaster()->Clear(false); + user->GetMotionMaster()->Mutate(new TargetedMovementGenerator(*user->getVictim())); + } + } + else + { + user->GetMotionMaster()->Clear(false); + user->GetMotionMaster()->Idle(); + } + + break; + case 12: + if(data3) + user->SetFlag(data1, data2); + else + user->RemoveFlag(data1, data2); + + break; + case 13: + user->setFaction(data1); + break; + case 14: + ((Player*)target)->CompleteQuest(data1); + break; + case 15: + { + for(int i = 0;i < data2; i++) + { + float spwX,spwY,spwZ; + user->GetRandomPoint(x,y,z,data3,spwX,spwY,spwZ); + Creature *ambusher = user->SummonCreature(data1, spwX, spwY, spwZ, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 3000); + ambusher->AI()->AttackStart(user); + guid_data.push_back(ambusher->GetGUID()); + } + break; + } + } +} + +void ScriptedEvent::Expire() +{ + switch(type) + { + case 2: + user->Relocate(x,y,z); + if(o) + user->SetOrientation(o); + + break; + case 3: + if(data2) + user->SetUInt32Value(UNIT_NPC_EMOTESTATE,0); + + break; + case 10: + if(data2) + ((GameObject*)target)->SetUInt32Value(GAMEOBJECT_STATE, data3); + + break; + } +} + +void ScriptedEvent::Update(const uint32 diff) +{ + duration -= diff; + switch(type) + { + case 15: + { + if(guid_data.size()) + { + for(std::vector::iterator itr = guid_data.begin(); itr != guid_data.end(); ++itr) + { + Unit *ambusher = Unit::GetUnit(*user, *itr); + if(!ambusher || ambusher->isDead()) + { + guid_data.erase(itr); + break; + } + else if(!user->isInCombat() && user->GetDistance(ambusher) < 7) + user->AI()->AttackStart(ambusher); + } + } + else + duration = 0; + } + } +} + +void EventAI::UpdateAI(const uint32 diff) +{ + if(!m_event_list.empty()) + { + event_list::iterator itr = m_event_list.begin(); + + if(!*itr) //Check if something went wrong and prevent crash + { + m_event_list.erase(itr); + return; + } + + ScriptedEvent *evnt = *itr; + + if(!evnt->started) + { + OnEvent(evnt->id); + evnt->Execute(); + } + else if(evnt->duration <= diff) + { + evnt->Expire(); + OnEventExpire(evnt->id); + m_event_list.erase(itr); + delete evnt; + } + else + evnt->Update(diff); + + + if(evnt->stop_ai) + return; + } + + UpdateAI_secondary(diff); + + ScriptedAI::UpdateAI(diff); + +} + +ScriptedEvent* EventAI::NewEvent(uint32 id, uint32 duration, Creature *user, bool StopAIactions) +{ + ScriptedEvent *evnt = new ScriptedEvent(id, duration, user, StopAIactions); + m_event_list.push_back(evnt); + return evnt; +} + +void EventAI::ClearEvents(bool expire_current) +{ + if(!m_event_list.size()) + return; + + event_list::iterator itr = m_event_list.begin(); + + if(*itr && expire_current) + (*itr)->Expire(); + + for( ; itr != m_event_list.end(); ++itr) + delete *itr; + + m_event_list.clear(); +} + +ScriptedEvent* EventAI::GetCurrentEvent() +{ + if(m_event_list.size()) + return *m_event_list.begin(); + return NULL; +} \ No newline at end of file Index: scripts/creature/EventAI.h =================================================================== --- scripts/creature/EventAI.h (revision 0) +++ scripts/creature/EventAI.h (revision 0) @@ -0,0 +1,99 @@ +/* Copyright (C) 2006,2007 ScriptDev2 +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef SCRIPTED_EVENTS_H +#define SCRIPTED_EVENTS_H + +#include "../../ScriptMgr.h" + +class ScriptedEvent +{ + private: + //1 - talk, 2 - walk, 3 - emote, 4 - summon, 5 - cast, 6 - remove aura, + //7 - wield weapon, 8 - fight with , 9 - leave combat, + // 10 - set object state (a.k.a. open/close doors) + // 11 - allow/disallow movement, 12 - set/remove flag + // 13 - change faction, 14 - Complete Quest for someone + // 15 - ambush by mobs + uint8 type; + Creature *user; + Object *target; + char *text_data; + uint32 data1,data2,data3; + float x,y,z,o; + public: + ScriptedEvent(uint32 ID,uint32 dur, Creature *initiator, bool stopAI = true, bool continueEvents = false) : + id(ID), duration(dur), user(initiator), target(NULL), type(0), data1(0), + data2(0), data3(0), x(0), y(0), z(0), o(o), text_data(NULL), + started(false), stop_ai(stopAI) {} + ~ScriptedEvent() {}; + + //Duration of event in miliseconds, used for other stuff too + uint32 duration; + //Does the event prevent Normal AI actions + bool stop_ai; + //whether the event is started + bool started; + //Id is used when calling OnEventStart/Expire + uint32 id; + //GUID data, for events like ambushes + std::vector guid_data; + + uint8 const GetEventType() { return type; } + + void Talk(char *txt, uint32 Type = 1, uint32 snd = 0, uint8 lang = LANG_UNIVERSAL); + void Waypoint(float X, float Y, float Z, float O, bool run); + void Emote(uint32 emo, bool State = false); + void Summon(uint32 id, uint32 type, uint32 despawn, float X, float Y, float Z, float O); + void Cast(uint32 id, Object *tar, uint32 triggered); + void RemoveAura(uint32 id); + void Equip(uint32 model, uint32 data, uint32 slot, uint32 id); + void Fight(Unit *who, bool fight_back); + void ExitCombat(); + void SetObjectState(Object *which, uint32 state, bool return_on_expire = false); + void AllowMovement(bool allow); + void SetFlag(uint32 index, uint32 flag, bool set); + void ChangeFaction(uint32 faction); + void QuestComplete(uint32 quest_id, Player *Target); + void Ambush(uint32 ambusher_id, uint32 ambusher_count, uint32 spawn_radius, float X, float Y, float Z); + + void Execute() ; + void Expire() ; + void Update(const uint32 diff) ; +}; + +typedef std::vector event_list; + +class MANGOS_DLL_DECL EventAI : public ScriptedAI +{ + void UpdateAI(const uint32 diff); + event_list m_event_list; + +public: + + EventAI(Creature *c) : ScriptedAI(c) { EnterEvadeMode(); } + + void ClearEvents(bool expire_current = false); + ScriptedEvent* NewEvent(uint32 id, uint32 duration, Creature *user, bool StopAIactions = true); + ScriptedEvent* GetCurrentEvent(); + + virtual void UpdateAI_secondary(const uint32 diff) {} + + virtual void OnEvent(uint32 event_id) {} + virtual void OnEventExpire(uint32 event_id) {} +}; + +#endif \ No newline at end of file Index: scripts/zone/westfall/npc_daphne_stilwell.cpp =================================================================== --- scripts/zone/westfall/npc_daphne_stilwell.cpp (revision 0) +++ scripts/zone/westfall/npc_daphne_stilwell.cpp (revision 0) @@ -0,0 +1,227 @@ +/* Copyright (C) 2006,2007 ScriptDev2 +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "../../sc_defines.h" +#include "../../creature/EventAI.h" +#include "../../../../../game/Player.h" + +struct Waypoint +{ + float x,y,z,o; +}; + + +//Some waypoint definitions, for easier use after + +static Waypoint toHouse[] = +{ + { -11466.443359, 1530.258789, 50.249241, 5.255720 }, + { -11463.185547, 1525.146362, 50.937778, 5.048375 }, + { -11460.483398, 1526.947510, 50.939514, 0.570917 } +}; + +static Waypoint toBattle[] = +{ + { -11463.185547, 1525.146362, 50.937778, 3.728201 }, + { -11466.443359, 1530.258789, 50.249241, 1.886439 }, + { -11461.179688, 1539.056885, 52.259014, 1.599460 } +}; + +static Waypoint toOrchad[] = +{ + { -11463.185547, 1525.146362, 50.937778, 3.728201 }, + { -11466.443359, 1530.258789, 50.249241, 1.886439 }, + { -11480.715820, 1551.802002, 49.263508, 1.893513 } +}; + +//Thug definitions + +#define DEFIAS_RAIDER 6180 + +#define THUG_SPAWN_X -11431 +#define THUG_SPAWN_Y 1607 +#define THUG_SPAWN_Z 70.746 +#define THUG_SPAWN_O 4.164382 +#define THUG_SPAWN_R 5 //radius , because they don't all spawn at the same spot + +class npc_DaphneStillwell_AI : public EventAI +{ +public: + + npc_DaphneStillwell_AI(Creature *c) : EventAI(c), thug_wave(0), InEvent(false) {} + + std::vector thugs; //Thugs out there + uint8 thug_wave; //Current wave of thugs + bool InEvent; //Is the quest event in progress + std::vector participants; //Vector because multiple people can start the quest simultaneously + + void EnterEvadeMode() + { + m_creature->RemoveAllAttackers(); + m_creature->RemoveAllAuras(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(); + + if(!InEvent) //Hack because of incorrect positioning functions (plz gief access to Map::CreatureRelocation) + DoGoHome(); + } + + void AttackStart(Unit *who) + { + ScriptedAI::AttackStart(who); + } + + void MoveInLineOfSight(Unit *who) + { + ScriptedAI::MoveInLineOfSight(who); + } + + void UpdateAI_secondary(const uint32 diff) + { + if(InEvent) + { + for(std::vector::iterator itr = participants.begin(); itr != participants.end(); ++itr) + { + Unit *pala = Unit::GetUnit(*m_creature, *itr); + if(!pala || pala->isDead()) + { + participants.erase(itr); + break; + } + } + } + } + + void EventFinished(bool complete) + { + for(std::vector::iterator itr = participants.begin();itr != participants.end(); ++itr) + { + Unit *paladin = Unit::GetUnit(*m_creature, *itr); + if(paladin && paladin->GetTypeId() == TYPEID_PLAYER) + if(complete) + ((Player*)paladin)->CompleteQuest(1651); + else + ((Player*)paladin)->FailQuest(1651); + } + participants.clear(); + } + + void JustDied(Unit *killer) + { + ClearEvents(); + EventFinished(false); + } + + void OnEvent(uint32 event_id) + { + switch(event_id) + { + case 1: //spawn the first raider which would yell and continue adding events + { + Creature *yeller = m_creature->SummonCreature(DEFIAS_RAIDER, THUG_SPAWN_X, THUG_SPAWN_Y, THUG_SPAWN_Z, THUG_SPAWN_O, TEMPSUMMON_TIMED_DESPAWN, 3000); + yeller->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + yeller->Yell("Kill her! Take the farm!", LANG_UNIVERSAL, 0); + yeller->SendMonsterMove(-11412, 1633, 75, 0, true, 2000); + + NewEvent(0, 0, m_creature)->Talk("To the house! Stay close to me no matter what! I have my gun and ammo there!"); //0 duration, there is an emote with a correct duration below + NewEvent(0, 3000, m_creature)->Emote(1, true); //Emote state talk for 3 seconds + + //Time to go to the house + for(int i = 0; i < 3; i++) + NewEvent(0,0,m_creature)->Waypoint(toHouse[i].x, toHouse[i].y, toHouse[i].z, toHouse[i].o, true); //0 duration means calculate it according to the creature's speed + + NewEvent(0, 5000, m_creature)->Emote(69, true); //Emote state 69 (use standing) + NewEvent(0, 0, m_creature)->Equip(0,0,0,0); //I don't know the correct model for the gun + + //TO BATTLE !!11!!1!1 + for(int i = 0; i < 3; i++) + NewEvent(0,0,m_creature)->Waypoint(toBattle[i].x, toBattle[i].y, toBattle[i].z, toBattle[i].o, true); + + NewEvent(0,5000,m_creature)->Talk("You won't ruin my lands, you scum!", 2); //type 2 == yell + + NewEvent(0,-1,m_creature, false)->Ambush(DEFIAS_RAIDER, 3, 10, THUG_SPAWN_X, THUG_SPAWN_Y, THUG_SPAWN_Z); //Spawn trigger + NewEvent(0,5000,m_creature)->Talk("One more down!"); + + NewEvent(0,-1,m_creature, false)->Ambush(DEFIAS_RAIDER, 4, 10, THUG_SPAWN_X, THUG_SPAWN_Y, THUG_SPAWN_Z); //Spawn trigger + NewEvent(0,5000,m_creature)->Talk("One more down!"); + + NewEvent(0,-1,m_creature, false)->Ambush(DEFIAS_RAIDER, 5, 10, THUG_SPAWN_X, THUG_SPAWN_Y, THUG_SPAWN_Z); //Spawn trigger + + NewEvent(2, 0, m_creature)->Talk("We've done it! We Won!"); + NewEvent(0, 2000, m_creature)->Emote(4); + NewEvent(0, 0, m_creature)->Talk("Meet me at the orchad--I just need to put my gun away"); + + //Move to house .. again + for(int i = 0; i < 3; i++) + NewEvent(0,0,m_creature)->Waypoint(toHouse[i].x, toHouse[i].y, toHouse[i].z, toHouse[i].o, true); //0 duration means calculate it according to the creature's speed + + NewEvent(0, 5000, m_creature)->Emote(69); //Work emote here + NewEvent(0, 0, m_creature)->Equip(0,0,0,0); //Unequip gun + + //Move to orchad + for(int i = 0; i < 3; i++) + NewEvent(0,0,m_creature)->Waypoint(toOrchad[i].x, toOrchad[i].y, toOrchad[i].z, toOrchad[i].o, true); + + NewEvent(0,0,m_creature)->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER, true); + + break; + } + case 2: //Quest Completed + { + EventFinished(true); + } + } + } + + void StartEvent(Player *initiator) + { + participants.push_back(initiator->GetGUID()); //Remember who started the whole ordeal so we can give him credit later + thug_wave = 0; + + if(InEvent) // If Someone else started already + { + ClearEvents(); + } + + //use '0' IDs for insignificant events + NewEvent(0, 5000, m_creature); //I.E. just sit there staring at emty space + NewEvent(1, 5000, m_creature)->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER, false); //remove the questgiver flag + //The rest of the events are in the OnEvent function + } +}; + +bool QuestAccept_npc_DaphneStillwell(Player *player, Creature *_Creature, Quest *_Quest) +{ + if(_Quest->GetQuestId() == 1651) + ((npc_DaphneStillwell_AI*)_Creature->AI())->StartEvent(player); + + return true; +} + +CreatureAI* GetAI_npc_DaphneStillwell(Creature *c) +{ + return new npc_DaphneStillwell_AI(c); +} + +void AddSC_npc_DaphneStillwell() +{ + Script *newscript = new Script; + newscript->Name = "npc_daphne_stillwell"; + newscript->GetAI = GetAI_npc_DaphneStillwell ; + newscript->pQuestAccept = &QuestAccept_npc_DaphneStillwell; + m_scripts[nrscripts++] = newscript; +} + Index: VC80/80ScriptDev2.vcproj =================================================================== --- VC80/80ScriptDev2.vcproj (revision 136) +++ VC80/80ScriptDev2.vcproj (working copy) @@ -1,7 +1,7 @@ + + + + @@ -1250,6 +1258,10 @@ + +