Browse Source

Fix #455 - Falling underworld to safe spot support

Emagi 1 year ago
parent
commit
13e0d2857f

+ 5 - 5
EQ2/source/WorldServer/Commands/Commands.cpp

@@ -4802,11 +4802,9 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 					if (spawn->IsNPC()) {
 						details4 += "\nRandomize:		" + to_string(((NPC*)spawn)->GetRandomize()) + "\n";
 					}
-
-					const char* spawnScriptMsg = (spawn->GetSpawnScript() && strlen(spawn->GetSpawnScript())>0) ? spawn->GetSpawnScript() : "Not Set";
-
-						details4 += "\nSpawnScript:		" + std::string(spawnScriptMsg) + "\n";
 				}
+				const char* spawnScriptMsg = (spawn->GetSpawnScript() && strlen(spawn->GetSpawnScript())>0) ? spawn->GetSpawnScript() : "Not Set";
+				details4 += "\nSpawnScript:		" + std::string(spawnScriptMsg) + "\n";
 
 				string title = string(spawn->GetName()) + "(" + to_string(spawn->GetDatabaseID()) + ")";
 				client->SendShowBook(client->GetPlayer(), title, 0, 4, details, details2, details3, details4);
@@ -6103,7 +6101,9 @@ void Commands::Command_Grid(Client* client)
 			auto loc = glm::vec3(client->GetPlayer()->GetX(), client->GetPlayer()->GetZ(), client->GetPlayer()->GetY());
 			uint32 GridID = 0;
 			float new_z = client->GetPlayer()->GetMap()->FindBestZ(loc, nullptr, &GridID);
-			client->Message(CHANNEL_COLOR_YELLOW, "Grid result is %u, at EQ2 Y coordinate %f", GridID, new_z);
+			float minY = client->GetPlayer()->GetMap()->GetMinY();
+			float maxY = client->GetPlayer()->GetMap()->GetMaxY();
+			client->Message(CHANNEL_COLOR_YELLOW, "Grid result is %u, at EQ2 Y coordinate %f.  Min/Max Y %f/%f", GridID, new_z, minY, maxY);
 	}
 }
 

+ 3 - 1
EQ2/source/WorldServer/Rules/Rules.cpp

@@ -329,7 +329,9 @@ void RuleManager::Init()
 	RULE_INIT(R_Zone, ShutdownDelayTimer, "120000");
 	RULE_INIT(R_Zone, WeatherTimer, "60000");						// default: 1 minute
 	RULE_INIT(R_Zone, SpawnDeleteTimer, "30000");					// default: 30 seconds, how long a spawn pointer is held onto after being removed from the world before deleting it
-
+	RULE_INIT(R_Zone, UseMapUnderworldCoords, "1"); // use maps lowest Y coordinate to establish underworld markers
+	RULE_INIT(R_Zone, MapUnderworldCoordOffset, "-50.0"); // adds (or in the case of negative value subtracts) so that the underworld marker is lower when map is using its lowest Y coordinate
+	
 	RULE_INIT(R_Loot, LootRadius, "5.0");
 	RULE_INIT(R_Loot, AutoDisarmChest, "1");
 	RULE_INIT(R_Loot, ChestTriggerRadiusGroup, "10.0"); // radius at which chest will trigger against group members

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

@@ -175,6 +175,8 @@ enum RuleType {
 	CheckAttackPlayer,
 	CheckAttackNPC,
 	HOTime,
+	UseMapUnderworldCoords,
+	MapUnderworldCoordOffset,
 
 	/* LOOT */
 	LootRadius,

+ 19 - 2
EQ2/source/WorldServer/Zone/map.cpp

@@ -66,6 +66,8 @@ Map::Map(string zonename, string file) {
 	m_ZoneName = zonename;
 	m_ZoneFile = file;
 	imp = nullptr;
+	m_MinY = 9999999.0f;
+	m_MaxY = -9999999.0f;
 }
 
 Map::~Map() {
@@ -411,7 +413,11 @@ bool Map::LoadV2(FILE* f) {
 			glm::vec3 a(x1, z1, y1);
 			glm::vec3 b(x2, z2, y2);
 			glm::vec3 c(x3, z3, y3);
-
+			
+			MapMinMaxY(y1);
+			MapMinMaxY(y2);
+			MapMinMaxY(y3);
+			
 			size_t sz = verts.size();
 			verts.push_back(a);
 			indices.push_back((uint32)sz);
@@ -549,6 +555,10 @@ bool Map::LoadV2Deflated(FILE* f) {
 			glm::vec3 b(x2, z2, y2);
 			glm::vec3 c(x3, z3, y3);
 
+			MapMinMaxY(y1);
+			MapMinMaxY(y2);
+			MapMinMaxY(y3);
+			
 			size_t sz = verts.size();
 			verts.push_back(a);
 			indices.push_back((uint32)sz);
@@ -617,6 +627,13 @@ void Map::TranslateVertex(glm::vec3 &v, float tx, float ty, float tz) {
 	v.z = v.z + tz;
 }
 
+void Map::MapMinMaxY(float y) {
+	if(y < m_MinY)
+		m_MinY = y;
+	if(y > m_MaxY)
+		m_MaxY = y;
+}
+
 void MapRange::AddVersionRange(std::string zoneName) {
   boost::filesystem::path targetDir("Maps/");
 
@@ -719,4 +736,4 @@ map<VersionRange*, Map*>::iterator MapRange::FindMapByVersion(int32 version)
 	}
 
 	return enditr;
-}
+}

+ 6 - 0
EQ2/source/WorldServer/Zone/map.h

@@ -75,10 +75,14 @@ public:
 	}
 	float GetMinX() { return m_MinX; }
 	float GetMaxX() { return m_MaxX; }
+	float GetMinY() { return m_MinY; }
+	float GetMaxY() { return m_MaxY; }
 	float GetMinZ() { return m_MinZ; }
 	float GetMaxZ() { return m_MaxZ; }
 
 	void SetFileName(std::string newfile) { m_FileName = string(newfile); }
+	
+	void MapMinMaxY(float y);
 private:
 	void RotateVertex(glm::vec3 &v, float rx, float ry, float rz);
 	void ScaleVertex(glm::vec3 &v, float sx, float sy, float sz);
@@ -92,8 +96,10 @@ private:
 	int32 m_CellSize;
 
 	float m_MinX;
+	float m_MinY;
 	float m_MinZ;
 	float m_MaxX;
+	float m_MaxY;
 	float m_MaxZ;
 
 	struct impl;

+ 25 - 1
EQ2/source/WorldServer/client.cpp

@@ -120,7 +120,7 @@ extern ChestTrapList chest_trap_list;
 
 using namespace std;
 
-Client::Client(EQStream* ieqs) : pos_update(125), quest_pos_timer(2000), lua_debug_timer(30000), delayTimer(500), transmuteID(0), temp_placement_timer(10), spawn_removal_timer(250) {
+Client::Client(EQStream* ieqs) : underworld_cooldown_timer(5000), pos_update(125), quest_pos_timer(2000), lua_debug_timer(30000), delayTimer(500), transmuteID(0), temp_placement_timer(10), spawn_removal_timer(250) {
 	eqs = ieqs;
 	ip = eqs->GetrIP();
 	port = ntohs(eqs->GetrPort());
@@ -214,6 +214,7 @@ Client::Client(EQStream* ieqs) : pos_update(125), quest_pos_timer(2000), lua_deb
 	spawn_removal_timer.Start();
 	disable_save = false;
 	SetZoningDestination(nullptr);
+	underworld_cooldown_timer.Disable();
 }
 
 Client::~Client() {
@@ -3189,6 +3190,29 @@ bool Client::Process(bool zone_process) {
 			GetPlayer()->GetRegionMap()->TicRegionsNearSpawn(this->GetPlayer(), regionDebugMessaging ? this : nullptr);
 		
 		if(player_pos_changed && IsReadyForUpdates()) {
+			if(!underworld_cooldown_timer.Enabled() || (underworld_cooldown_timer.Enabled() && underworld_cooldown_timer.Check())) {
+				bool underworld = false;
+				if(rule_manager.GetGlobalRule(R_Zone, UseMapUnderworldCoords)->GetBool()) {
+					if(GetPlayer()->GetMap() && GetPlayer()->GetY() < (GetPlayer()->GetMap()->GetMinY() + rule_manager.GetGlobalRule(R_Zone, MapUnderworldCoordOffset)->GetFloat())) {		
+						underworld = true;
+					}
+				}
+				else if(GetPlayer()->GetMap() && GetPlayer()->GetY() < GetCurrentZone()->GetUnderWorld()) {		
+						underworld = true;
+				}
+				if(underworld) {
+						player->SetX(GetCurrentZone()->GetSafeX());
+						player->SetY(GetCurrentZone()->GetSafeY());
+						player->SetZ(GetCurrentZone()->GetSafeZ());
+						player->SetHeading(GetCurrentZone()->GetSafeHeading());
+						EQ2Packet* app = GetPlayer()->Move(player->GetX(), player->GetY(), player->GetZ(), GetVersion(), player->GetHeading());
+						if(app){
+							QueuePacket(app);
+						}
+						SimpleMessage(CHANNEL_COLOR_RED, "You have been teleported to a safe location in the zone, because you appeared to have fallen through the world.");
+					}
+				underworld_cooldown_timer.Start();
+			}
 			//GetPlayer()->CalculateLocation();
 			client_list.CheckPlayersInvisStatus(this);
 			GetCurrentZone()->SendPlayerPositionChanges(GetPlayer());

+ 1 - 0
EQ2/source/WorldServer/client.h

@@ -636,6 +636,7 @@ private:
 	float	zoning_h;
 	bool	firstlogin;
 	bool	new_client_login;
+	Timer	underworld_cooldown_timer;
 	Timer	pos_update;
 	Timer	quest_pos_timer;
 	Timer	lua_debug_timer;

+ 6 - 1
EQ2/source/WorldServer/zoneserver.cpp

@@ -5435,7 +5435,12 @@ EQ2Packet* ZoneServer::GetZoneInfoPacket(Client* client){
 		safe_delete(slides);
 	}
 
-	packet->setDataByName("underworld", underworld);
+	if(rule_manager.GetGlobalRule(R_Zone, UseMapUnderworldCoords)->GetBool() && client->GetPlayer()->GetMap()) {
+		packet->setDataByName("underworld", client->GetPlayer()->GetMap()->GetMinY() + rule_manager.GetGlobalRule(R_Zone, MapUnderworldCoordOffset)->GetFloat());
+	}
+	else {
+		packet->setDataByName("underworld", underworld);
+	}
 	
 	// unknown3 can prevent screen shots from being taken if
 	//packet->setDataByName("unknown3", 2094661567, 1);			// Screenshots allowed with this value

+ 2 - 1
EQ2/source/WorldServer/zoneserver.h

@@ -504,6 +504,8 @@ public:
 	}
 	
 	void			SetUnderWorld(float under){ underworld = under; }
+	float			GetUnderWorld(){ return underworld; }
+	
 	inline int32	GetZoneID()		{ return zoneID; }
 	void			SetZoneID(int32 new_id){ zoneID = new_id; }
 	
@@ -622,7 +624,6 @@ public:
 	//inline int16		GetCPort()		{ return clientport; }			// never used?
 	//inline bool			IsBootingUp()	{ return BootingUp; }			// never used?
 	//int32	GetShutdownTimer() {return shutdownTimer.GetTimerTime();}	// never used
-	//float	GetUnderWorld(){ return underworld; }						// function never used but variable is
 
 	// Following were private