Here are at least a couple places where we can try removing the 99 change for features set to 0 and see if that corrects the issues with bald players and features not showing up correctly once and for all:
zone/client_packet.cpp
Code:
void Client::Handle_OP_FaceChange(const EQApplicationPacket *app)
{
if (app->size != sizeof(FaceChange_Struct)) {
LogFile->write(EQEMuLog::Error, "Invalid size for OP_FaceChange: Expected: %i, Got: %i",
sizeof(FaceChange_Struct), app->size);
return;
}
// Notify other clients in zone
entity_list.QueueClients(this, app, false);
FaceChange_Struct* fc = (FaceChange_Struct*)app->pBuffer;
m_pp.haircolor = fc->haircolor;
m_pp.beardcolor = fc->beardcolor;
m_pp.eyecolor1 = fc->eyecolor1;
m_pp.eyecolor2 = fc->eyecolor2;
m_pp.hairstyle = fc->hairstyle;
m_pp.face = fc->face;
// vesuvias - appearence fix
m_pp.beard = fc->beard;
if (fc->face == 0) {m_pp.face = 99;}
if (fc->eyecolor1 == 0) {m_pp.eyecolor1 = 99;}
if (fc->eyecolor2 == 0) {m_pp.eyecolor2 = 99;}
if (fc->hairstyle == 0) {m_pp.hairstyle = 99;}
if (fc->haircolor == 0) {m_pp.haircolor = 99;}
if (fc->beard == 0) {m_pp.beard = 99;}
if (fc->beardcolor == 0) {m_pp.beardcolor = 99;}
Save();
Message_StringID(13,FACE_ACCEPTED);
//Message(13, "Facial features updated.");
return;
}
world/client.cpp
Code:
bool Client::OPCharCreate(char *name, CharCreate_Struct *cc)
{
PlayerProfile_Struct pp;
ExtendedProfile_Struct ext;
Inventory inv;
time_t bday = time(NULL);
char startzone[50]={0};
uint32 i;
struct in_addr in;
int stats_sum = cc->STR + cc->STA + cc->AGI + cc->DEX +
cc->WIS + cc->INT + cc->CHA;
in.s_addr = GetIP();
clog(WORLD__CLIENT,"Character creation request from %s LS#%d (%s:%d) : ", GetCLE()->LSName(), GetCLE()->LSID(), inet_ntoa(in), GetPort());
clog(WORLD__CLIENT,"Name: %s", name);
clog(WORLD__CLIENT,"Race: %d Class: %d Gender: %d Deity: %d Start zone: %d",
cc->race, cc->class_, cc->gender, cc->deity, cc->start_zone);
clog(WORLD__CLIENT,"STR STA AGI DEX WIS INT CHA Total");
clog(WORLD__CLIENT,"%3d %3d %3d %3d %3d %3d %3d %3d",
cc->STR, cc->STA, cc->AGI, cc->DEX, cc->WIS, cc->INT, cc->CHA,
stats_sum);
clog(WORLD__CLIENT,"Face: %d Eye colors: %d %d", cc->face, cc->eyecolor1, cc->eyecolor2);
clog(WORLD__CLIENT,"Hairstyle: %d Haircolor: %d", cc->hairstyle, cc->haircolor);
clog(WORLD__CLIENT,"Beard: %d Beardcolor: %d", cc->beard, cc->beardcolor);
// validate the char creation struct
if(!CheckCharCreateInfo(cc))
{
clog(WORLD__CLIENT_ERR,"CheckCharCreateInfo did not validate the request (bad race/class/stats)");
return false;
}
// Convert incoming cc_s to the new PlayerProfile_Struct
memset(&pp, 0, sizeof(PlayerProfile_Struct)); // start building the profile
InitExtendedProfile(&ext);
strncpy(pp.name, name, 63);
// clean the capitalization of the name
#if 0 // on second thought, don't - this will just make the creation fail
// because the name won't match what was already reserved earlier
for (i = 0; pp.name[i] && i < 63; i++)
{
if(!isalpha(pp.name[i]))
return false;
pp.name[i] = tolower(pp.name[i]);
}
pp.name[0] = toupper(pp.name[0]);
#endif
pp.race = cc->race;
pp.class_ = cc->class_;
pp.gender = cc->gender;
pp.deity = cc->deity;
pp.STR = cc->STR;
pp.STA = cc->STA;
pp.AGI = cc->AGI;
pp.DEX = cc->DEX;
pp.WIS = cc->WIS;
pp.INT = cc->INT;
pp.CHA = cc->CHA;
pp.face = cc->face;
pp.eyecolor1 = cc->eyecolor1;
pp.eyecolor2 = cc->eyecolor2;
pp.hairstyle = cc->hairstyle;
pp.haircolor = cc->haircolor;
pp.beard = cc->beard;
pp.beardcolor = cc->beardcolor;
if (cc->face == 0) {pp.face = 99;}
if (cc->eyecolor1 == 0) {pp.eyecolor1 = 99;}
if (cc->eyecolor2 == 0) {pp.eyecolor2 = 99;}
if (cc->hairstyle == 0) {pp.hairstyle = 99;}
if (cc->haircolor == 0) {pp.haircolor = 99;}
if (cc->beard == 0) {pp.beard = 99;}
if (cc->beardcolor == 0) {pp.beardcolor = 99;}
pp.birthday = bday;
pp.lastlogin = bday;
pp.level = 1;
pp.points = 5;
pp.cur_hp = 1000; // 1k hp during dev only
//what was the point of this? zone dosent handle this:
//pp.expAA = 0xFFFFFFFF;
pp.hunger_level = 6000;
pp.thirst_level = 6000;
// FIXME: FV roleplay, database goodness...
// Racial Languages
SetRacialLanguages( &pp ); // bUsh
SetRaceStartingSkills( &pp ); // bUsh
SetClassStartingSkills( &pp ); // bUsh
pp.skills[SENSE_HEADING] = 200;
// Some one fucking fix this to use a field name. -Doodman
//pp.unknown3596[28] = 15; // @bp: This is to enable disc usage
// strcpy(pp.servername, WorldConfig::get()->ShortName.c_str());
for(i = 0; i < MAX_PP_SPELLBOOK; i++)
pp.spell_book[i] = 0xFFFFFFFF;
for(i = 0; i < MAX_PP_MEMSPELL; i++)
pp.mem_spells[i] = 0xFFFFFFFF;
for(i = 0; i < BUFF_COUNT; i++)
pp.buffs[i].spellid = 0xFFFF;
//was memset(pp.unknown3704, 0xffffffff, 8);
//but I dont think thats what you really wanted to do...
//memset is byte based
//If server is PVP by default, make all character set to it.
pp.pvp = database.GetServerType() == 1 ? 1 : 0;
// if there's a startzone variable put them in there
if(database.GetVariable("startzone", startzone, 50))
{
clog(WORLD__CLIENT,"Found 'startzone' variable setting: %s", startzone);
pp.zone_id = database.GetZoneID(startzone);
if(pp.zone_id)
database.GetSafePoints(pp.zone_id, &pp.x, &pp.y, &pp.z);
else
clog(WORLD__CLIENT_ERR,"Error getting zone id for '%s'", startzone);
}
else // otherwise use normal starting zone logic
{
if(!SoFClient)
database.GetStartZone(&pp, cc);
else
database.GetStartZoneSoF(&pp, cc);
}
if(!pp.zone_id)
{
pp.zone_id = 1; // qeynos
pp.x = pp.y = pp.z = -1;
}
if(!pp.binds[0].zoneId)
{
pp.binds[0].zoneId = pp.zone_id;
pp.binds[0].x = pp.x;
pp.binds[0].y = pp.y;
pp.binds[0].z = pp.z;
pp.binds[0].heading = pp.heading;
}
clog(WORLD__CLIENT,"Current location: %s %0.2f, %0.2f, %0.2f",
database.GetZoneName(pp.zone_id), pp.x, pp.y, pp.z);
clog(WORLD__CLIENT,"Bind location: %s %0.2f, %0.2f, %0.2f",
database.GetZoneName(pp.binds[0].zoneId), pp.binds[0].x, pp.binds[0].y, pp.binds[0].z);
// Starting Items inventory
database.SetStartingItems(&pp, &inv, pp.race, pp.class_, pp.deity, pp.zone_id, pp.name, GetAdmin());
// now we give the pp and the inv we made to StoreCharacter
// to see if we can store it
if (!database.StoreCharacter(GetAccountID(), &pp, &inv, &ext))
{
clog(WORLD__CLIENT_ERR,"Character creation failed: %s", pp.name);
return false;
}
else
{
clog(WORLD__CLIENT,"Character creation successful: %s", pp.name);
return true;
}
}
worlddb.cpp
Code:
void WorldDatabase::GetCharSelectInfo(int32 account_id, CharacterSelect_Struct* cs) {
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
Inventory *inv;
for (int i=0; i<10; i++) {
strcpy(cs->name[i], "<none>");
cs->zone[i] = 0;
cs->level[i] = 0;
cs->tutorial[i] = 0;
cs->gohome[i] = 0;
}
int char_num = 0;
unsigned long* lengths;
// Populate character info
if (RunQuery(query, MakeAnyLenString(&query, "SELECT name,profile,zonename,class,level FROM character_ WHERE account_id=%i order by name limit 10", account_id), errbuf, &result)) {
safe_delete_array(query);
while ((row = mysql_fetch_row(result))) {
lengths = mysql_fetch_lengths(result);
////////////
//////////// This is the current one, the other are for converting
////////////
if ((lengths[1] == sizeof(PlayerProfile_Struct))) {
strcpy(cs->name[char_num], row[0]);
PlayerProfile_Struct* pp = (PlayerProfile_Struct*)row[1];
uint8 clas = atoi(row[3]);
uint8 lvl = atoi(row[4]);
// Character information
if(lvl == 0)
cs->level[char_num] = pp->level; //no level in DB, trust PP
else
cs->level[char_num] = lvl;
if(clas == 0)
cs->class_[char_num] = pp->class_; //no class in DB, trust PP
else
cs->class_[char_num] = clas;
cs->race[char_num] = pp->race;
cs->gender[char_num] = pp->gender;
cs->deity[char_num] = pp->deity;
cs->zone[char_num] = GetZoneID(row[2]);
cs->face[char_num] = pp->face;
cs->haircolor[char_num] = pp->haircolor;
cs->beardcolor[char_num] = pp->beardcolor;
cs->eyecolor2[char_num] = pp->eyecolor2;
cs->eyecolor1[char_num] = pp->eyecolor1;
cs->hair[char_num] = pp->hairstyle;
cs->beard[char_num] = pp->beard;
if (pp->face == 99) {cs->face[char_num] = 0;}
if (pp->eyecolor1 == 99) {cs->eyecolor1[char_num] = 0;}
if (pp->eyecolor2 == 99) {cs->eyecolor2[char_num] = 0;}
if (pp->hairstyle == 99) {cs->hair[char_num] = 0;}
if (pp->haircolor == 99) {cs->haircolor[char_num] = 0;}
if (pp->beard == 99) {cs->beard[char_num] = 0;}
if (pp->beardcolor == 99) {cs->beardcolor[char_num] = 0;}
if(RuleB(World, EnableTutorialButton) && (lvl <= RuleI(World, MaxLevelForTutorial)))
cs->tutorial[char_num] = 1;
if(RuleB(World, EnableReturnHomeButton)) {
int now = time(NULL);
if((now - pp->lastlogin) >= RuleI(World, MinOfflineTimeToReturnHome))
cs->gohome[char_num] = 1;
}
// Character's equipped items
// @merth: Haven't done bracer01/bracer02 yet.
// Also: this needs a second look after items are a little more solid
// NOTE: items don't have a color, players MAY have a tint, if the
// use_tint part is set. otherwise use the regular color
inv = new Inventory;
if(GetInventory(account_id, cs->name[char_num], inv))
{
for (uint8 material = 0; material <= 8; material++)
{
uint32 color;
ItemInst *item = inv->GetItem(Inventory::CalcSlotFromMaterial(material));
if(item == 0)
continue;
cs->equip[char_num][material] = item->GetItem()->Material;
if(pp->item_tint[material].rgb.use_tint) // they have a tint (LoY dye)
color = pp->item_tint[material].color;
else // no tint, use regular item color
color = item->GetItem()->Color;
cs->cs_colors[char_num][material].color = color;
// the weapons are kept elsewhere
if ((material==MATERIAL_PRIMARY) || (material==MATERIAL_SECONDARY))
{
if(strlen(item->GetItem()->IDFile) > 2) {
int32 idfile=atoi(&item->GetItem()->IDFile[2]);
if (material==MATERIAL_PRIMARY)
cs->primary[char_num]=idfile;
else
cs->secondary[char_num]=idfile;
}
}
}
}
else
{
printf("Error loading inventory for %s\n", cs->name[char_num]);
}
safe_delete(inv);
if (++char_num > 10)
break;
}
else
{
cout << "Got a bogus character (" << row[0] << ") Ignoring!!!" << endl;
cout << "PP length ="<<lengths[1]<<" but PP should be "<<sizeof(PlayerProfile_Struct)<<endl;
//DeleteCharacter(row[0]);
}
}
mysql_free_result(result);
}
else
{
cerr << "Error in GetCharSelectInfo query '" << query << "' " << errbuf << endl;
safe_delete_array(query);
return;
}
return;
}
mob.cpp
Code:
void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
{
int i;
strcpy(ns->spawn.name, name);
if(IsClient())
strncpy(ns->spawn.lastName,lastname,sizeof(lastname));
ns->spawn.heading = FloatToEQ19(heading);
ns->spawn.x = FloatToEQ19(x_pos);//((sint32)x_pos)<<3;
ns->spawn.y = FloatToEQ19(y_pos);//((sint32)y_pos)<<3;
ns->spawn.z = FloatToEQ19(z_pos);//((sint32)z_pos)<<3;
ns->spawn.spawnId = GetID();
ns->spawn.curHp = (sint16)GetHPRatio();
ns->spawn.max_hp = 100; //this field needs a better name
ns->spawn.race = race;
ns->spawn.runspeed = runspeed;
ns->spawn.walkspeed = runspeed * 0.5f;
ns->spawn.class_ = class_;
ns->spawn.gender = gender;
ns->spawn.level = level;
ns->spawn.deity = deity;
ns->spawn.animation = 0;
ns->spawn.findable = findable?1:0;
// vesuvias - appearence fix
ns->spawn.light = light;
ns->spawn.invis = (invisible || hidden) ? 1 : 0; // TODO: load this before spawning players
ns->spawn.NPC = IsClient() ? 0 : 1;
ns->spawn.petOwnerId = ownerid;
ns->spawn.haircolor = haircolor ? haircolor : 0xFF;
ns->spawn.beardcolor = beardcolor ? beardcolor : 0xFF;
ns->spawn.eyecolor1 = eyecolor1 ? eyecolor1 : 0xFF;
ns->spawn.eyecolor2 = eyecolor2 ? eyecolor2 : 0xFF;
ns->spawn.hairstyle = hairstyle ? hairstyle : 0xFF;
ns->spawn.face = luclinface;
ns->spawn.beard = beard ? beard : 0xFF;
ns->spawn.equip_chest2 = texture;
// ns->spawn.invis2 = 0xff;//this used to be labeled beard.. if its not FF it will turn
//mob invis
if(helmtexture && helmtexture != 0xFF)
{
//ns->spawn.equipment[MATERIAL_HEAD] = helmtexture;
ns->spawn.helm=helmtexture;
} else {
//ns->spawn.equipment[MATERIAL_HEAD] = 0;
ns->spawn.helm = 0;
}
ns->spawn.guildrank = 0xFF;
ns->spawn.size = size;
ns->spawn.bodytype = bodytype;
// The 'flymode' settings have the following effect:
// 0 - Mobs in water sink like a stone to the bottom
// 1 - Same as #flymode 1
// 2 - Same as #flymode 2
// 3 - Mobs in water do not sink. A value of 3 in this field appears to be the default setting for all mobs
// (in water or not) according to 6.2 era packet collects.
if(IsClient())
ns->spawn.flymode = 0;
else
ns->spawn.flymode = 3;
ns->spawn.lastName[0] = '\0';
strncpy(ns->spawn.lastName, lastname, sizeof(lastname));
for(i = 0; i < MAX_MATERIALS; i++)
{
ns->spawn.equipment[i] = GetEquipmentMaterial(i);
ns->spawn.colors[i].color = GetEquipmentColor(i);
}
memset(ns->spawn.set_to_0xFF, 0xFF, sizeof(ns->spawn.set_to_0xFF));
}
It makes sense that NPCs load perfectly as they are set because they don't do these weird conversions. So, I think it is logical to conclude that the 99 and the 0xFF settings that are getting done are to blame for PCs not showing up properly. Seems to me like things were being overcomplicated when they should just save the number that is set and use that same number when loading the settings. The bald characters and the white hair issues should both be caused by the 99 and 0xFF settings, so removing that should correct them.
Last edited by trevius; 05-13-2009 at 06:54 AM..
|