void CharacterScene::InventoryToStorage( int slot ) { // Always succeeds in the inv->storage directory Inventory* inv = unit->GetInventory(); Item item = inv->GetItem( slot ); if ( item.IsSomething() ) { inventoryWidget->SetInfoText( item.GetItemDef() ); storage->AddItem( item ); inv->RemoveItem( slot ); } }
// the current stuff is at the bottom of this function void WorldDatabase::GetCharSelectInfo(uint32 accountID, EQApplicationPacket **outApp, uint32 clientVersionBit) { /* Set Character Creation Limit */ ClientVersion client_version = ClientVersionFromBit(clientVersionBit); size_t character_limit = EQLimits::CharacterCreationLimit(client_version); // Validate against absolute server max if (character_limit > EmuConstants::CHARACTER_CREATION_LIMIT) character_limit = EmuConstants::CHARACTER_CREATION_LIMIT; // Force Titanium clients to use '8' if (client_version == ClientVersion::Titanium) character_limit = 8; /* Get Character Info */ std::string cquery = StringFormat( "SELECT " "`id`, " // 0 "name, " // 1 "gender, " // 2 "race, " // 3 "class, " // 4 "`level`, " // 5 "deity, " // 6 "last_login, " // 7 "time_played, " // 8 "hair_color, " // 9 "beard_color, " // 10 "eye_color_1, " // 11 "eye_color_2, " // 12 "hair_style, " // 13 "beard, " // 14 "face, " // 15 "drakkin_heritage, " // 16 "drakkin_tattoo, " // 17 "drakkin_details, " // 18 "zone_id " // 19 "FROM " "character_data " "WHERE `account_id` = %i ORDER BY `name` LIMIT %u", accountID, character_limit); auto results = database.QueryDatabase(cquery); size_t character_count = results.RowCount(); if (character_count == 0) { *outApp = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct)); CharacterSelect_Struct *cs = (CharacterSelect_Struct *)(*outApp)->pBuffer; cs->CharCount = 0; cs->TotalChars = character_limit; return; } size_t packet_size = sizeof(CharacterSelect_Struct) + (sizeof(CharacterSelectEntry_Struct) * character_count); *outApp = new EQApplicationPacket(OP_SendCharInfo, packet_size); unsigned char *buff_ptr = (*outApp)->pBuffer; CharacterSelect_Struct *cs = (CharacterSelect_Struct *)buff_ptr; cs->CharCount = character_count; cs->TotalChars = character_limit; buff_ptr += sizeof(CharacterSelect_Struct); for (auto row = results.begin(); row != results.end(); ++row) { CharacterSelectEntry_Struct *cse = (CharacterSelectEntry_Struct *)buff_ptr; PlayerProfile_Struct pp; Inventory inv; uint32 character_id = (uint32)atoi(row[0]); uint8 has_home = 0; uint8 has_bind = 0; memset(&pp, 0, sizeof(PlayerProfile_Struct)); /* Fill CharacterSelectEntry_Struct */ memset(cse->Name, 0, sizeof(cse->Name)); strcpy(cse->Name, row[1]); cse->Class = (uint8)atoi(row[4]); cse->Race = (uint32)atoi(row[3]); cse->Level = (uint8)atoi(row[5]); cse->ShroudClass = cse->Class; cse->ShroudRace = cse->Race; cse->Zone = (uint16)atoi(row[19]); cse->Instance = 0; cse->Gender = (uint8)atoi(row[2]); cse->Face = (uint8)atoi(row[15]); for (uint32 matslot = 0; matslot < _MaterialCount; matslot++) { // Processed below cse->Equip[matslot].Material = 0; cse->Equip[matslot].Unknown1 = 0; cse->Equip[matslot].EliteMaterial = 0; cse->Equip[matslot].HeroForgeModel = 0; cse->Equip[matslot].Material2 = 0; cse->Equip[matslot].Color.Color = 0; } cse->Unknown15 = 0xFF; cse->Unknown19 = 0xFF; cse->DrakkinTattoo = (uint32)atoi(row[17]); cse->DrakkinDetails = (uint32)atoi(row[18]); cse->Deity = (uint32)atoi(row[6]); cse->PrimaryIDFile = 0; // Processed Below cse->SecondaryIDFile = 0; // Processed Below cse->HairColor = (uint8)atoi(row[9]); cse->BeardColor = (uint8)atoi(row[10]); cse->EyeColor1 = (uint8)atoi(row[11]); cse->EyeColor2 = (uint8)atoi(row[12]); cse->HairStyle = (uint8)atoi(row[13]); cse->Beard = (uint8)atoi(row[14]); cse->GoHome = 0; // Processed Below cse->Tutorial = 0; // Processed Below cse->DrakkinHeritage = (uint32)atoi(row[16]); cse->Unknown1 = 0; cse->Enabled = 1; cse->LastLogin = (uint32)atoi(row[7]); // RoF2 value: 1212696584 cse->Unknown2 = 0; /* Fill End */ if (RuleB(World, EnableReturnHomeButton)) { int now = time(nullptr); if ((now - atoi(row[7])) >= RuleI(World, MinOfflineTimeToReturnHome)) cse->GoHome = 1; } if (RuleB(World, EnableTutorialButton) && (cse->Level <= RuleI(World, MaxLevelForTutorial))) { cse->Tutorial = 1; } /* Set Bind Point Data for any character that may possibly be missing it for any reason */ cquery = StringFormat("SELECT `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot` FROM `character_bind` WHERE `id` = %i LIMIT 5", character_id); auto results_bind = database.QueryDatabase(cquery); for (auto row_b = results_bind.begin(); row_b != results_bind.end(); ++row_b) { if (row_b[6] && atoi(row_b[6]) == 4){ has_home = 1; } if (row_b[6] && atoi(row_b[6]) == 0){ has_bind = 1; } } if (has_home == 0 || has_bind == 0) { cquery = StringFormat("SELECT `zone_id`, `bind_id`, `x`, `y`, `z` FROM `start_zones` WHERE `player_class` = %i AND `player_deity` = %i AND `player_race` = %i", cse->Class, cse->Deity, cse->Race); auto results_bind = database.QueryDatabase(cquery); for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) { /* If a bind_id is specified, make them start there */ if (atoi(row_d[1]) != 0) { pp.binds[4].zoneId = (uint32)atoi(row_d[1]); GetSafePoints(pp.binds[4].zoneId, 0, &pp.binds[4].x, &pp.binds[4].y, &pp.binds[4].z); } /* Otherwise, use the zone and coordinates given */ else { pp.binds[4].zoneId = (uint32)atoi(row_d[0]); float x = atof(row_d[2]); float y = atof(row_d[3]); float z = atof(row_d[4]); if (x == 0 && y == 0 && z == 0){ GetSafePoints(pp.binds[4].zoneId, 0, &x, &y, &z); } pp.binds[4].x = x; pp.binds[4].y = y; pp.binds[4].z = z; } } pp.binds[0] = pp.binds[4]; /* If no home bind set, set it */ if (has_home == 0) { std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)" " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", character_id, pp.binds[4].zoneId, 0, pp.binds[4].x, pp.binds[4].y, pp.binds[4].z, pp.binds[4].heading, 4); auto results_bset = QueryDatabase(query); } /* If no regular bind set, set it */ if (has_bind == 0) { std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)" " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", character_id, pp.binds[0].zoneId, 0, pp.binds[0].x, pp.binds[0].y, pp.binds[0].z, pp.binds[0].heading, 0); auto results_bset = QueryDatabase(query); } } /* Bind End */ /* Load Character Material Data for Char Select */ cquery = StringFormat("SELECT slot, red, green, blue, use_tint, color FROM `character_material` WHERE `id` = %u", character_id); auto results_b = database.QueryDatabase(cquery); uint8 slot = 0; for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) { slot = atoi(row_b[0]); pp.item_tint[slot].RGB.Red = atoi(row_b[1]); pp.item_tint[slot].RGB.Green = atoi(row_b[2]); pp.item_tint[slot].RGB.Blue = atoi(row_b[3]); pp.item_tint[slot].RGB.UseTint = atoi(row_b[4]); } /* Character Material Data End */ /* Load Inventory */ // If we ensure that the material data is updated appropriately, we can do away with inventory loads if (GetInventory(accountID, cse->Name, &inv)) { const Item_Struct* item = nullptr; const ItemInst* inst = nullptr; int16 invslot = 0; for (uint32 matslot = 0; matslot < _MaterialCount; matslot++) { invslot = Inventory::CalcSlotFromMaterial(matslot); if (invslot == INVALID_INDEX) { continue; } inst = inv.GetItem(invslot); if (inst == nullptr) { continue; } item = inst->GetItem(); if (item == nullptr) { continue; } if (matslot > 6) { uint32 idfile = 0; // Weapon Models if (inst->GetOrnamentationIDFile() != 0) { idfile = inst->GetOrnamentationIDFile(); cse->Equip[matslot].Material = idfile; } else { if (strlen(item->IDFile) > 2) { idfile = atoi(&item->IDFile[2]); cse->Equip[matslot].Material = idfile; } } if (matslot == MaterialPrimary) { cse->PrimaryIDFile = idfile; } else { cse->SecondaryIDFile = idfile; } } else { uint32 color = 0; if (pp.item_tint[matslot].RGB.UseTint) { color = pp.item_tint[matslot].Color; } else { color = inst->GetColor(); } // Armor Materials/Models cse->Equip[matslot].Material = item->Material; cse->Equip[matslot].EliteMaterial = item->EliteMaterial; cse->Equip[matslot].HeroForgeModel = inst->GetOrnamentHeroModel(matslot); cse->Equip[matslot].Color.Color = color; } } } else { printf("Error loading inventory for %s\n", cse->Name); } /* Load Inventory End */ buff_ptr += sizeof(CharacterSelectEntry_Struct); } }
// solar: the current stuff is at the bottom of this function void WorldDatabase::GetCharSelectInfo(uint32 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->hairstyle[char_num] = pp->hairstyle; cs->beard[char_num] = pp->beard; cs->drakkin_heritage[char_num] = pp->drakkin_heritage; cs->drakkin_tattoo[char_num] = pp->drakkin_tattoo; cs->drakkin_details[char_num] = pp->drakkin_details; if(RuleB(World, EnableTutorialButton) && (lvl <= RuleI(World, MaxLevelForTutorial))) cs->tutorial[char_num] = 1; if(RuleB(World, EnableReturnHomeButton)) { int now = time(nullptr); if((now - pp->lastlogin) >= RuleI(World, MinOfflineTimeToReturnHome)) cs->gohome[char_num] = 1; } // This part creates home city entries for characters created before the home bind point was tracked. // Do it here because the player profile is already loaded and it's as good a spot as any. This whole block should // probably be removed at some point, when most accounts are safely converted. if(pp->binds[4].zoneId == 0) { bool altered = false; MYSQL_RES *result2; MYSQL_ROW row2; char startzone[50] = {0}; // check for start zone variable (I didn't even know any variables were still being used...) if(database.GetVariable("startzone", startzone, 50)) { uint32 zoneid = database.GetZoneID(startzone); if(zoneid) { pp->binds[4].zoneId = zoneid; GetSafePoints(zoneid, 0, &pp->binds[4].x, &pp->binds[4].y, &pp->binds[4].z); altered = true; } } else { RunQuery(query, MakeAnyLenString(&query, "SELECT zone_id,bind_id,x,y,z FROM start_zones " "WHERE player_class=%i AND player_deity=%i AND player_race=%i", pp->class_, pp->deity, pp->race ), errbuf, &result2 ); safe_delete_array(query); // if there is only one possible start city, set it if(mysql_num_rows(result2) == 1) { row2 = mysql_fetch_row(result2); if(atoi(row2[1]) != 0) { // if a bind_id is specified, make them start there pp->binds[4].zoneId = (uint32)atoi(row2[1]); GetSafePoints(pp->binds[4].zoneId, 0, &pp->binds[4].x, &pp->binds[4].y, &pp->binds[4].z); } else { // otherwise, use the zone and coordinates given pp->binds[4].zoneId = (uint32)atoi(row2[0]); float x = atof(row2[2]); float y = atof(row2[3]); float z = atof(row2[4]); if(x == 0 && y == 0 && z == 0) GetSafePoints(pp->binds[4].zoneId, 0, &x, &y, &z); pp->binds[4].x = x; pp->binds[4].y = y; pp->binds[4].z = z; } altered = true; } mysql_free_result(result2); } // update the player profile if(altered) { uint32 char_id = GetCharacterID(cs->name[char_num]); RunQuery(query,MakeAnyLenString(&query,"SELECT extprofile FROM character_ WHERE id=%i",char_id), errbuf, &result2); safe_delete_array(query); if(result2) { row2 = mysql_fetch_row(result2); ExtendedProfile_Struct* ext = (ExtendedProfile_Struct*)row2[0]; SetPlayerProfile(account_id,char_id,pp,inv,ext, 0, 0, 5); } mysql_free_result(result2); } } // end of "set start zone" block // 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==MaterialPrimary) || (material==MaterialSecondary)) { if(strlen(item->GetItem()->IDFile) > 2) { uint32 idfile=atoi(&item->GetItem()->IDFile[2]); if (material==MaterialPrimary) 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 { std::cout << "Got a bogus character (" << row[0] << ") Ignoring!!!" << std::endl; std::cout << "PP length ="<<lengths[1]<<" but PP should be "<<sizeof(PlayerProfile_Struct) << std::endl; //DeleteCharacter(row[0]); } } mysql_free_result(result); } else { std::cerr << "Error in GetCharSelectInfo query '" << query << "' " << errbuf << std::endl; safe_delete_array(query); return; } return; }