Files
test/Assets/PerfectWorld/Scripts/UI/Login/CreateCharacterScreen.cs
T
2026-04-25 18:05:26 +07:00

414 lines
14 KiB
C#

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;
namespace BrewMonster.UI
{
/// <summary>
/// UI screen for creating a new character.
/// Equivalent to CDlgCreateGenderName + CDlgCreateProfession in C++.
/// </summary>
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<RoleInfo> _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<RoleInfo> 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();
}
/// <summary>
/// Gets the default gender for a profession based on the original C++ logic.
/// Matches the s_bShowMale array from EC_ProfConfigs.cpp CanShowOnCreate function.
/// </summary>
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<Canvas>();
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<RoleInfo> _roleInfos = new();
// Setup all the role infor for the PlayerModelPreview
private void SetupRoleInfos()
{
List<GRoleInventory> 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);
}
private void LoadShowModel(int prof, int gender)
{
PlayerModelPreview .Instance.ShowPlayerModel(prof * (int)Gender.NUM_GENDER + gender);
}
}
}