#include "glue/CCharacterCreation.hpp" #include "glue/CCharacterSelection.hpp" #include "component/CCharacterComponent.hpp" #include "component/Types.hpp" #include "db/Db.hpp" #include "glue/CGlueLoading.hpp" #include "model/CM2Shared.hpp" #include "object/client/Player_C.hpp" #include "ui/simple/CSimpleModelFFX.hpp" CCharacterComponent* CCharacterCreation::s_character; CSimpleModelFFX* CCharacterCreation::s_charCustomizeFrame; float CCharacterCreation::s_charFacing; CharacterPreferences* CCharacterCreation::s_charPreferences[22][2]; TSFixedArray CCharacterCreation::s_classes; int32_t CCharacterCreation::s_existingCharacterIndex; int32_t CCharacterCreation::s_raceIndex; TSGrowableArray CCharacterCreation::s_races; int32_t CCharacterCreation::s_selectedClassID; void CCharacterCreation::CalcClasses(int32_t raceID) { uint32_t classCount = 0; for (int32_t i = 0; i < g_charBaseInfoDB.GetNumRecords(); i++) { auto infoRec = g_charBaseInfoDB.GetRecordByIndex(i); if (infoRec->m_raceID == raceID) { classCount++; } } CCharacterCreation::s_classes.SetCount(classCount); uint32_t classIndex = 0; for (int32_t i = 0; i < g_charBaseInfoDB.GetNumRecords(); i++) { auto infoRec = g_charBaseInfoDB.GetRecordByIndex(i); if (infoRec->m_raceID != raceID) { continue; } auto classRec = g_chrClassesDB.GetRecord(infoRec->m_classID); CCharacterCreation::s_classes[classIndex++] = classRec; } } void CCharacterCreation::CreateComponent(ComponentData* data, bool randomize) { auto modelData = Player_C_GetModelName(data->raceID, data->sexID); if (!modelData || !modelData->m_modelName) { return; } auto existingComponent = CCharacterCreation::s_character; if (existingComponent) { auto model = existingComponent->m_data.model; if (model->m_attachParent) { model->DetachFromParent(); } // TODO CCharacterComponent::FreeComponent(existingComponent); } CCharacterCreation::s_character = CCharacterComponent::AllocComponent(); auto scene = CCharacterCreation::s_charCustomizeFrame->GetScene(); auto model = scene->CreateModel(modelData->m_modelName, 0); data->model = model; if (!model) { return; } // TODO lighting data->model->SetBoneSequence(-1, 0, 0, 0, 1.0f, 1, 1); data->flags |= 0x2; CCharacterCreation::s_character->Init(data, nullptr); if (randomize) { // TODO select random variations } // TODO track previous variations CCharacterCreation::SetFacing(CCharacterCreation::s_charFacing); CCharacterCreation::Dress(); CCharacterCreation::s_character->RenderPrep(0); if (CCharacterCreation::s_charCustomizeFrame->m_model) { model->AttachToParent(CCharacterCreation::s_charCustomizeFrame->m_model, 0, nullptr, 0); } } void CCharacterCreation::Dress() { // TODO } int32_t CCharacterCreation::GetRandomClassID() { // TODO return 1; } void CCharacterCreation::GetRandomRaceAndSex(ComponentData* data) { // TODO data->raceID = 1; data->sexID = UNITSEX_MALE; } void CCharacterCreation::Initialize() { CCharacterCreation::s_charFacing = 0.0f; CCharacterCreation::s_charCustomizeFrame = nullptr; CCharacterCreation::s_existingCharacterIndex = -1; CCharacterCreation::s_races.SetCount(0); // TODO enum or define for faction sides for (int32_t side = 0; side < 2; side++) { for (int32_t race = 0; race < g_chrRacesDB.GetNumRecords(); race++) { auto raceRec = g_chrRacesDB.GetRecordByIndex(race); // TODO NPCOnly? if (raceRec->m_flags & 0x1) { continue; } auto factionTemplateRec = g_factionTemplateDB.GetRecord(raceRec->m_factionID); for (int32_t group = 0; group < g_factionGroupDB.GetNumRecords(); group++) { auto factionGroupRec = g_factionGroupDB.GetRecordByIndex(group); if (!factionGroupRec || !factionGroupRec->m_maskID) { continue; } bool templateMatch = (1 << factionGroupRec->m_maskID) & factionTemplateRec->m_factionGroup; if (!templateMatch) { continue; } bool sideMatch = (side == 0 && !SStrCmpI(factionGroupRec->m_internalName, "Alliance")) || (side == 1 && !SStrCmpI(factionGroupRec->m_internalName, "Horde")); if (!sideMatch) { continue; } // Race is playable and part of a faction aligned to either Alliance or Horde *CCharacterCreation::s_races.New() = raceRec->m_ID; } } } } bool CCharacterCreation::IsClassValid(int32_t classID) { for (int32_t i = 0; i < CCharacterCreation::s_classes.Count(); i++) { auto classRec = CCharacterCreation::s_classes[i]; if (classRec->m_ID == classID) { return true; } } return false; } bool CCharacterCreation::IsRaceClassValid(int32_t raceID, int32_t classID) { for (int32_t i = 0; i < g_charBaseInfoDB.GetNumRecords(); i++) { auto infoRec = g_charBaseInfoDB.GetRecordByIndex(i); if (infoRec->m_raceID == raceID && infoRec->m_classID == classID) { return true; } } return false; } void CCharacterCreation::ResetCharCustomizeInfo() { if (!CCharacterCreation::s_charCustomizeFrame) { return; } CCharacterCreation::s_existingCharacterIndex = -1; auto model = CCharacterCreation::s_charCustomizeFrame->m_model; if (model) { model->DetachAllChildrenById(0); } ComponentData data = {}; CCharacterCreation::GetRandomRaceAndSex(&data); CCharacterCreation::CalcClasses(data.raceID); CCharacterCreation::CreateComponent(&data, true); CCharacterCreation::SetSelectedClass(CCharacterCreation::GetRandomClassID()); data.classID = CCharacterCreation::s_selectedClassID; CCharacterCreation::s_raceIndex = -1; for (int32_t i = 0; i < CCharacterCreation::s_races.Count(); i++) { auto raceID = CCharacterCreation::s_races[i]; if (CCharacterCreation::s_character->m_data.raceID == raceID) { CCharacterCreation::s_raceIndex = i; break; } } // TODO name gen stuff CGlueLoading::StartLoad(CCharacterCreation::s_character, true); } void CCharacterCreation::SetCharCustomizeModel(const char* filename) { if (!CCharacterCreation::s_charCustomizeFrame || !filename || !*filename) { return; } auto existingModel = CCharacterCreation::s_charCustomizeFrame->m_model; if (existingModel && !SStrCmpI(existingModel->m_shared->m_filePath, filename)) { return; } CCharacterCreation::s_charCustomizeFrame->SetModel(filename); auto customizeModel = CCharacterCreation::s_charCustomizeFrame->m_model; // TODO inlined? if (customizeModel) { // TODO lighting stuff customizeModel->IsDrawable(1, 1); } // TODO inlined? if (customizeModel) { auto characterModel = CCharacterCreation::s_character->m_data.model; if (characterModel) { characterModel->AttachToParent(customizeModel, 0, nullptr, 0); } } } void CCharacterCreation::SetFacing(float orientation) { CCharacterCreation::s_charFacing = orientation; auto characterModel = CCharacterCreation::s_character->m_data.model; if (characterModel) { C3Vector position = { 0.0f, 0.0f, 0.0f }; characterModel->SetWorldTransform(position, orientation, 1.0f); } } void CCharacterCreation::SavePreferences() { auto raceID = CCharacterCreation::s_races.m_data[CCharacterCreation::s_raceIndex]; auto sexID = CCharacterCreation::s_character->m_data.sexID; auto preferences = CCharacterCreation::s_charPreferences[raceID][sexID]; if (!preferences) { preferences = STORM_NEW(CharacterPreferences); CCharacterCreation::s_charPreferences[raceID][sexID] = preferences; } CCharacterCreation::s_character->GetPreferences(preferences); } void CCharacterCreation::SetSelectedClass(int32_t classID) { if (!CCharacterCreation::IsClassValid(classID)) { return; } CCharacterCreation::s_selectedClassID = classID; ComponentData data = {}; data.raceID = CCharacterCreation::s_character->m_data.raceID; data.sexID = CCharacterCreation::s_character->m_data.sexID; data.classID = classID; data.skinColorID = CCharacterCreation::s_character->m_data.skinColorID; data.hairStyleID = CCharacterCreation::s_character->m_data.hairStyleID; data.hairColorID = CCharacterCreation::s_character->m_data.hairColorID; data.facialHairStyleID = CCharacterCreation::s_character->m_data.facialHairStyleID; data.faceID = CCharacterCreation::s_character->m_data.faceID; // TODO CCharacterComponent::ValidateComponentData(&data, 0); CCharacterCreation::CreateComponent(&data, false); CCharacterCreation::Dress(); CGlueLoading::StartLoad(CCharacterCreation::s_character, true); } void CCharacterCreation::SetSelectedRace(int32_t raceIndex) { if (raceIndex >= CCharacterCreation::s_races.Count() || raceIndex == CCharacterCreation::s_raceIndex) { return; } auto selectedRaceID = CCharacterCreation::s_races[raceIndex]; auto selectedSexID = CCharacterCreation::s_character->m_data.sexID; CCharacterCreation::SavePreferences(); CCharacterCreation::s_raceIndex = raceIndex; ComponentData data = {}; auto existingCharacter = CCharacterCreation::s_existingCharacterIndex >= 0 ? CCharacterSelection::GetCharacterDisplay(CCharacterCreation::s_existingCharacterIndex) : nullptr; bool useExistingCharacter = existingCharacter && existingCharacter->m_info.raceID == selectedRaceID && existingCharacter->m_info.customizeFlags & 0x110000; bool usePreferences = !useExistingCharacter && CCharacterCreation::s_charPreferences[selectedRaceID][selectedSexID]; if (useExistingCharacter) { data.raceID = existingCharacter->m_info.raceID; data.sexID = existingCharacter->m_info.sexID; data.classID = existingCharacter->m_info.classID; data.skinColorID = existingCharacter->m_info.skinColorID; data.hairStyleID = existingCharacter->m_info.hairStyleID; data.hairColorID = existingCharacter->m_info.hairColorID; data.facialHairStyleID = existingCharacter->m_info.facialHairStyleID; data.faceID = existingCharacter->m_info.faceID; CCharacterCreation::CreateComponent(&data, false); CCharacterCreation::SetSelectedSex(existingCharacter->m_info.sexID); } else if (usePreferences) { auto preferences = CCharacterCreation::s_charPreferences[selectedRaceID][selectedSexID]; data.SetPreferences(preferences); CCharacterCreation::CalcClasses(data.raceID); if (!CCharacterCreation::IsRaceClassValid(data.raceID, CCharacterCreation::s_selectedClassID)) { CCharacterCreation::s_selectedClassID = CCharacterCreation::GetRandomClassID(); } data.classID = CCharacterCreation::s_selectedClassID; // TODO CCharacterComponent::ValidateComponentData(&data, 0); CCharacterCreation::CreateComponent(&data, false); } else { data.raceID = selectedRaceID; data.sexID = selectedSexID; CCharacterCreation::CalcClasses(data.raceID); if (!CCharacterCreation::IsRaceClassValid(data.raceID, CCharacterCreation::s_selectedClassID)) { CCharacterCreation::SetSelectedClass(CCharacterCreation::GetRandomClassID()); } data.classID = CCharacterCreation::s_selectedClassID; CCharacterCreation::CreateComponent(&data, true); } // TODO name gen stuff CCharacterCreation::Dress(); CGlueLoading::StartLoad(CCharacterCreation::s_character, true); } void CCharacterCreation::SetSelectedSex(int32_t sexID) { // TODO }