using System; using System.Text; using BrewMonster.Network; using CSNetwork; using CSNetwork.GPDataType; using CSNetwork.Protocols; using CSNetwork.Protocols.RPCData; using TMPro; using UnityEngine; using UnityEngine.UI; using BrewMonster.Scripts; using BrewMonster.PerfectWorld.Scripts.Utility.ChatFilter; using System.Collections.Generic; using Cysharp.Threading.Tasks; namespace BrewMonster.UI { /// /// UI screen for creating a new character. /// Equivalent to CDlgCreateGenderName + CDlgCreateProfession in C++. /// public class CreateCharacterScreen : MonoBehaviour { [SerializeField] private GameObject professionSelectionPanel; [SerializeField] private Button[] professionButtons; [SerializeField] private Button maleGenderButton; [SerializeField] private Button femaleGenderButton; [SerializeField] private TMP_InputField nameInputField; [SerializeField] private TMP_Text validationMessageText; [SerializeField] private Button confirmButton; [SerializeField] private Button cancelButton; [SerializeField] private Button backButton; [SerializeField] private CDlgMessageBox messageBoxPrefab; private CDlgMessageBox _messageBoxInstance; private int _currentProfession = -1; private int _currentGender = -1; private Action _onCreateComplete; private Action _onCancel; // Static array matching s_bShowMale[NUM_PROFESSION] from original C++ code (EC_ProfConfigs.cpp) // true = show male, false = show female private static readonly bool[] s_bShowMale = new bool[] { true, // 0: kiem khach (nhan toc) false, // 1: phap su (nhan toc) false, // 2: vu su (tich toc) false, // 3: tien thu (thu toc) true, // 4: than thu (thu toc) true, // 5: thich khach (tich toc) true, // 6: vu mang (vu toc) false, // 7: vu linh (vu toc) true, // 8: kiem linh (linh toc) false, // 9: mi linh (linh toc) true, // 10: da anh (long toc) false, // 11: nguyet tien (long toc) }; private void Start() { if (confirmButton != null) confirmButton.onClick.AddListener(OnConfirmClicked); if (cancelButton != null) cancelButton.onClick.AddListener(OnCancelClicked); if (backButton != null) backButton.onClick.AddListener(OnCancelClicked); if (maleGenderButton != null) maleGenderButton.onClick.AddListener(() => OnGenderSelected(GENDER.GENDER_MALE)); if (femaleGenderButton != null) femaleGenderButton.onClick.AddListener(() => OnGenderSelected(GENDER.GENDER_FEMALE)); // Setup profession buttons if (professionButtons != null) { for (int i = 0; i < professionButtons.Length && i < 12; i++) { int prof = i; // Capture for closure if (professionButtons[i] != null) professionButtons[i].onClick.AddListener(() => OnProfessionSelected(prof)); } } if (nameInputField != null) { nameInputField.onSubmit.AddListener((text) => { if (CanConfirm()) OnConfirmClicked(); }); nameInputField.onValueChanged.AddListener((text) => ClearValidationError()); } } public void Show(Action onCreateComplete, Action onCancel) { _onCreateComplete = onCreateComplete; _onCancel = onCancel; _currentProfession = -1; _currentGender = -1; gameObject.SetActive(true); if (nameInputField != null) { nameInputField.text = ""; nameInputField.Select(); } ClearValidationError(); UpdateConfirmButtonState(); SetupRoleInfos(); } public void Hide() { gameObject.SetActive(false); } public void OnRaceSelected(int prof) { if (prof < 0 || prof >= (int)Profession.NUM_PROFESSION) return; _currentProfession = prof; OnGenderSelected(GetDefaultGenderForProfession(prof)); } private void OnProfessionSelected(int profession) { if (profession < 0 || profession >= (int)Profession.NUM_PROFESSION) return; _currentProfession = profession; // Update UI to show selected profession if (professionButtons != null) { for (int i = 0; i < professionButtons.Length; i++) { if (professionButtons[i] != null) { // Visual feedback for selected profession var colors = professionButtons[i].colors; colors.normalColor = (i == profession) ? Color.yellow : Color.white; professionButtons[i].colors = colors; } } } // Auto-select gender based on profession (matching original C++ logic from EC_ProfConfigs.cpp) // This matches the s_bShowMale array in CanShowOnCreate function int autoGender = GetDefaultGenderForProfession(profession); OnGenderSelected(autoGender); UpdateConfirmButtonState(); } /// /// Gets the default gender for a profession based on the original C++ logic. /// Matches the s_bShowMale array from EC_ProfConfigs.cpp CanShowOnCreate function. /// private int GetDefaultGenderForProfession(int profession) { if (profession >= 0 && profession < s_bShowMale.Length) { return s_bShowMale[profession] ? GENDER.GENDER_MALE : GENDER.GENDER_FEMALE; } // Fallback to male if profession is invalid return GENDER.GENDER_MALE; } private void OnGenderSelected(int gender) { if (gender != GENDER.GENDER_MALE && gender != GENDER.GENDER_FEMALE) return; _currentGender = gender; // Update UI to show selected gender if (maleGenderButton != null) { var colors = maleGenderButton.colors; colors.normalColor = (gender == GENDER.GENDER_MALE) ? Color.yellow : Color.white; maleGenderButton.colors = colors; } if (femaleGenderButton != null) { var colors = femaleGenderButton.colors; colors.normalColor = (gender == GENDER.GENDER_FEMALE) ? Color.yellow : Color.white; femaleGenderButton.colors = colors; } UpdateConfirmButtonState(); LoadShowModel(_currentProfession, _currentGender); } private void OnConfirmClicked() { if (!CanConfirm()) return; if (!TryGetValidatedCharacterName(out string characterName)) { return; } // Create RoleInfo using helper method RoleInfo roleInfo = GameSession.CreateNewRoleInfo(characterName, _currentProfession, _currentGender); // Create role via network Debug.Log($"Calling CreateRoleAsync for character: {characterName}, profession: {_currentProfession}, gender: {_currentGender}"); UnityGameSession.CreateRoleAsync(roleInfo, new Octets(), (createdRole) => { if (createdRole != null) { Debug.Log($"Character created successfully: {characterName}, RoleID: {createdRole.roleid}"); Hide(); _onCreateComplete?.Invoke(createdRole); } else { Debug.LogError($"Failed to create character: {characterName}. Check GameSession logs for error details."); // TODO: Show error message to user } }); } private void OnCancelClicked() { Hide(); _onCancel?.Invoke(); } private bool TryGetValidatedCharacterName(out string characterName) { characterName = nameInputField != null ? nameInputField.text.Trim() : string.Empty; if (string.IsNullOrWhiteSpace(characterName)) { ShowValidationError("Character name cannot be empty."); return false; } if (ChatFilterService.ContainsBadWord(characterName)) { ShowValidationError("Character name contains inappropriate words."); return false; } ClearValidationError(); return true; } private void ShowValidationError(string message) { if (validationMessageText != null) { validationMessageText.text = message; validationMessageText.gameObject.SetActive(true); } var dialog = GetOrCreateMessageBoxInstance(); if (dialog != null) { dialog.ShowMessageBoxYes("Invalid Character Name", message, null, null); } else { CECUIManager.Instance?.ShowMessageBoxYes("Invalid Character Name", message, null, null); } Debug.LogWarning(message); } private CDlgMessageBox GetOrCreateMessageBoxInstance() { if (_messageBoxInstance != null) { return _messageBoxInstance; } if (messageBoxPrefab == null) { return null; } var canvas = GetComponentInParent(); if (canvas == null) { return null; } _messageBoxInstance = Instantiate(messageBoxPrefab, canvas.transform); _messageBoxInstance.gameObject.SetActive(false); return _messageBoxInstance; } private void ClearValidationError() { if (validationMessageText != null) { validationMessageText.text = string.Empty; validationMessageText.gameObject.SetActive(false); } } private bool CanConfirm() { if (_currentProfession < 0 || _currentProfession >= (int)Profession.NUM_PROFESSION) return false; if (_currentGender != GENDER.GENDER_MALE && _currentGender != GENDER.GENDER_FEMALE) return false; string name = nameInputField != null ? nameInputField.text : ""; if (string.IsNullOrWhiteSpace(name)) return false; return true; } private void UpdateConfirmButtonState() { if (confirmButton != null) { confirmButton.interactable = CanConfirm(); } } private void Update() { // Update confirm button state in case name changes if (nameInputField != null && nameInputField.isFocused) { UpdateConfirmButtonState(); } } int[] equipmentIds = new int[] { // Set 0 45504, 35751, 35750, 35752, 35749, 0, // Set 1 36348, 35763, 35762, 35764, 35761, 0, // Set 2 36353, 35755, 35754, 35756, 35753, 0, // Set 3 36346, 35747, 35746, 35748, 35745, 0, // Set 4 45909, 35775, 35774, 35776, 35773, 0, // Set 5 36354, 35743, 35742, 35744, 35741, 0, // Set 6 36352, 35739, 35738, 35740, 35737, 42330, // Set 7 36347, 35771, 35770, 35772, 35769, 42330, // Set 8 36551, 35767, 35766, 35768, 35765, 0, // Set 9 36349, 35759, 35758, 35760, 35757, 0, // Set 10 44959, 45179, 45191, 45215, 45203, 0, // Set 11 45043, 45170, 45171, 45173, 45172, 0 }; private const int NEWCHAR_WEAPON = 0; private const int NEWCHAR_UPPERBODY = 1; private const int NEWCHAR_LOWERBODY = 2; private const int NEWCHAR_WRIST = 3; private const int NEWCHAR_FOOT = 4; private const int NEWCHAR_WING = 5; private const int NEWCHAR_NUM_EQUIP = 6; List _roleInfos = new(); // Setup all the role infor for the PlayerModelPreview private void SetupRoleInfos() { List equipments = null; for (int i = 0; i < (int)Profession.NUM_PROFESSION; i++) { for (int j = 0; j < (int)Gender.NUM_GENDER; j++) { RoleInfo roleInfo = new(); roleInfo.roleid = i * (int)Gender.NUM_GENDER + j; roleInfo.occupation = (byte)i; roleInfo.gender = (byte)j; _roleInfos.Add(roleInfo); // now we setup the quipment equipments = new() { new GRoleInventory() { id = (uint)equipmentIds[i * 6], pos = NEWCHAR_WEAPON }, new GRoleInventory() { id = (uint)equipmentIds[i * 6 + 1], pos = NEWCHAR_UPPERBODY }, new GRoleInventory() { id = (uint)equipmentIds[i * 6 + 2], pos = NEWCHAR_LOWERBODY }, new GRoleInventory() { id = (uint)equipmentIds[i * 6 + 3], pos = NEWCHAR_WRIST }, new GRoleInventory() { id = (uint)equipmentIds[i * 6 + 4], pos = NEWCHAR_FOOT }, new GRoleInventory() { id = (uint)equipmentIds[i * 6 + 5], pos = NEWCHAR_WING } }; roleInfo.equipment = equipments; } } PlayerModelPreview.Instance.ShowAllPlayerModels(_roleInfos).Forget(); } private void LoadShowModel(int prof, int gender) { PlayerModelPreview .Instance.ShowPlayerModel(prof * (int)Gender.NUM_GENDER + gender); } } }