Browse Source

SpawnSet given more color options

Fix #265 fields added:

hair_color1, hair_color2, hair_type_color, hair_face_color, hair_type_highlight_color, face_hairlight_color, hair_highlight, model_color, eye_color
soga_skin_color, soga_hair_color1, soga_hair_color2, soga_hair_type_color, soga_hair_face_color, soga_hair_type_highlight_color, soga_face_hairlight_color, soga_hair_highlight, soga_model_color, soga_eye_color

SpawnSet(Spawn, "fieldname", "R G B")

SpawnSet also set to always be temporary unless 5th argument is set to false.
Image 3 years ago
parent
commit
611456cb06

+ 10 - 0
EQ2/source/WorldServer/Bots/BotDB.cpp

@@ -35,6 +35,7 @@ int32 WorldDatabase::CreateNewBot(int32 char_id, string name, int8 race, int8 ad
 
 void WorldDatabase::SaveBotAppearance(Bot* bot) {
 	SaveBotColors(bot->BotID, "skin_color", bot->features.skin_color);
+	SaveBotColors(bot->BotID, "model_color", bot->features.model_color);
 	SaveBotColors(bot->BotID, "eye_color", bot->features.eye_color);
 	SaveBotColors(bot->BotID, "hair_color1", bot->features.hair_color1);
 	SaveBotColors(bot->BotID, "hair_color2", bot->features.hair_color2);
@@ -62,6 +63,7 @@ void WorldDatabase::SaveBotAppearance(Bot* bot) {
 	SaveBotFloats(bot->BotID, "body_age", bot->features.body_age, 0, 0);
 
 	SaveBotColors(bot->BotID, "soga_skin_color", bot->features.soga_skin_color);
+	SaveBotColors(bot->BotID, "soga_model_color", bot->features.soga_model_color);
 	SaveBotColors(bot->BotID, "soga_eye_color", bot->features.soga_eye_color);
 	SaveBotColors(bot->BotID, "soga_hair_color1", bot->features.soga_hair_color1);
 	SaveBotColors(bot->BotID, "soga_hair_color2", bot->features.soga_hair_color2);
@@ -369,6 +371,14 @@ void WorldDatabase::LoadBotAppearance(Bot* bot) {
 			bot->features.body_age = color.red;
 			break;
 		}
+		case APPEARANCE_MC:{
+			bot->features.model_color = color;
+			break;
+		}
+		case APPEARANCE_SMC:{
+			bot->features.soga_model_color = color;
+			break;
+		}
 		}
 	}
 }

+ 243 - 13
EQ2/source/WorldServer/Commands/Commands.cpp

@@ -156,6 +156,27 @@ Commands::Commands(){
 	spawn_set_values["merchant_max_level"] = SPAWN_SET_VALUE_MERCHANT_MAX_LEVEL;
 	spawn_set_values["skin_color"] = SPAWN_SET_SKIN_COLOR;
 	spawn_set_values["aaxp_rewards"] = SPAWN_SET_AAXP_REWARDS;
+	
+	spawn_set_values["hair_color1"] = SPAWN_SET_HAIR_COLOR1;
+	spawn_set_values["hair_color2"] = SPAWN_SET_HAIR_COLOR2;
+	spawn_set_values["hair_type_color"] = SPAWN_SET_HAIR_TYPE_COLOR;
+	spawn_set_values["hair_face_color"] = SPAWN_SET_HAIR_FACE_COLOR;
+	spawn_set_values["hair_type_highlight_color"] = SPAWN_SET_HAIR_TYPE_HIGHLIGHT_COLOR;
+	spawn_set_values["face_hairlight_color"] = SPAWN_SET_HAIR_FACE_HIGHLIGHT_COLOR;
+	spawn_set_values["hair_highlight"] = SPAWN_SET_HAIR_HIGHLIGHT;
+	spawn_set_values["model_color"] = SPAWN_SET_MODEL_COLOR;
+	spawn_set_values["eye_color"] = SPAWN_SET_EYE_COLOR;
+	
+	spawn_set_values["soga_skin_color"] = SPAWN_SET_SOGA_SKIN_COLOR;
+	spawn_set_values["soga_hair_color1"] = SPAWN_SET_SOGA_HAIR_COLOR1;
+	spawn_set_values["soga_hair_color2"] = SPAWN_SET_SOGA_HAIR_COLOR2;
+	spawn_set_values["soga_hair_type_color"] = SPAWN_SET_SOGA_HAIR_TYPE_COLOR;
+	spawn_set_values["soga_hair_face_color"] = SPAWN_SET_SOGA_HAIR_FACE_COLOR;
+	spawn_set_values["soga_hair_type_highlight_color"] = SPAWN_SET_SOGA_HAIR_TYPE_HIGHLIGHT_COLOR;
+	spawn_set_values["soga_face_hairlight_color"] = SPAWN_SET_SOGA_HAIR_FACE_HIGHLIGHT_COLOR;
+	spawn_set_values["soga_hair_highlight"] = SPAWN_SET_SOGA_HAIR_HIGHLIGHT;
+	spawn_set_values["soga_model_color"] = SPAWN_SET_SOGA_MODEL_COLOR;
+	spawn_set_values["soga_eye_color"] = SPAWN_SET_SOGA_EYE_COLOR;
 
 	zone_set_values["expansion_id"] = ZONE_SET_VALUE_EXPANSION_ID;
 	zone_set_values["name"] = ZONE_SET_VALUE_NAME;
@@ -200,9 +221,34 @@ bool Commands::SetSpawnCommand(Client* client, Spawn* target, int8 type, const c
 	int32 val = 0;
 	try{
 		if(type != SPAWN_SET_VALUE_NAME && 
-			!(type >= SPAWN_SET_VALUE_SPAWN_SCRIPT && type <= SPAWN_SET_VALUE_SUB_TITLE) && !(type >= SPAWN_SET_VALUE_PREFIX && type <= SPAWN_SET_VALUE_EXPANSION_FLAG || type == SPAWN_SET_VALUE_HOLIDAY_FLAG)
-			&& type != SPAWN_SET_SKIN_COLOR)
-			val = atoul(value);
+			!(type >= SPAWN_SET_VALUE_SPAWN_SCRIPT && type <= SPAWN_SET_VALUE_SUB_TITLE) && !(type >= SPAWN_SET_VALUE_PREFIX && type <= SPAWN_SET_VALUE_EXPANSION_FLAG || type == SPAWN_SET_VALUE_HOLIDAY_FLAG))
+			{
+				switch(type)
+				{
+					case SPAWN_SET_SKIN_COLOR:
+					case SPAWN_SET_HAIR_COLOR1:
+					case SPAWN_SET_HAIR_COLOR2:
+					case SPAWN_SET_HAIR_TYPE_COLOR:
+					case SPAWN_SET_HAIR_FACE_COLOR:
+					case SPAWN_SET_HAIR_TYPE_HIGHLIGHT_COLOR:
+					case SPAWN_SET_HAIR_FACE_HIGHLIGHT_COLOR:
+					case SPAWN_SET_HAIR_HIGHLIGHT:
+					case SPAWN_SET_EYE_COLOR:
+					case SPAWN_SET_SOGA_SKIN_COLOR:
+					case SPAWN_SET_SOGA_HAIR_COLOR1:
+					case SPAWN_SET_SOGA_HAIR_COLOR2:
+					case SPAWN_SET_SOGA_HAIR_TYPE_COLOR:
+					case SPAWN_SET_SOGA_HAIR_FACE_COLOR:
+					case SPAWN_SET_SOGA_HAIR_TYPE_HIGHLIGHT_COLOR:
+					case SPAWN_SET_SOGA_HAIR_FACE_HIGHLIGHT_COLOR:
+					case SPAWN_SET_SOGA_HAIR_HIGHLIGHT:
+					case SPAWN_SET_SOGA_EYE_COLOR:
+					// ignore these are colors can't pass as a integer value
+						break;
+					default:
+						val = atoul(value);
+				}
+			}
 	}
 	catch(...){
 		if(client)
@@ -509,8 +555,28 @@ bool Commands::SetSpawnCommand(Client* client, Spawn* target, int8 type, const c
 				target->SetMerchantLevelRange(target->GetMerchantMinLevel(), atoul(value));
 				break;
 			}
-			case SPAWN_SET_SKIN_COLOR: {
-				if (target->IsNPC())
+			case SPAWN_SET_SKIN_COLOR:
+			case SPAWN_SET_HAIR_COLOR1:
+			case SPAWN_SET_HAIR_COLOR2:
+			case SPAWN_SET_HAIR_TYPE_COLOR:
+			case SPAWN_SET_HAIR_FACE_COLOR:
+			case SPAWN_SET_HAIR_TYPE_HIGHLIGHT_COLOR:
+			case SPAWN_SET_HAIR_FACE_HIGHLIGHT_COLOR:
+			case SPAWN_SET_HAIR_HIGHLIGHT:
+			case SPAWN_SET_MODEL_COLOR:
+			case SPAWN_SET_EYE_COLOR:
+			case SPAWN_SET_SOGA_SKIN_COLOR:
+			case SPAWN_SET_SOGA_HAIR_COLOR1:
+			case SPAWN_SET_SOGA_HAIR_COLOR2:
+			case SPAWN_SET_SOGA_HAIR_TYPE_COLOR:
+			case SPAWN_SET_SOGA_HAIR_FACE_COLOR:
+			case SPAWN_SET_SOGA_HAIR_TYPE_HIGHLIGHT_COLOR:
+			case SPAWN_SET_SOGA_HAIR_FACE_HIGHLIGHT_COLOR:
+			case SPAWN_SET_SOGA_HAIR_HIGHLIGHT:
+			case SPAWN_SET_SOGA_MODEL_COLOR:
+			case SPAWN_SET_SOGA_EYE_COLOR:
+					{
+				if (target->IsEntity())
 				{
 					Seperator* skinsep = new Seperator(value, ' ', 3, 500, true);
 					if (skinsep->IsNumber(0) && skinsep->IsNumber(1) && skinsep->IsNumber(2))
@@ -520,7 +586,69 @@ bool Commands::SetSpawnCommand(Client* client, Spawn* target, int8 type, const c
 						clr.green = atoul(skinsep->arg[1]);
 						clr.blue = atoul(skinsep->arg[2]);
 
-						((Entity*)target)->SetSkinColor(clr);
+						switch(type)
+						{
+							case SPAWN_SET_SKIN_COLOR:
+								((Entity*)target)->SetSkinColor(clr);
+							break;
+							case SPAWN_SET_HAIR_COLOR1:
+								((Entity*)target)->SetHairColor1(clr);
+							break;
+							case SPAWN_SET_HAIR_COLOR2:
+								((Entity*)target)->SetHairColor2(clr);
+							break;
+							case SPAWN_SET_HAIR_TYPE_COLOR:
+								((Entity*)target)->SetHairColor(clr);
+							break;
+							case SPAWN_SET_HAIR_FACE_COLOR:
+								((Entity*)target)->SetFacialHairColor(clr);
+							break;
+							case SPAWN_SET_HAIR_TYPE_HIGHLIGHT_COLOR:
+								((Entity*)target)->SetHairTypeHighlightColor(clr);
+							break;
+							case SPAWN_SET_HAIR_FACE_HIGHLIGHT_COLOR:
+								((Entity*)target)->SetFacialHairHighlightColor(clr);
+							break;
+							case SPAWN_SET_HAIR_HIGHLIGHT:
+								((Entity*)target)->SetHairHighlightColor(clr);
+							break;
+							case SPAWN_SET_MODEL_COLOR:
+								((Entity*)target)->SetModelColor(clr);
+							break;
+							case SPAWN_SET_EYE_COLOR:
+								((Entity*)target)->SetEyeColor(clr);
+							break;
+							case SPAWN_SET_SOGA_SKIN_COLOR:
+								((Entity*)target)->SetSogaSkinColor(clr);
+							break;
+							case SPAWN_SET_SOGA_HAIR_COLOR1:
+								((Entity*)target)->SetSogaHairColor1(clr);
+							break;
+							case SPAWN_SET_SOGA_HAIR_COLOR2:
+								((Entity*)target)->SetSogaHairColor2(clr);
+							break;
+							case SPAWN_SET_SOGA_HAIR_TYPE_COLOR:
+								((Entity*)target)->SetSogaHairColor(clr);
+							break;
+							case SPAWN_SET_SOGA_HAIR_FACE_COLOR:
+								((Entity*)target)->SetSogaFacialHairColor(clr);
+							break;
+							case SPAWN_SET_SOGA_HAIR_TYPE_HIGHLIGHT_COLOR:
+								((Entity*)target)->SetSogaHairTypeHighlightColor(clr);
+							break;
+							case SPAWN_SET_SOGA_HAIR_FACE_HIGHLIGHT_COLOR:
+								((Entity*)target)->SetSogaFacialHairHighlightColor(clr);
+							break;
+							case SPAWN_SET_SOGA_HAIR_HIGHLIGHT:
+								((Entity*)target)->SetSogaHairHighlightColor(clr);
+							break;
+							case SPAWN_SET_SOGA_MODEL_COLOR:
+								((Entity*)target)->SetSogaModelColor(clr);
+							break;
+							case SPAWN_SET_SOGA_EYE_COLOR:
+								((Entity*)target)->SetSogaEyeColor(clr);
+							break;
+						}
 					}
 					safe_delete(skinsep);
 				}
@@ -892,7 +1020,26 @@ bool Commands::SetSpawnCommand(Client* client, Spawn* target, int8 type, const c
 				break;
 			}
 
-			case SPAWN_SET_SKIN_COLOR: {
+			case SPAWN_SET_SKIN_COLOR:
+			case SPAWN_SET_HAIR_COLOR1:
+			case SPAWN_SET_HAIR_COLOR2:
+			case SPAWN_SET_HAIR_TYPE_COLOR:
+			case SPAWN_SET_HAIR_FACE_COLOR:
+			case SPAWN_SET_HAIR_TYPE_HIGHLIGHT_COLOR:
+			case SPAWN_SET_HAIR_FACE_HIGHLIGHT_COLOR:
+			case SPAWN_SET_HAIR_HIGHLIGHT:
+			case SPAWN_SET_MODEL_COLOR:
+			case SPAWN_SET_EYE_COLOR:
+			case SPAWN_SET_SOGA_SKIN_COLOR:
+			case SPAWN_SET_SOGA_HAIR_COLOR1:
+			case SPAWN_SET_SOGA_HAIR_COLOR2:
+			case SPAWN_SET_SOGA_HAIR_TYPE_COLOR:
+			case SPAWN_SET_SOGA_HAIR_FACE_COLOR:
+			case SPAWN_SET_SOGA_HAIR_TYPE_HIGHLIGHT_COLOR:
+			case SPAWN_SET_SOGA_HAIR_FACE_HIGHLIGHT_COLOR:
+			case SPAWN_SET_SOGA_HAIR_HIGHLIGHT:
+			case SPAWN_SET_SOGA_MODEL_COLOR:
+			case SPAWN_SET_SOGA_EYE_COLOR: {
 				if (target->IsNPC())
 				{
 					Seperator* skinsep = new Seperator(value, ' ', 3, 500, true);
@@ -902,11 +1049,94 @@ bool Commands::SetSpawnCommand(Client* client, Spawn* target, int8 type, const c
 						clr.red = atoul(skinsep->arg[0]);
 						clr.green = atoul(skinsep->arg[1]);
 						clr.blue = atoul(skinsep->arg[2]);
-
-						((Entity*)target)->SetSkinColor(clr);
 						Query replaceSkinQuery;
-						replaceSkinQuery.AddQueryAsync(0, &database, Q_DELETE, "delete from npc_appearance where spawn_id=%u and type='skin_color'", target->GetDatabaseID());
-						replaceSkinQuery.AddQueryAsync(0, &database, Q_INSERT, "insert into npc_appearance set spawn_id=%u, type='skin_color', red=%u, green=%u, blue=%u", target->GetDatabaseID(), clr.red, clr.green, clr.blue);
+
+						string fieldName("");
+						switch(type)
+						{
+							case SPAWN_SET_SKIN_COLOR:
+								((Entity*)target)->SetSkinColor(clr);
+								fieldName.append("skin_color");
+								break;
+							case SPAWN_SET_HAIR_COLOR1:
+								((Entity*)target)->SetHairColor1(clr);
+								fieldName.append("hair_color1");
+								break;
+							case SPAWN_SET_HAIR_COLOR2:
+								((Entity*)target)->SetHairColor2(clr);
+								fieldName.append("hair_color2");
+								break;
+							case SPAWN_SET_HAIR_TYPE_COLOR:
+								((Entity*)target)->SetHairColor(clr);
+								fieldName.append("hair_type_color");
+								break;
+							case SPAWN_SET_HAIR_FACE_COLOR:
+								((Entity*)target)->SetFacialHairColor(clr);
+								fieldName.append("hair_face_color");
+								break;
+							case SPAWN_SET_HAIR_TYPE_HIGHLIGHT_COLOR:
+								((Entity*)target)->SetHairTypeHighlightColor(clr);
+								fieldName.append("hair_type_highlight_color");
+								break;
+							case SPAWN_SET_HAIR_FACE_HIGHLIGHT_COLOR:
+								((Entity*)target)->SetFacialHairHighlightColor(clr);
+								fieldName.append("hair_face_highlight_color");
+								break;
+							case SPAWN_SET_HAIR_HIGHLIGHT:
+								((Entity*)target)->SetHairHighlightColor(clr);
+								fieldName.append("hair_highlight");
+								break;
+							case SPAWN_SET_MODEL_COLOR:
+								((Entity*)target)->SetModelColor(clr);
+								fieldName.append("model_color");
+								break;
+							case SPAWN_SET_EYE_COLOR:
+								((Entity*)target)->SetEyeColor(clr);
+								fieldName.append("eye_color");
+								break;
+							case SPAWN_SET_SOGA_SKIN_COLOR:
+								((Entity*)target)->SetSogaSkinColor(clr);
+								fieldName.append("soga_skin_color");
+								break;
+							case SPAWN_SET_SOGA_HAIR_COLOR1:
+								((Entity*)target)->SetSogaHairColor1(clr);
+								fieldName.append("soga_hair_color1");
+								break;
+							case SPAWN_SET_SOGA_HAIR_COLOR2:
+								((Entity*)target)->SetSogaHairColor2(clr);
+								fieldName.append("soga_hair_color2");
+								break;
+							case SPAWN_SET_SOGA_HAIR_TYPE_COLOR:
+								((Entity*)target)->SetSogaHairColor(clr);
+								fieldName.append("soga_hair_type_color");
+								break;
+							case SPAWN_SET_SOGA_HAIR_FACE_COLOR:
+								((Entity*)target)->SetSogaFacialHairColor(clr);
+								fieldName.append("soga_hair_face_color");
+								break;
+							case SPAWN_SET_SOGA_HAIR_TYPE_HIGHLIGHT_COLOR:
+								((Entity*)target)->SetSogaHairTypeHighlightColor(clr);
+								fieldName.append("soga_hair_type_highlight_color");
+								break;
+							case SPAWN_SET_SOGA_HAIR_FACE_HIGHLIGHT_COLOR:
+								((Entity*)target)->SetSogaFacialHairHighlightColor(clr);
+								fieldName.append("soga_hair_face_highlight_color");
+								break;
+							case SPAWN_SET_SOGA_HAIR_HIGHLIGHT:
+								((Entity*)target)->SetSogaHairHighlightColor(clr);
+								fieldName.append("soga_hair_highlight");
+								break;
+							case SPAWN_SET_SOGA_MODEL_COLOR:
+								((Entity*)target)->SetSogaModelColor(clr);
+								fieldName.append("soga_model_color");
+								break;
+							case SPAWN_SET_SOGA_EYE_COLOR:
+								((Entity*)target)->SetSogaEyeColor(clr);
+								fieldName.append("soga_eye_color");
+								break;
+						}
+						replaceSkinQuery.AddQueryAsync(0, &database, Q_DELETE, "delete from npc_appearance where spawn_id=%u and type='%s'", target->GetDatabaseID(), fieldName.c_str());
+						replaceSkinQuery.AddQueryAsync(0, &database, Q_INSERT, "insert into npc_appearance set spawn_id=%u, type='%s', red=%u, green=%u, blue=%u", target->GetDatabaseID(), fieldName.c_str(), clr.red, clr.green, clr.blue);
 					}
 					safe_delete(skinsep);
 				}
@@ -3910,9 +4140,9 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 							{
 								client->SimpleMessage(CHANNEL_COLOR_YELLOW, "New name will not be effective until zone reload.");
 							}
-							else if (set_type == SPAWN_SET_SKIN_COLOR)
+							else if (set_type == SPAWN_SET_SKIN_COLOR || (set_type >= SPAWN_SET_HAIR_COLOR1 && set_type <= SPAWN_SET_SOGA_EYE_COLOR))
 							{
-								client->Message(CHANNEL_COLOR_YELLOW, "Successfully set skin_color to R G B: %s.", sep->argplus[1]);
+								client->Message(CHANNEL_COLOR_YELLOW, "Successfully set color field to R G B: %s.", sep->argplus[1]);
 							}
 							else if(set_type == SPAWN_SET_VALUE_LOCATION)
 							{

+ 20 - 0
EQ2/source/WorldServer/Commands/Commands.h

@@ -521,6 +521,26 @@ private:
 #define SPAWN_SET_SKIN_COLOR				63
 #define SPAWN_SET_AAXP_REWARDS				64
 
+#define SPAWN_SET_HAIR_COLOR1							65
+#define SPAWN_SET_HAIR_COLOR2							66
+#define SPAWN_SET_HAIR_TYPE_COLOR						67
+#define SPAWN_SET_HAIR_FACE_COLOR						68
+#define SPAWN_SET_HAIR_TYPE_HIGHLIGHT_COLOR				69
+#define SPAWN_SET_HAIR_FACE_HIGHLIGHT_COLOR				70
+#define SPAWN_SET_HAIR_HIGHLIGHT						71
+#define SPAWN_SET_MODEL_COLOR							72
+#define SPAWN_SET_EYE_COLOR								73
+#define SPAWN_SET_SOGA_SKIN_COLOR						74
+#define SPAWN_SET_SOGA_HAIR_COLOR1						75
+#define SPAWN_SET_SOGA_HAIR_COLOR2						76
+#define SPAWN_SET_SOGA_HAIR_TYPE_COLOR					77
+#define SPAWN_SET_SOGA_HAIR_FACE_COLOR					78
+#define SPAWN_SET_SOGA_HAIR_TYPE_HIGHLIGHT_COLOR		79
+#define SPAWN_SET_SOGA_HAIR_FACE_HIGHLIGHT_COLOR		80
+#define SPAWN_SET_SOGA_HAIR_HIGHLIGHT					81
+#define SPAWN_SET_SOGA_MODEL_COLOR						82
+#define SPAWN_SET_SOGA_EYE_COLOR						83
+
 #define ZONE_SET_VALUE_EXPANSION_ID			0
 #define ZONE_SET_VALUE_NAME					1
 #define ZONE_SET_VALUE_FILE					2

+ 12 - 1
EQ2/source/WorldServer/Spawn.cpp

@@ -120,6 +120,7 @@ Spawn::Spawn(){
 	current_map = nullptr;
 	RegionMutex.SetName("Spawn::RegionMutex");
 	pause_timer.Disable();
+	m_SpawnMutex.SetName("Spawn::SpawnMutex");
 }
 
 Spawn::~Spawn(){
@@ -2421,6 +2422,7 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) {
 			packet->setDataByName("soga_nose_type", entity->features.soga_nose_type[i], i);
 		}
 		packet->setColorByName("skin_color", entity->features.skin_color);
+		packet->setColorByName("model_color", entity->features.model_color);
 		packet->setColorByName("eye_color", entity->features.eye_color);
 		packet->setColorByName("hair_type_color", entity->features.hair_type_color);
 		packet->setColorByName("hair_type_highlight_color", entity->features.hair_type_highlight_color);
@@ -2432,6 +2434,7 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) {
 		packet->setColorByName("hair_color1", entity->features.hair_color1);
 		packet->setColorByName("hair_color2", entity->features.hair_color2);
 		packet->setColorByName("soga_skin_color", entity->features.soga_skin_color);
+		packet->setColorByName("soga_model_color", entity->features.soga_model_color);
 		packet->setColorByName("soga_eye_color", entity->features.soga_eye_color);
 		packet->setColorByName("soga_hair_color1", entity->features.soga_hair_color1);
 		packet->setColorByName("soga_hair_color2", entity->features.soga_hair_color2);
@@ -2449,8 +2452,10 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) {
 		empty.blue = 255;
 		empty.green = 255;
 		packet->setColorByName("skin_color", empty);
+		packet->setColorByName("model_color", empty);
 		packet->setColorByName("eye_color", empty);
 		packet->setColorByName("soga_skin_color", empty);
+		packet->setColorByName("soga_model_color", empty);
 		packet->setColorByName("soga_eye_color", empty);
 	}
 	if (appearance.icon == 0) {
@@ -3434,11 +3439,17 @@ void Spawn::RemoveSpawnFromGroup(bool erase_all){
 }
 
 void Spawn::SetSpawnGroupID(int32 id){
+	m_SpawnMutex.writelock();
 	group_id = id;
+	m_SpawnMutex.releasewritelock();
 }
 
 int32 Spawn::GetSpawnGroupID(){
-	return group_id;
+	int32 groupid = 0;
+	m_SpawnMutex.readlock();
+	groupid = group_id;
+	m_SpawnMutex.releasereadlock();
+	return groupid;
 }
 
 void Spawn::AddChangedZoneSpawn(){

+ 1 - 1
EQ2/source/WorldServer/Spawn.h

@@ -1274,7 +1274,7 @@ private:
 	int16 m_spawnAnimLeeway;
 
 	Mutex m_Update;
-
+	Mutex m_SpawnMutex;
 	bool disable_sounds;
 
 	RegionMap* region_map;

+ 27 - 2
EQ2/source/WorldServer/WorldDatabase.cpp

@@ -641,6 +641,10 @@ int8 WorldDatabase::GetAppearanceType(string type){
 		ret = APPEARANCE_SOGA_U13;
 	else if (type == "body_age")
 		ret = APPEARANCE_BODY_AGE;
+	else if (type == "model_color")
+		ret = APPEARANCE_MC;
+	else if (type == "soga_model_color")
+		ret = APPEARANCE_SMC;
 	return ret;
 }
 
@@ -889,7 +893,16 @@ int32 WorldDatabase::LoadAppearances(ZoneServer* zone, Client* client){
 				entity->features.body_age = color.red;
 				break;
 			}
+			case APPEARANCE_MC:{
+				entity->features.model_color = color;
+				break;
+			}
+			case APPEARANCE_SMC:{
+				entity->features.soga_model_color = color;
+				break;
+			}
 		}
+		entity->info_changed = true;
 	}
 	return count;
 }
@@ -2254,6 +2267,7 @@ int32 WorldDatabase::SaveCharacter(PacketStruct* create, int32 loginID){
 	AddNewPlayerToServerGuild(loginID, char_id);
 
 	SaveCharacterColors(char_id,"skin_color", create->getType_EQ2_Color_ByName("skin_color"));
+	SaveCharacterColors(char_id,"model_color", create->getType_EQ2_Color_ByName("model_color"));
 	SaveCharacterColors(char_id,"eye_color", create->getType_EQ2_Color_ByName("eye_color"));
 	SaveCharacterColors(char_id,"hair_color1", create->getType_EQ2_Color_ByName("hair_color1"));
 	SaveCharacterColors(char_id,"hair_color2", create->getType_EQ2_Color_ByName("hair_color2"));
@@ -2279,6 +2293,7 @@ int32 WorldDatabase::SaveCharacter(PacketStruct* create, int32 loginID){
 	SaveCharacterFloats(char_id,"body_size", create->getType_float_ByName("body_size",0), 0, 0);
 
 	SaveCharacterColors(char_id,"soga_skin_color", create->getType_EQ2_Color_ByName("soga_skin_color"));
+	SaveCharacterColors(char_id,"soga_model_color", create->getType_EQ2_Color_ByName("soga_model_color"));
 	SaveCharacterColors(char_id,"soga_eye_color", create->getType_EQ2_Color_ByName("soga_eye_color"));
 	SaveCharacterColors(char_id,"soga_hair_color1", create->getType_EQ2_Color_ByName("soga_hair_color1"));
 	SaveCharacterColors(char_id,"soga_hair_color2", create->getType_EQ2_Color_ByName("soga_hair_color2"));
@@ -3918,10 +3933,10 @@ void WorldDatabase::Save(Client* client){
 	if(client->GetCurrentZone())
 		zone_id = client->GetCurrentZone()->GetZoneID();
 	query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update characters set current_zone_id=%u, x=%f, y=%f, z=%f, heading=%f, level=%i,instance_id=%i,last_saved=%i, `class`=%i, `tradeskill_level`=%i, `tradeskill_class`=%i, `group_id`=%u where id = %u", zone_id, player->GetX(), player->GetY(), player->GetZ(), player->GetHeading(), player->GetLevel(), instance_id, client->GetLastSavedTimeStamp(), client->GetPlayer()->GetAdventureClass(), client->GetPlayer()->GetTSLevel(), client->GetPlayer()->GetTradeskillClass(), client->GetPlayer()->GetGroupMemberInfo() ? client->GetPlayer()->GetGroupMemberInfo()->group_id : 0, client->GetCharacterID());
-	query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update character_details set hp=%u, power=%u, str=%i, sta=%i, agi=%i, wis=%i, intel=%i, heat=%i, cold=%i, magic=%i, mental=%i, divine=%i, disease=%i, poison=%i, coin_copper=%u, coin_silver=%u, coin_gold=%u, coin_plat=%u, max_hp = %u, max_power=%u, xp = %u, xp_needed = %u, xp_debt = %f, xp_vitality = %f, tradeskill_xp = %u, tradeskill_xp_needed = %u, tradeskill_xp_vitality = %f, bank_copper = %u, bank_silver = %u, bank_gold = %u, bank_plat = %u, bind_zone_id=%u, bind_x = %f, bind_y = %f, bind_z = %f, bind_heading = %f, house_zone_id=%u, combat_voice = %i, emote_voice = %i, biography='%s', flags=%u, flags2=%u, last_name='%s', assigned_aa = %i, unassigned_aa = %i, tradeskill_aa = %i, unassigned_tradeskill_aa = %i, prestige_aa = %i, unassigned_prestige_aa = %i, tradeskill_prestige_aa = %i, unassigned_tradeskill_prestige_aa = %i where char_id = %u",
+	query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update character_details set hp=%u, power=%u, str=%i, sta=%i, agi=%i, wis=%i, intel=%i, heat=%i, cold=%i, magic=%i, mental=%i, divine=%i, disease=%i, poison=%i, coin_copper=%u, coin_silver=%u, coin_gold=%u, coin_plat=%u, max_hp = %u, max_power=%u, xp = %u, xp_needed = %u, xp_debt = %f, xp_vitality = %f, tradeskill_xp = %u, tradeskill_xp_needed = %u, tradeskill_xp_vitality = %f, bank_copper = %u, bank_silver = %u, bank_gold = %u, bank_plat = %u, status_points = %u, bind_zone_id=%u, bind_x = %f, bind_y = %f, bind_z = %f, bind_heading = %f, house_zone_id=%u, combat_voice = %i, emote_voice = %i, biography='%s', flags=%u, flags2=%u, last_name='%s', assigned_aa = %i, unassigned_aa = %i, tradeskill_aa = %i, unassigned_tradeskill_aa = %i, prestige_aa = %i, unassigned_prestige_aa = %i, tradeskill_prestige_aa = %i, unassigned_tradeskill_prestige_aa = %i where char_id = %u",
 		player->GetHP(), player->GetPower(), player->GetStrBase(), player->GetStaBase(), player->GetAgiBase(), player->GetWisBase(), player->GetIntBase(), player->GetHeatResistanceBase(), player->GetColdResistanceBase(), player->GetMagicResistanceBase(),
 		player->GetMentalResistanceBase(), player->GetDivineResistanceBase(), player->GetDiseaseResistanceBase(), player->GetPoisonResistanceBase(), player->GetCoinsCopper(), player->GetCoinsSilver(), player->GetCoinsGold(), player->GetCoinsPlat(), player->GetTotalHPBase(), player->GetTotalPowerBase(), player->GetXP(), player->GetNeededXP(), player->GetXPDebt(), player->GetXPVitality(), player->GetTSXP(), player->GetNeededTSXP(), player->GetTSXPVitality(), player->GetBankCoinsCopper(),
-		player->GetBankCoinsSilver(), player->GetBankCoinsGold(), player->GetBankCoinsPlat(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneID(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneX(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneY(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneZ(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneHeading(), client->GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), 
+		player->GetBankCoinsSilver(), player->GetBankCoinsGold(), player->GetBankCoinsPlat(), player->GetStatusPoints(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneID(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneX(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneY(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneZ(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneHeading(), client->GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), 
 		client->GetPlayer()->GetCombatVoice(), client->GetPlayer()->GetEmoteVoice(), getSafeEscapeString(client->GetPlayer()->GetBiography().c_str()).c_str(), player->GetFlags(), player->GetFlags2(), client->GetPlayer()->GetLastName(), 
 		client->GetPlayer()->GetAssignedAA(), client->GetPlayer()->GetUnassignedAA(), client->GetPlayer()->GetTradeskillAA(), client->GetPlayer()->GetUnassignedTradeskillAA(), client->GetPlayer()->GetPrestigeAA(), 
 		client->GetPlayer()->GetUnassignedPretigeAA(), client->GetPlayer()->GetTradeskillPrestigeAA(), client->GetPlayer()->GetUnassignedTradeskillPrestigeAA(), client->GetCharacterID());
@@ -6934,8 +6949,18 @@ void WorldDatabase::LoadAppearance(ZoneServer* zone, int32 spawn_id) {
 			case APPEARANCE_SOGA_U13:{
 				break;
 			}
+			case APPEARANCE_MC:{
+				entity->features.model_color = color;
+				break;
+			}
+			case APPEARANCE_SMC:{
+				entity->features.soga_model_color = color;
+				break;
+			}
 		}
 	}
+
+	entity->info_changed = true;
 }
 
 void WorldDatabase::LoadNPCAppearanceEquipmentData(ZoneServer* zone, int32 spawn_id) {

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

@@ -101,6 +101,8 @@ using namespace std;
 #define APPEARANCE_EYET			45
 #define APPEARANCE_LT			46
 #define APPEARANCE_BODY_AGE		47
+#define APPEARANCE_MC			48
+#define APPEARANCE_SMC			49
 
 #define CHAR_PROPERTY_SPEED			"modify_speed"
 #define CHAR_PROPERTY_FLYMODE		"modify_flymode"

+ 3 - 0
EQ2/source/common/EQ2_Common_Structs.h

@@ -148,6 +148,9 @@ struct CharFeatures{
 	EQ2_Color			soga_hair_face_color;
 	EQ2_Color			soga_hair_face_highlight_color;
 	EQ2_Color			soga_hair_highlight_color;
+	
+	EQ2_Color			model_color;
+	EQ2_Color			soga_model_color;
 };
 struct PositionData{
 	int32			grid_id;

+ 4 - 4
server/SpawnStructs.xml

@@ -2170,10 +2170,10 @@
 <Data ElementName="soga_hair_face_highlight_color" Type="EQ2_Color" />
 <Data ElementName="skin_color" Type="EQ2_Color" Size="1" />
 <Data ElementName="eye_color" Type="EQ2_Color" Size="1" />
-<Data ElementName="kunark_unknown_color1" Type="EQ2_Color" />
-<Data ElementName="soga_eye_color" Type="EQ2_Color" />
+<Data ElementName="model_color" Type="EQ2_Color" />
 <Data ElementName="soga_skin_color" Type="EQ2_Color" />
-<Data ElementName="kunark_unknown_color2" Type="EQ2_Color" />
+<Data ElementName="soga_eye_color" Type="EQ2_Color" />
+<Data ElementName="soga_model_color" Type="EQ2_Color" />
 <Data ElementName="slider_bytes" Type="int8" Size="26" />
 <Data ElementName="soga_slider_bytes" Type="int8" Size="26" />
 <Data ElementName="mount_color" Type="EQ2_Color" />
@@ -2719,4 +2719,4 @@
 <Data ElementName="vis" Substruct="Substruct_SpawnVisualizationInfoStruct" Size="1" />
 <Data ElementName="info" Substruct="Substruct_SpawnInfoStruct" Size="1" />
 </Struct>
-</EQ2Emulator>
+</EQ2Emulator>