Browse Source

Added Spawn Proximity code

Fix #107

LUA AddSpawnProximity added:
Argument 1 is spawn to add the proximity
Argument 2 is spawn id OR location id
Argument 3 is 0 for spawn id OR 1 for location id
Argument 4 is distance
Argument 5 is in range function
Argument 6 is out of range function
AddSpawnProximity(NPC,locationid,1,15,"InRange","OutRange")AddSpawnProximity(NPC,spawnid,0,15,"InRange","OutRange")

created 'prespawn' LUA function, AddSpawnProximity can be called before the spawn is added
Image 4 years ago
parent
commit
cbcac95674

+ 24 - 2
EQ2/source/WorldServer/LuaFunctions.cpp

@@ -1007,10 +1007,18 @@ int EQ2Emu_lua_Spawn(lua_State* state) {
 			spawn->SetHeading(heading);
 			if (restricted_npc)
 				spawn->AddAllowAccessSpawn(spawn);
-			zone->AddSpawn(spawn);
+
 			const char* spawn_script = world.GetSpawnScript(spawn_id);
+			bool scriptActive = false;
 			if (spawn_script && lua_interface->GetSpawnScript(spawn_script) != 0) {
+				scriptActive = true;
 				spawn->SetSpawnScript(string(spawn_script));
+			}
+
+			zone->CallSpawnScript(spawn, SPAWN_SCRIPT_PRESPAWN);
+
+			zone->AddSpawn(spawn);
+			if (scriptActive) {
 				zone->CallSpawnScript(spawn, SPAWN_SCRIPT_SPAWN);
 			}
 			lua_interface->SetSpawnValue(state, spawn);
@@ -9229,4 +9237,18 @@ int EQ2Emu_lua_GetZoneExpansionFlag(lua_State* state) {
 		return 1;
 	}
 	return 0;
-}
+}
+
+int EQ2Emu_lua_AddSpawnProximity(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	Spawn* spawn = lua_interface->GetSpawn(state);
+	int32 spawn_value = lua_interface->GetInt32Value(state, 2);
+	int8 spawn_type = lua_interface->GetInt8Value(state, 3);
+	float distance = lua_interface->GetFloatValue(state, 4);
+	string in_range_function = lua_interface->GetStringValue(state, 5);
+	string leaving_range_function = lua_interface->GetStringValue(state, 6);
+	if (spawn && distance > 0 && in_range_function.length() > 0)
+		spawn->AddLUASpawnProximity(spawn_value, (Spawn::SpawnProximityType)spawn_type, distance, in_range_function, leaving_range_function);
+	return 0;
+}

+ 2 - 0
EQ2/source/WorldServer/LuaFunctions.h

@@ -418,4 +418,6 @@ int EQ2Emu_lua_CheckLOSByCoordinates(lua_State* state);
 
 int EQ2Emu_lua_SetZoneExpansionFlag(lua_State* state);
 int EQ2Emu_lua_GetZoneExpansionFlag(lua_State* state);
+
+int EQ2Emu_lua_AddSpawnProximity(lua_State* state);
 #endif

+ 2 - 0
EQ2/source/WorldServer/LuaInterface.cpp

@@ -1021,6 +1021,8 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
 
 	lua_register(state, "SetZoneExpansionFlag", EQ2Emu_lua_SetZoneExpansionFlag);
 	lua_register(state, "GetZoneExpansionFlag", EQ2Emu_lua_GetZoneExpansionFlag);
+
+	lua_register(state, "AddSpawnProximity", EQ2Emu_lua_AddSpawnProximity);
 }
 
 void LuaInterface::LogError(const char* error, ...)  {

+ 64 - 0
EQ2/source/WorldServer/Spawn.cpp

@@ -136,6 +136,9 @@ Spawn::~Spawn(){
 		safe_delete(rq_itr->second);
 	}
 	m_requiredQuests.releasewritelock(__FUNCTION__, __LINE__);
+
+	// just in case to make sure data is destroyed
+	RemoveSpawnProximities();
 }
 
 void Spawn::InitializeHeaderPacketData(Player* player, PacketStruct* header, int16 index) {
@@ -2174,6 +2177,8 @@ void Spawn::MoveToLocation(Spawn* spawn, float distance, bool immediate, bool ma
 }
 
 void Spawn::ProcessMovement(bool isSpawnListLocked){
+	CheckProximities();
+
 	if(IsPlayer()){
 		//Check if player is riding a boat, if so update pos (boat's current location + XYZ offsets)
 		Player* player = ((Player*)this);
@@ -3157,3 +3162,62 @@ void Spawn::CalculateNewFearpoint()
 		}
 	}
 }
+
+void Spawn::CheckProximities()
+{
+	if (spawn_proximities.size() > 0)
+	{
+		MutexList<SpawnProximity*>::iterator itr = spawn_proximities.begin();
+		while (itr.Next()) {
+			SpawnProximity* prox = itr.value;
+			map<int32, bool>::iterator spawnsItr;
+			for (spawnsItr = prox->spawns_in_proximity.begin(); spawnsItr != prox->spawns_in_proximity.end(); spawnsItr++) {
+				Spawn* tmpSpawn = 0;
+				if (spawnsItr->first &&
+					((prox->spawn_type == SPAWNPROXIMITY_DATABASE_ID && (tmpSpawn = GetZone()->GetSpawnByDatabaseID(spawnsItr->first)) != 0) ||
+						(prox->spawn_type == SPAWNPROXIMITY_LOCATION_ID && (tmpSpawn = GetZone()->GetSpawnByLocationID(spawnsItr->first)) != 0)))
+				{
+					if (!spawnsItr->second && tmpSpawn->GetDistance(this) <= prox->distance)
+					{
+						if (prox->in_range_lua_function.size() > 0)
+							GetZone()->CallSpawnScript(this, SPAWN_SCRIPT_CUSTOM, tmpSpawn, prox->in_range_lua_function.c_str());
+						spawnsItr->second = true;
+					}
+					else if (spawnsItr->second && tmpSpawn->GetDistance(this) > prox->distance)
+					{
+						if (prox->leaving_range_lua_function.size() > 0)
+							GetZone()->CallSpawnScript(this, SPAWN_SCRIPT_CUSTOM, tmpSpawn, prox->leaving_range_lua_function.c_str());
+						spawnsItr->second = false;
+					}
+				}
+			}
+		}
+	}
+}
+
+void Spawn::AddSpawnToProximity(int32 spawnValue, SpawnProximityType type)
+{
+	if (spawn_proximities.size() > 0)
+	{
+		MutexList<SpawnProximity*>::iterator itr = spawn_proximities.begin();
+		while (itr.Next()) {
+			SpawnProximity* prox = itr->value;
+			if (prox->spawn_value == spawnValue && prox->spawn_type == type)
+				prox->spawns_in_proximity.insert(make_pair(spawnValue, false));
+		}
+	}
+}
+
+void Spawn::RemoveSpawnFromProximity(int32 spawnValue, SpawnProximityType type)
+{
+	if (spawn_proximities.size() > 0)
+	{
+		MutexList<SpawnProximity*>::iterator itr = spawn_proximities.begin();
+		while (itr.Next()) {
+			SpawnProximity* prox = itr->value;
+			if (prox->spawn_value == spawnValue && prox->spawn_type == type &&
+				prox->spawns_in_proximity.count(spawnValue) > 0)
+				prox->spawns_in_proximity.erase(spawnValue);
+		}
+	}
+}

+ 45 - 0
EQ2/source/WorldServer/Spawn.h

@@ -34,6 +34,7 @@
 #include "Items/Items.h"
 #include "Zone/map.h"
 #include "../common/Mutex.h"
+#include "MutexList.h"
 #include <deque>
 #include <memory> // needed for LS to compile properly on linux
 
@@ -994,7 +995,47 @@ public:
 	int16 pos_packet_size;
 	int16 info_packet_size;
 	int16 vis_packet_size;
+
+	enum SpawnProximityType {
+		SPAWNPROXIMITY_DATABASE_ID = 0,
+		SPAWNPROXIMITY_LOCATION_ID = 1
+	};
+
+	struct SpawnProximity {
+		float				x;
+		float				y;
+		float				z;
+		int32				spawn_value;
+		int8				spawn_type;
+		float				distance;
+		string				in_range_lua_function;
+		string				leaving_range_lua_function;
+		map<int32, bool>	spawns_in_proximity;
+	};
+
+	void AddSpawnToProximity(int32 spawnValue, SpawnProximityType type);
+	void RemoveSpawnFromProximity(int32 spawnValue, SpawnProximityType type);
+
+	SpawnProximity* AddLUASpawnProximity(int32 spawnValue, SpawnProximityType type, float distance, string in_range_function, string leaving_range_function) {
+		SpawnProximity* prox = new SpawnProximity;
+		prox->spawn_value = spawnValue;
+		prox->spawn_type = type;
+		prox->distance = distance;
+		prox->in_range_lua_function = in_range_function;
+		prox->leaving_range_lua_function = leaving_range_function;
+		spawn_proximities.Add(prox);
+		return prox;
+	}
+
+	void RemoveSpawnProximities() {
+		MutexList<SpawnProximity*>::iterator itr = spawn_proximities.begin();
+		while (itr.Next()) {
+			safe_delete(itr->value);
+		}
+		spawn_proximities.clear();
+	}
 protected:
+
 	bool	send_spawn_changes;
 	bool	invulnerable;
 	bool	attack_resume_needed;
@@ -1021,6 +1062,10 @@ protected:
 	map<int32, vector<int16>* > required_quests;
 	map<int32, LUAHistory> required_history;
 	EquipmentItemList equipment_list;
+
+	MutexList<SpawnProximity*> spawn_proximities;
+
+	void CheckProximities();
 private:	
 	deque<MovementLocation*>* movement_locations;
 	Mutex*			MMovementLocations;

+ 142 - 38
EQ2/source/WorldServer/zoneserver.cpp

@@ -2092,49 +2092,25 @@ Spawn* ZoneServer::ProcessSpawnLocation(SpawnLocation* spawnlocation, bool respa
 				continue;
 		}
 
-		if(spawnlocation->entities[i]->spawn_percentage >= rand_number){
-			if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_NPC)
+		if (spawnlocation->entities[i]->spawn_percentage >= rand_number) {
+			if (spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_NPC)
 				spawn = AddNPCSpawn(spawnlocation, spawnlocation->entities[i]);
-			else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_GROUNDSPAWN)
+			else if (spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_GROUNDSPAWN)
 				spawn = AddGroundSpawn(spawnlocation, spawnlocation->entities[i]);
-			else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_OBJECT)
+			else if (spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_OBJECT)
 				spawn = AddObjectSpawn(spawnlocation, spawnlocation->entities[i]);
-			else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_WIDGET)
+			else if (spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_WIDGET)
 				spawn = AddWidgetSpawn(spawnlocation, spawnlocation->entities[i]);
-			else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_SIGN)
+			else if (spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_SIGN)
 				spawn = AddSignSpawn(spawnlocation, spawnlocation->entities[i]);
 
-			if (!spawn) 
+			if (!spawn)
 			{
 				LogWrite(ZONE__ERROR, 0, "Zone", "Error adding spawn to zone");
 				safe_delete(spawn);
 				continue;
 			}
 
-			const char* script = 0;
-
-			for(int x=0;x<3;x++)
-			{
-				switch(x)
-				{
-					case 0:
-						script = world.GetSpawnEntryScript(spawnlocation->entities[i]->spawn_entry_id);
-						break;
-					case 1:
-						script = world.GetSpawnLocationScript(spawnlocation->entities[i]->spawn_location_id);
-						break;
-					case 2:
-						script = world.GetSpawnScript(spawnlocation->entities[i]->spawn_id);
-						break;
-				}
-
-				if(script && lua_interface && lua_interface->GetSpawnScript(script) != 0)
-				{
-					spawn->SetSpawnScript(string(script));
-					break;
-				}
-			}
-
 			if (spawn) 
 			{
 				if(respawn)
@@ -2449,6 +2425,7 @@ NPC* ZoneServer::AddNPCSpawn(SpawnLocation* spawnlocation, SpawnEntry* spawnentr
 	NPC* npc = GetNewNPC(spawnentry->spawn_id);
 	if(npc){
 		DeterminePosition(spawnlocation, npc);
+		npc->SetDatabaseID(spawnentry->spawn_id);
 		npc->SetSpawnLocationID(spawnentry->spawn_location_id);
 		npc->SetSpawnEntryID(spawnentry->spawn_entry_id);
 		npc->SetRespawnTime(spawnentry->respawn);
@@ -2456,6 +2433,11 @@ NPC* ZoneServer::AddNPCSpawn(SpawnLocation* spawnlocation, SpawnEntry* spawnentr
 		if (spawnentry->expire_time > 0)
 			AddSpawnExpireTimer(npc, spawnentry->expire_time, spawnentry->expire_offset);
 		AddLoot(npc);
+
+		SetSpawnScript(spawnentry, npc);
+
+		CallSpawnScript(npc, SPAWN_SCRIPT_PRESPAWN);
+
 		AddSpawn(npc);
 	}
 	LogWrite(SPAWN__TRACE, 1, "Spawn", "Exit %s", __FUNCTION__);
@@ -2713,6 +2695,10 @@ void ZoneServer::CallSpawnScript(Spawn* npc, int8 type, Spawn* spawn, const char
 				lua_interface->RunSpawnScript(script, "hear_say", npc, spawn, message);
 				break;
 			}
+			case SPAWN_SCRIPT_PRESPAWN: {
+				lua_interface->RunSpawnScript(script, "prespawn", npc);
+				break;
+			}
 		}
 	}
 	LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit %s", __FUNCTION__);
@@ -2774,14 +2760,18 @@ Sign* ZoneServer::AddSignSpawn(SpawnLocation* spawnlocation, SpawnEntry* spawnen
 	Sign* sign = GetNewSign(spawnentry->spawn_id);
 	if(sign){
 		DeterminePosition(spawnlocation, sign);
+		sign->SetDatabaseID(spawnentry->spawn_id);
 		sign->SetSpawnLocationID(spawnentry->spawn_location_id);
 		sign->SetSpawnEntryID(spawnentry->spawn_entry_id);
 		sign->SetRespawnTime(spawnentry->respawn);
 		sign->SetExpireTime(spawnentry->expire_time);
 		if (spawnentry->expire_time > 0)
 			AddSpawnExpireTimer(sign, spawnentry->expire_time, spawnentry->expire_offset);
-		
-		
+
+		SetSpawnScript(spawnentry, sign);
+
+		CallSpawnScript(sign, SPAWN_SCRIPT_PRESPAWN);
+
 		AddSpawn(sign);
 	}
 	LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit %s", __FUNCTION__);
@@ -2793,6 +2783,7 @@ Widget* ZoneServer::AddWidgetSpawn(SpawnLocation* spawnlocation, SpawnEntry* spa
 	Widget* widget = GetNewWidget(spawnentry->spawn_id);
 	if(widget){
 		DeterminePosition(spawnlocation, widget);
+		widget->SetDatabaseID(spawnentry->spawn_id);
 		widget->SetSpawnLocationID(spawnentry->spawn_location_id);
 		widget->SetSpawnEntryID(spawnentry->spawn_entry_id);
 		if(!widget->GetIncludeLocation()){
@@ -2806,6 +2797,11 @@ Widget* ZoneServer::AddWidgetSpawn(SpawnLocation* spawnlocation, SpawnEntry* spa
 		widget->SetSpawnOrigHeading(widget->GetHeading());
 		if (spawnentry->expire_time > 0)
 			AddSpawnExpireTimer(widget, spawnentry->expire_time, spawnentry->expire_offset);
+
+		SetSpawnScript(spawnentry, widget);
+
+		CallSpawnScript(widget, SPAWN_SCRIPT_PRESPAWN);
+
 		AddSpawn(widget);
 	}
 	LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit %s", __FUNCTION__);
@@ -2817,12 +2813,18 @@ Object* ZoneServer::AddObjectSpawn(SpawnLocation* spawnlocation, SpawnEntry* spa
 	Object* object = GetNewObject(spawnentry->spawn_id);
 	if(object){
 		DeterminePosition(spawnlocation, object);
+		object->SetDatabaseID(spawnentry->spawn_id);
 		object->SetSpawnLocationID(spawnentry->spawn_location_id);
 		object->SetSpawnEntryID(spawnentry->spawn_entry_id);
 		object->SetRespawnTime(spawnentry->respawn);
 		object->SetExpireTime(spawnentry->expire_time);
 		if (spawnentry->expire_time > 0)
 			AddSpawnExpireTimer(object, spawnentry->expire_time, spawnentry->expire_offset);
+
+		SetSpawnScript(spawnentry, object);
+
+		CallSpawnScript(object, SPAWN_SCRIPT_PRESPAWN);
+
 		AddSpawn(object);
 	}
 	LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit %s", __FUNCTION__);
@@ -2834,12 +2836,18 @@ GroundSpawn* ZoneServer::AddGroundSpawn(SpawnLocation* spawnlocation, SpawnEntry
 	GroundSpawn* spawn = GetNewGroundSpawn(spawnentry->spawn_id);
 	if(spawn){
 		DeterminePosition(spawnlocation, spawn);
+		spawn->SetDatabaseID(spawnentry->spawn_id);
 		spawn->SetSpawnLocationID(spawnentry->spawn_location_id);
 		spawn->SetSpawnEntryID(spawnentry->spawn_entry_id);
 		spawn->SetRespawnTime(spawnentry->respawn);
 		spawn->SetExpireTime(spawnentry->expire_time);
 		if (spawnentry->expire_time > 0)
 			AddSpawnExpireTimer(spawn, spawnentry->expire_time, spawnentry->expire_offset);
+
+		SetSpawnScript(spawnentry, spawn);
+
+		CallSpawnScript(spawn, SPAWN_SCRIPT_PRESPAWN);
+
 		AddSpawn(spawn);
 	}
 	LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit %s", __FUNCTION__);
@@ -2848,6 +2856,7 @@ GroundSpawn* ZoneServer::AddGroundSpawn(SpawnLocation* spawnlocation, SpawnEntry
 
 void ZoneServer::AddSpawn(Spawn* spawn) {
 	spawn->SetZone(this);
+
 	spawn->position_changed = false;
 	spawn->info_changed = false;
 	spawn->vis_changed = false;
@@ -2879,6 +2888,8 @@ void ZoneServer::AddSpawn(Spawn* spawn) {
 		movementMgr->AddMob((Entity*)spawn);
 	}
 
+	AddSpawnProximities(spawn);
+
 	spawn->SetAddedToWorldTimestamp(Timer::GetCurrentTime2());
 }
 
@@ -3753,6 +3764,9 @@ void ZoneServer::RemoveSpawn(bool spawnListLocked, Spawn* spawn, bool delete_spa
 {
 	LogWrite(ZONE__DEBUG, 3, "Zone", "Processing RemoveSpawn function for %s (%i)...", spawn->GetName(),spawn->GetID());
 
+	spawn->RemoveSpawnProximities();
+	RemoveSpawnProximities(spawnListLocked, spawn);
+
 	if (Grid != nullptr) {
 		Grid->RemoveSpawnFromCell(spawn);
 	}
@@ -4186,7 +4200,7 @@ void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, boo
 
 
 		for (int8 i = 0; i < encounter->size(); i++) {
-			spawn = GetSpawnByID(encounter->at(i),spawnListLocked);
+			spawn = GetSpawnByID(encounter->at(i), spawnListLocked);
 			// set a flag to let us know if the killer is in the encounter
 			if (!killer_in_encounter && spawn == killer)
 				killer_in_encounter = true;
@@ -4205,11 +4219,11 @@ void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, boo
 				// Get the client of the player
 				client = GetClientBySpawn(spawn);
 				// valid client?
-				if(client) {
+				if (client) {
 					// Check for quest kill updates
 					client->CheckPlayerQuestsKillUpdate(dead);
 					// If the dead mob is not a player and if it had a faction with an ID greater or equal to 10 the send faction changes
-					if(!dead->IsPlayer() && dead->GetFactionID() > 10)
+					if (!dead->IsPlayer() && dead->GetFactionID() > 10)
 						ProcessFaction(dead, client);
 
 					// Send xp...this is currently wrong fix it
@@ -4222,7 +4236,7 @@ void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, boo
 							if (((Player*)spawn)->AddXP((int32)xp)) {
 								client->Message(CHANNEL_COLOR_EXP, "You gain %u XP!", (int32)xp);
 								LogWrite(PLAYER__DEBUG, 0, "Player", "Player: %s earned %u experience.", spawn->GetName(), (int32)xp);
-								if(spawn->GetLevel() != level)
+								if (spawn->GetLevel() != level)
 									client->ChangeLevel(level, spawn->GetLevel());
 								((Player*)spawn)->SetCharSheetChanged(true);
 							}
@@ -4232,7 +4246,7 @@ void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, boo
 			}
 
 			// If a chest is being dropped add this spawn to the chest's encounter so they can loot it
-			if (chest)
+			if (chest && spawn && spawn->IsEntity())
 				chest->Brain()->AddToEncounter((Entity*)spawn);
 		}
 
@@ -7150,4 +7164,94 @@ void ZoneServer::ProcessSpawnConditional(int8 condition) {
 	}
 	
 	MSpawnLocationList.releasereadlock(__FUNCTION__, __LINE__);
+}
+
+void ZoneServer::AddSpawnProximities(Spawn* newSpawn) {
+	Spawn* spawn = 0;
+	map<int32, Spawn*>::iterator itr;
+	MSpawnList.readlock(__FUNCTION__, __LINE__);
+	MPendingSpawnListAdd.readlock(__FUNCTION__, __LINE__);
+	for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
+		spawn = itr->second;
+		if (spawn && spawn != newSpawn) {
+			if (newSpawn->GetDatabaseID())
+				spawn->AddSpawnToProximity(newSpawn->GetDatabaseID(), Spawn::SpawnProximityType::SPAWNPROXIMITY_DATABASE_ID);
+			if (newSpawn->GetSpawnLocationID())
+				spawn->AddSpawnToProximity(newSpawn->GetSpawnLocationID(), Spawn::SpawnProximityType::SPAWNPROXIMITY_LOCATION_ID);
+
+			if (spawn->GetDatabaseID())
+				newSpawn->AddSpawnToProximity(spawn->GetDatabaseID(), Spawn::SpawnProximityType::SPAWNPROXIMITY_DATABASE_ID);
+			if (spawn->GetSpawnLocationID())
+				newSpawn->AddSpawnToProximity(spawn->GetSpawnLocationID(), Spawn::SpawnProximityType::SPAWNPROXIMITY_LOCATION_ID);
+		}
+	}
+
+	list<Spawn*>::iterator itr2;
+	for (itr2 = pending_spawn_list_add.begin(); itr2 != pending_spawn_list_add.end(); itr2++) {
+		spawn = *itr2;
+		if (spawn && spawn != newSpawn) {
+			if (newSpawn->GetDatabaseID())
+				spawn->AddSpawnToProximity(newSpawn->GetDatabaseID(), Spawn::SpawnProximityType::SPAWNPROXIMITY_DATABASE_ID);
+			if (newSpawn->GetSpawnLocationID())
+				spawn->AddSpawnToProximity(newSpawn->GetSpawnLocationID(), Spawn::SpawnProximityType::SPAWNPROXIMITY_LOCATION_ID);
+
+			if (spawn->GetDatabaseID())
+				newSpawn->AddSpawnToProximity(spawn->GetDatabaseID(), Spawn::SpawnProximityType::SPAWNPROXIMITY_DATABASE_ID);
+			if (spawn->GetSpawnLocationID())
+				newSpawn->AddSpawnToProximity(spawn->GetSpawnLocationID(), Spawn::SpawnProximityType::SPAWNPROXIMITY_LOCATION_ID);
+		}
+	}
+	MPendingSpawnListAdd.releasereadlock(__FUNCTION__, __LINE__);
+	MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
+}
+
+void ZoneServer::RemoveSpawnProximities(bool spawnListLocked, Spawn* oldSpawn) {
+	Spawn* spawn = 0;
+	map<int32, Spawn*>::iterator itr;
+	if (!spawnListLocked)
+		MSpawnList.readlock(__FUNCTION__, __LINE__);
+	for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
+		spawn = itr->second;
+		if (spawn && spawn != oldSpawn) {
+			if (oldSpawn->GetDatabaseID())
+				spawn->RemoveSpawnFromProximity(oldSpawn->GetDatabaseID(), Spawn::SpawnProximityType::SPAWNPROXIMITY_DATABASE_ID);
+			if (oldSpawn->GetSpawnLocationID())
+				spawn->RemoveSpawnFromProximity(oldSpawn->GetSpawnLocationID(), Spawn::SpawnProximityType::SPAWNPROXIMITY_LOCATION_ID);
+
+			// don't need to remove oldSpawn proximities, we clear them all out
+		}
+	}
+
+	if (!spawnListLocked)
+		MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
+}
+
+void ZoneServer::SetSpawnScript(SpawnEntry* entry, Spawn* spawn)
+{
+	if (!entry || !spawn)
+		return;
+
+	const char* script = 0;
+
+	for (int x = 0; x < 3; x++)
+	{
+		switch (x)
+		{
+		case 0:
+			script = world.GetSpawnEntryScript(entry->spawn_entry_id);
+			break;
+		case 1:
+			script = world.GetSpawnLocationScript(entry->spawn_location_id);
+			break;
+		case 2:
+			script = world.GetSpawnScript(entry->spawn_id);
+			break;
+		}
+
+		if (script && lua_interface && lua_interface->GetSpawnScript(script) != 0)
+		{
+			spawn->SetSpawnScript(string(script));
+			break;
+		}
+	}
 }

+ 4 - 0
EQ2/source/WorldServer/zoneserver.h

@@ -85,6 +85,7 @@ class Bot;
 #define SPAWN_SCRIPT_COMBAT_RESET		16
 #define SPAWN_SCRIPT_GROUP_DEAD			17
 #define SPAWN_SCRIPT_HEAR_SAY			18
+#define SPAWN_SCRIPT_PRESPAWN			19
 
 #define SPAWN_CONDITIONAL_NONE			0
 #define SPAWN_CONDITIONAL_DAY			1
@@ -609,6 +610,9 @@ public:
 
 	void SetSpawnStructs(Client* client);
 
+	void AddSpawnProximities(Spawn* spawn);
+	void RemoveSpawnProximities(bool spawnListLocked, Spawn* spawn);
+	void SetSpawnScript(SpawnEntry* entry, Spawn* spawn);
 	bool IsLoading() {
 		return LoadingData;
 	}