Browse Source

Adds char properties and fixes looting alive npcs

Fixes #21 - need cross zone capabilities for speed, flymode and invul
Fixes #22 - NPCs that are alive should not be looted (also /loot command was causing npcs to poof if you attempted to loot npc with no loot)
Image 1 year ago
parent
commit
b19fd5f36f

+ 48 - 0
EQ2/source/WorldServer/ClientPacketFunctions.cpp

@@ -399,4 +399,52 @@ void ClientPacketFunctions::SendStateCommand(Client* client, int32 spawn_id, int
 		client->QueuePacket(packet->serialize());
 	}
 	safe_delete(packet);
+}
+
+void ClientPacketFunctions::SendFlyMode(Client* client, int8 flymode, bool updateCharProperty)
+{
+	PacketStruct* packet = configReader.getStruct("WS_ServerControlFlags", client->GetVersion());
+
+	if (updateCharProperty)
+		database.insertCharacterProperty(client, CHAR_PROPERTY_FLYMODE, (char*)std::to_string(flymode).c_str());
+
+	if (packet) {
+		packet->setDataByName("parameter5", 32);
+		packet->setDataByName("value", flymode);
+		client->QueuePacket(packet->serialize());
+
+		client->Message(CHANNEL_STATUS, "Flymode %s", flymode == 1 ? "on" : "off");
+		/*
+		Some other values for this packet
+		first param:
+		01 flymode
+		02 collisons off
+		04 unknown
+		08 forward movement
+		16 heading movement
+		32 low gravity
+		64 sit
+
+		second
+		2 crouch
+
+
+		third:
+		04 float when trying to jump, no movement
+		08 jump high, no movement
+
+		fourth:
+		04 autorun (fear?)
+		16 moon jumps
+		32 safe fall (float to ground)
+		64 cant move
+
+		fifth:
+		01 die
+		08 hover (fae)
+		32 flymode2?
+
+		*/
+		safe_delete(packet);
+	}
 }

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

@@ -67,6 +67,8 @@ public:
 
 	static void SendStateCommand(Client* client, int32 spawn_id, int32 state);
 
+	static void SendFlyMode(Client* client, int8 flymode, bool updateCharProperty=true);
+
 	/* Tradeskills (/Tradeskills/TradeskillsPackets.cpp) */
 	static void SendCreateFromRecipe(Client* client, int32 recipeID);
 	static void SendItemCreationUI(Client* client, Recipe* recipe);

+ 20 - 42
EQ2/source/WorldServer/Commands/Commands.cpp

@@ -1819,47 +1819,10 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 			break;
 							}
 		case COMMAND_FLYMODE:{
-			PacketStruct* packet = configReader.getStruct("WS_ServerControlFlags", client->GetVersion());
-			if(packet && sep && sep->arg[0] && sep->IsNumber(0)){
+			if(sep && sep->arg[0] && sep->IsNumber(0)){
 				PrintSep(sep, "COMMAND_FLYMODE");
 				int8 val = atoi(sep->arg[0]);
-				packet->setDataByName("parameter5", 32);
-				packet->setDataByName("value", val);
-				client->QueuePacket(packet->serialize());
-
-				client->Message(CHANNEL_STATUS, "Flymode %s", val == 1 ? "on" : "off");
-				/*
-				Some other values for this packet
-				first param:
-				01 flymode
-				02 collisons off
-				04 unknown
-				08 forward movement
-				16 heading movement
-				32 low gravity
-				64 sit
-
-				second
-				2 crouch
-
-
-				third:
-				04 float when trying to jump, no movement
-				08 jump high, no movement
-
-				fourth:
-				04 autorun (fear?)
-				16 moon jumps
-				32 safe fall (float to ground)
-				64 cant move
-
-				fifth:
-				01 die
-				08 hover (fae)
-				32 flymode2?
-
-				*/
-				safe_delete(packet);
+				ClientPacketFunctions::SendFlyMode(client, val);
 			}
 			else{
 				client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Usage ON: /flymode 1");
@@ -1937,6 +1900,12 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 		case COMMAND_LOOT_CORPSE:
 		case COMMAND_LOOT:{
 			Spawn* target = client->GetPlayer()->GetTarget();
+			if (((Entity*)target)->IsNPC() && target->Alive())
+			{
+				client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Your target is not dead.");
+				break;
+			}
+
 			if(target && target->IsEntity()){
 				if (target->GetDistance(client->GetPlayer()) <= 10){
 					client->Loot((Entity*)target);
@@ -2672,6 +2641,9 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 			sint8 val = -1;
 			if(sep && sep->arg[0] && sep->IsNumber(0)){
 				val = atoi(sep->arg[0]);
+				
+				database.insertCharacterProperty(client, CHAR_PROPERTY_INVUL, (val == 1) ? "1" : "0");
+
 				client->GetPlayer()->SetInvulnerable(val==1);
 				if(client->GetPlayer()->GetInvulnerable())
 					client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are now invulnerable!");
@@ -7014,9 +6986,15 @@ void Commands::Command_SpawnTemplate(Client* client, Seperator* sep)
 void Commands::Command_Speed(Client* client, Seperator* sep) {
 	if(sep && sep->arg[0][0] && sep->IsNumber(0)){
 		float new_speed = atof(sep->arg[0]);
-		client->GetPlayer()->SetSpeed(new_speed);
-		client->GetPlayer()->SetCharSheetChanged(true);
-		client->Message(CHANNEL_STATUS, "Setting speed to %.2f.", new_speed);
+		if (new_speed > 0.0f)
+		{
+			client->GetPlayer()->SetSpeed(new_speed);
+			client->GetPlayer()->SetCharSheetChanged(true);
+			database.insertCharacterProperty(client, CHAR_PROPERTY_SPEED, sep->arg[0]);
+			client->Message(CHANNEL_STATUS, "Setting speed to %.2f.", new_speed);
+		}
+		else
+			client->Message(CHANNEL_STATUS, "Invalid speed provided %s.", sep->arg[0]);
 	}
 	else{
 		client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Usage: /speed {new speed value}");

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

@@ -135,7 +135,6 @@
 #define HISTORY_SUBTYPE_ITEM		5
 #define HISTORY_SUBTYPE_LOCATION	6
 
-
 /// <summary>Character history data, should match the `character_history` table in the DB</summary>
 struct HistoryData {
 	int32		Value;

+ 58 - 0
EQ2/source/WorldServer/WorldDatabase.cpp

@@ -43,6 +43,7 @@ along with EQ2Emulator.  If not, see <http://www.gnu.org/licenses/>.
 #include "Titles.h"
 #include "Languages.h"
 #include "Traits/Traits.h"
+#include "ClientPacketFunctions.h"
 
 extern Classes classes;
 extern Commands commands;
@@ -1605,6 +1606,63 @@ bool WorldDatabase::UpdateCharacterTimeStamp(int32 account_id, int32 character_i
 	return true;
 }
 
+bool WorldDatabase::insertCharacterProperty(Client* client, char* propName, char* propValue) {
+	Query query;
+
+	string update_status = string("update charactersProperties set propvalue='%s' where charid=%i and propname='%s'");
+	query.RunQuery2(Q_UPDATE, update_status.c_str(), propValue, client->GetCharacterID(), propName);
+	if (!query.GetAffectedRows())
+	{
+		query.RunQuery2(Q_UPDATE, "insert into charactersProperties (charid, propname, propvalue) values(%i, '%s', '%s')", client->GetCharacterID(), propName, propValue);
+		if (query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF) {
+			LogWrite(WORLD__ERROR, 0, "World", "Error in insertCharacterProperty query '%s': %s", query.GetQuery(), query.GetError());
+			return false;
+		}
+	}
+	return true;
+}
+
+bool WorldDatabase::loadCharacterProperties(Client* client) {
+	Query query;
+	MYSQL_ROW row;
+	int32 id = 0;
+	MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT propname, propvalue FROM charactersProperties where charid = %i", client->GetCharacterID());
+	// no character found
+	if (result == NULL) {
+		LogWrite(PLAYER__ERROR, 0, "Player", "Error loading character properties for '%s'", client->GetPlayer()->GetName());
+		return false;
+	}
+
+	while (result && (row = mysql_fetch_row(result))) {
+		char* prop_name = row[0];
+		char* prop_value = row[1];
+
+		if (!prop_name || !prop_value)
+			continue;
+
+		if (!stricmp(prop_name, CHAR_PROPERTY_SPEED))
+		{
+			float new_speed = atof(prop_value);
+			client->GetPlayer()->SetSpeed(new_speed);
+			client->GetPlayer()->SetCharSheetChanged(true);
+		}
+		else if (!stricmp(prop_name, CHAR_PROPERTY_FLYMODE))
+		{
+			int8 flymode = atoi(prop_value);
+			ClientPacketFunctions::SendFlyMode(client, flymode, false);
+		}
+		else if (!stricmp(prop_name, CHAR_PROPERTY_INVUL))
+		{
+			int8 invul = atoi(prop_value);
+			client->GetPlayer()->SetInvulnerable(invul == 1);
+			if (client->GetPlayer()->GetInvulnerable())
+				client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are now invulnerable!");
+		}
+	}
+
+	return true;
+}
+
 //gets the name FROM the db with the right letters in caps
 string WorldDatabase::GetPlayerName(char* name){
 	Query query;

+ 6 - 0
EQ2/source/WorldServer/WorldDatabase.h

@@ -101,6 +101,10 @@ using namespace std;
 #define APPEARANCE_LT			46
 #define APPEARANCE_BODY_AGE		47
 
+#define CHAR_PROPERTY_SPEED			"modify_speed"
+#define CHAR_PROPERTY_FLYMODE		"modify_flymode"
+#define CHAR_PROPERTY_INVUL			"modify_invul"
+
 
 struct StartingItem{
 	string	type;
@@ -277,6 +281,8 @@ public:
 	bool	LoadCharacterStats(int32 id, int32 account_id, Client* client);
 	bool	InsertCharacterStats(int32 character_id, int8 class_id, int8 race_id);
 	bool	UpdateCharacterTimeStamp(int32 account_id, int32 character_id, int32 timestamp);
+	bool	insertCharacterProperty(Client* client, char* propName, char* propValue);
+	bool	loadCharacterProperties(Client* client);
 	string	GetPlayerName(char* name);
 	int32	GetCharacterTimeStamp(int32 character_id, int32 account_id,bool* char_exists);
 	int32	GetCharacterTimeStamp(int32 character_id);

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

@@ -937,6 +937,8 @@ bool Client::HandlePacket(EQApplicationPacket *app) {
 						ClientPacketFunctions::SendLoginDenied( this );
 					}
 					zone_auth.RemoveAuth(zar);
+
+					database.loadCharacterProperties(this);
 				}
 				else
 				{
@@ -2107,7 +2109,7 @@ void Client::HandleLoot(EQApplicationPacket* app){
 			Loot(0, player->GetPendingLootItems(loot_id), (Entity*)spawn);
 		}
 		else{
-			if(spawn && spawn->IsNPC() && ((NPC*)spawn)->Brain()->CheckLootAllowed(player)){
+			if(spawn && !spawn->Alive() && spawn->IsNPC() && ((NPC*)spawn)->Brain()->CheckLootAllowed(player)){
 				if(loot_all){
 					while(loot_all && ((item_id = ((Entity*)spawn)->GetLootItemID()) > 0)){
 						loot_all = HandleLootItem((Entity*)spawn, item_id);