/* * FILE: EC_Configs.cs * * DESCRIPTION: Configuration management for game client * * CONVERTED FROM: EC_Configs.cpp/EC_Configs.h * * Copyright (c) 2004 Archosaur Studio, All Rights Reserved. */ using BrewMonster.Network; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using UnityEngine; namespace BrewMonster { // Constants public static class EC_ConfigConstants { public const int EC_AUTOREPLY_LEN = 64; public const int EC_BLACKLIST_LEN = 20; public const int EC_USERCHANNEL_NUM = 6; public const int EC_COMBOSKILL_NUM = 8; public const int EC_COMBOSKILL_LEN = 8; public const int EC_CONFIG_VERSION = 36; public const int DEFAULT_UI_THEME_ID = 0; } // System Settings Structure [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct EC_SYSTEM_SETTING { public byte nLevel; public byte nSight; public byte nWaterEffect; [MarshalAs(UnmanagedType.U1)] public bool bSimpleTerrain; public byte nTreeDetail; public byte nGrassDetail; public byte nCloudDetail; [MarshalAs(UnmanagedType.U1)] public bool bShadow; [MarshalAs(UnmanagedType.U1)] public bool bMipMapBias; [MarshalAs(UnmanagedType.U1)] public bool bFullGlow; [MarshalAs(UnmanagedType.U1)] public bool bSpaceWarp; [MarshalAs(UnmanagedType.U1)] public bool bSunFlare; [MarshalAs(UnmanagedType.U1)] public bool bAdvancedWater; public byte nSoundVol; public byte nMusicVol; public int iGamma; public int iRndWidth; public int iRndHeight; public int iTexDetail; public int iSoundQuality; [MarshalAs(UnmanagedType.U1)] public bool bFullScreen; [MarshalAs(UnmanagedType.U1)] public bool bWideScreen; [MarshalAs(UnmanagedType.U1)] public bool bVSync; [MarshalAs(UnmanagedType.U1)] public bool bScaleUI; public int iTheme; public void Reset() { nLevel = 0; nSight = 0; nWaterEffect = 0; bSimpleTerrain = true; nTreeDetail = 0; nGrassDetail = 0; nCloudDetail = 0; bShadow = false; bMipMapBias = false; bFullGlow = false; bSpaceWarp = false; bSunFlare = false; bAdvancedWater = false; nSoundVol = 100; nMusicVol = 100; iGamma = 41; iRndWidth = 800; iRndHeight = 600; iTexDetail = 2; iSoundQuality = 2; bFullScreen = true; bWideScreen = false; bVSync = true; bScaleUI = false; iTheme = EC_ConfigConstants.DEFAULT_UI_THEME_ID; } } // Combo Skill Structure [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct EC_COMBOSKILL { public byte nIcon; [MarshalAs(UnmanagedType.ByValArray, SizeConst = EC_ConfigConstants.EC_COMBOSKILL_LEN)] public short[] idSkill; public EC_COMBOSKILL(bool initialize) { nIcon = 0; idSkill = new short[EC_ConfigConstants.EC_COMBOSKILL_LEN]; } public bool Clear(int id) { bool bClear = false; for (int i = 0; i < EC_ConfigConstants.EC_COMBOSKILL_LEN; ++i) { if (idSkill[i] == id) { idSkill[i] = 0; bClear = true; } } return bClear; } } // Video Settings Structure [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct EC_VIDEO_SETTING { [MarshalAs(UnmanagedType.U1)] public bool bPlayerHP; [MarshalAs(UnmanagedType.U1)] public bool bPlayerMP; [MarshalAs(UnmanagedType.U1)] public bool bPlayerEXP; [MarshalAs(UnmanagedType.U1)] public bool bPlayerHeadText; [MarshalAs(UnmanagedType.U1)] public bool bPlayerName; [MarshalAs(UnmanagedType.U1)] public bool bPlayerFaction; [MarshalAs(UnmanagedType.U1)] public bool bPlayerTitle; [MarshalAs(UnmanagedType.U1)] public bool bPlayerShop; [MarshalAs(UnmanagedType.U1)] public bool bPlayerSelfName; [MarshalAs(UnmanagedType.U1)] public bool bPlayerTalk; [MarshalAs(UnmanagedType.U1)] public bool bPlayerBubble; [MarshalAs(UnmanagedType.U1)] public bool bMonsterName; [MarshalAs(UnmanagedType.U1)] public bool bNPCName; [MarshalAs(UnmanagedType.U1)] public bool bGoodsName; [MarshalAs(UnmanagedType.U1)] public bool bModelLimit; public byte nDistance; [MarshalAs(UnmanagedType.U1)] public bool bMapMonster; [MarshalAs(UnmanagedType.U1)] public bool bMapNPC; [MarshalAs(UnmanagedType.U1)] public bool bMapPlayer; public byte nEffect; [MarshalAs(UnmanagedType.U1)] public bool bShowCustomize; [MarshalAs(UnmanagedType.ByValArray, SizeConst = EC_ConfigConstants.EC_COMBOSKILL_NUM)] public EC_COMBOSKILL[] comboSkill; [MarshalAs(UnmanagedType.U1)] public bool bPlayerForce; [MarshalAs(UnmanagedType.U1)] public bool bMailToFriendsRemind; public int iMailToFriendsCheckTime; public char cTabSelType; [MarshalAs(UnmanagedType.U1)] public bool bActivityRemind; public int ibActivityReminderCheckTime; [MarshalAs(UnmanagedType.U1)] public bool bCloseMeridiansHint; public void Reset() { bPlayerHP = false; bPlayerMP = false; bPlayerEXP = false; bPlayerHeadText = true; bPlayerName = true; bPlayerFaction = true; bPlayerTitle = true; bPlayerShop = true; bPlayerSelfName = true; bPlayerTalk = true; bPlayerBubble = false; bMonsterName = true; bNPCName = true; bGoodsName = true; bModelLimit = false; nDistance = 50; bMapMonster = false; bMapNPC = true; bMapPlayer = true; nEffect = 4; bShowCustomize = true; bPlayerForce = true; bMailToFriendsRemind = true; iMailToFriendsCheckTime = 0; bActivityRemind = true; ibActivityReminderCheckTime = 0; cTabSelType = (char)0; bCloseMeridiansHint = false; comboSkill = new EC_COMBOSKILL[EC_ConfigConstants.EC_COMBOSKILL_NUM]; for (int i = 0; i < EC_ConfigConstants.EC_COMBOSKILL_NUM; i++) { comboSkill[i] = new EC_COMBOSKILL(true); comboSkill[i].nIcon = 0; } } public void Read(BinaryReader reader, uint dwVer) { Reset(); if (dwVer < 15) return; bPlayerHP = reader.ReadBoolean(); bPlayerMP = reader.ReadBoolean(); bPlayerEXP = reader.ReadBoolean(); bPlayerHeadText = reader.ReadBoolean(); bPlayerName = reader.ReadBoolean(); bPlayerFaction = reader.ReadBoolean(); bPlayerTitle = reader.ReadBoolean(); bPlayerShop = reader.ReadBoolean(); bPlayerSelfName = reader.ReadBoolean(); bPlayerTalk = reader.ReadBoolean(); bPlayerBubble = reader.ReadBoolean(); bMonsterName = reader.ReadBoolean(); bNPCName = reader.ReadBoolean(); bGoodsName = reader.ReadBoolean(); bModelLimit = reader.ReadBoolean(); nDistance = reader.ReadByte(); bMapMonster = reader.ReadBoolean(); bMapNPC = reader.ReadBoolean(); bMapPlayer = reader.ReadBoolean(); nEffect = reader.ReadByte(); bShowCustomize = reader.ReadBoolean(); for (int i = 0; i < EC_ConfigConstants.EC_COMBOSKILL_NUM; i++) { comboSkill[i].nIcon = reader.ReadByte(); for (int j = 0; j < EC_ConfigConstants.EC_COMBOSKILL_LEN; j++) { comboSkill[i].idSkill[j] = reader.ReadInt16(); } } if (dwVer >= 21) bPlayerForce = reader.ReadBoolean(); if (dwVer >= 22) { bMailToFriendsRemind = reader.ReadBoolean(); iMailToFriendsCheckTime = reader.ReadInt32(); } if (dwVer >= 24) cTabSelType = reader.ReadChar(); if (dwVer >= 28) { bActivityRemind = reader.ReadBoolean(); ibActivityReminderCheckTime = reader.ReadInt32(); } if (dwVer >= 31) bCloseMeridiansHint = reader.ReadBoolean(); } } // Game Settings Structure // CharSet.Ansi required so szAutoReply matches C++ ACHAR[65] (1 byte/char); default is Unicode (2 bytes/char). [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)] public struct EC_GAME_SETTING { [MarshalAs(UnmanagedType.U1)] public bool bNoTeamRequest; [MarshalAs(UnmanagedType.U1)] public bool bNoTradeRequest; [MarshalAs(UnmanagedType.U1)] public bool bTurnaround; [MarshalAs(UnmanagedType.U1)] public bool bReverseWheel; [MarshalAs(UnmanagedType.ByValArray, SizeConst = EC_ConfigConstants.EC_USERCHANNEL_NUM * 15, ArraySubType = UnmanagedType.I1)] // GP_CHAT_MAX = 15; I1 = 1 byte per bool to match C++ bool[6][15] public byte[] bChannel; // I can't use bool[] here because C# bool is 1 byte but C++ bool is also 1 byte, so we use byte[] to match the size and layout of the original C++ struct. [MarshalAs(UnmanagedType.U1)] public bool bAutoReply; [MarshalAs(UnmanagedType.ByValArray, SizeConst = EC_ConfigConstants.EC_AUTOREPLY_LEN + 1)] public ushort[] szAutoReply; // Using ushort[] to match C++ ACHAR[65] (1 byte/char) with CharSet.Ansi; we will convert to string when reading/writing. /// Convert szAutoReply buffer (UTF-8 bytes stored as ushort) to string. public static string SzAutoReplyToString(ushort[] buf) { if (buf == null || buf.Length == 0) return string.Empty; int n = 0; while (n < buf.Length && buf[n] != 0) n++; if (n == 0) return string.Empty; var bytes = new byte[n]; for (int i = 0; i < n; i++) bytes[i] = (byte)buf[i]; return Encoding.UTF8.GetString(bytes); } /// Write string into szAutoReply buffer as null-terminated UTF-8 (each byte stored as ushort). public static void StringToSzAutoReply(string s, ushort[] buf) { if (buf == null || buf.Length == 0) return; int maxLen = Math.Min(EC_ConfigConstants.EC_AUTOREPLY_LEN, buf.Length - 1); byte[] bytes = Encoding.UTF8.GetBytes(s ?? string.Empty); int n = Math.Min(bytes.Length, maxLen); for (int i = 0; i < n; i++) buf[i] = bytes[i]; buf[n] = 0; for (int i = n + 1; i < buf.Length; i++) buf[i] = 0; } [MarshalAs(UnmanagedType.U1)] public float fCamTurnSpeed; public float fCamZoomSpeed; public byte nFontSize; [MarshalAs(UnmanagedType.U1)] public bool bAtk_Player; [MarshalAs(UnmanagedType.U1)] public bool bAtk_NoMafia; [MarshalAs(UnmanagedType.U1)] public bool bAtk_NoWhite; [MarshalAs(UnmanagedType.U1)] public bool bFontBold; [MarshalAs(UnmanagedType.U1)] public bool bBls_NoRed; [MarshalAs(UnmanagedType.U1)] public bool bBls_NoMafia; [MarshalAs(UnmanagedType.U1)] public bool bBls_Self; [MarshalAs(UnmanagedType.U1)] public bool bBlsRefuse_Neutral; [MarshalAs(UnmanagedType.U1)] public bool bHideAutoGuide; [MarshalAs(UnmanagedType.U1)] public bool bAtk_NoAlliance; [MarshalAs(UnmanagedType.U1)] public bool bBls_NoAlliance; [MarshalAs(UnmanagedType.U1)] public bool bBlsRefuse_NonTeammate; [MarshalAs(UnmanagedType.U1)] public bool bAtk_NoForce; [MarshalAs(UnmanagedType.U1)] public bool bBls_NoForce; [MarshalAs(UnmanagedType.U1)] public bool bLockQuickBar; [MarshalAs(UnmanagedType.U1)] public bool bPetAutoSkill; [MarshalAs(UnmanagedType.U1)] public bool bAutoTeamForTask; [MarshalAs(UnmanagedType.U1)] public bool bDisableAutoWikiHelp; [MarshalAs(UnmanagedType.U1)] public bool bExclusiveAwardMode; [MarshalAs(UnmanagedType.U1)] public bool bHideIceThunderBall; public void Reset() { bNoTeamRequest = false; bNoTradeRequest = false; bTurnaround = false; bReverseWheel = false; bAutoReply = false; szAutoReply = new ushort[EC_ConfigConstants.EC_AUTOREPLY_LEN + 1]; // empty string (first byte 0) fCamTurnSpeed = 10.0f; fCamZoomSpeed = 1.0f; nFontSize = 2; bAtk_Player = false; bAtk_NoMafia = true; bAtk_NoWhite = false; bBls_NoRed = false; bBls_NoMafia = false; bBls_Self = false; bBlsRefuse_Neutral = false; bFontBold = true; bHideAutoGuide = false; bAtk_NoAlliance = true; bBls_NoAlliance = false; bBlsRefuse_NonTeammate = false; bAtk_NoForce = false; bBls_NoForce = false; bLockQuickBar = false; bPetAutoSkill = true; bAutoTeamForTask = true; bDisableAutoWikiHelp = false; bExclusiveAwardMode = true; bHideIceThunderBall = false; // Initialize channel array with default values bChannel = new byte[EC_ConfigConstants.EC_USERCHANNEL_NUM * 15]; bool[,] a_bChannel = new bool[,] { { true, true, true, true, true, false, false, true, true, true, false, true, true, true, true }, { false, false, true, false, false, false, false, false, true, true, false, true, true, true, true }, { false, false, false, true, false, false, false, false, true, true, false, true, true, true, true }, { false, false, false, false, true, false, false, false, true, true, false, true, true, true, true }, { false, false, false, false, false, false, false, true, true, true, false, true, true, true, true }, { false, false, false, false, false, false, true, false, false, false, true, false, false, false, false } }; for (int i = 0; i < EC_ConfigConstants.EC_USERCHANNEL_NUM; i++) { for (int j = 0; j < 15; j++) { bChannel[i * 15 + j] = (byte)(a_bChannel[i, j] ? 1 : 0); } } } public void Read(BinaryReader reader, uint dwVer) { Reset(); if (dwVer < 15) return; bNoTeamRequest = reader.ReadBoolean(); bNoTradeRequest = reader.ReadBoolean(); bTurnaround = reader.ReadBoolean(); bReverseWheel = reader.ReadBoolean(); if (dwVer >= 29) { for (int i = 0; i < EC_ConfigConstants.EC_USERCHANNEL_NUM * 15; i++) { bChannel[i] = (byte)(reader.ReadBoolean() ? 1 : 0); } } else if (dwVer >= 26) { for (int i = 0; i < EC_ConfigConstants.EC_USERCHANNEL_NUM * 14; i++) { if (i < bChannel.Length) bChannel[i] = (byte)(reader.ReadBoolean() ? 1 : 0); } } else { for (int i = 0; i < EC_ConfigConstants.EC_USERCHANNEL_NUM * 13; i++) { if (i < bChannel.Length) bChannel[i] = (byte)(reader.ReadBoolean() ? 1 : 0); } } bAutoReply = reader.ReadBoolean(); // Binary format: 65 bytes UTF-8; store each byte in ushort[] for struct layout if (szAutoReply == null || szAutoReply.Length < EC_ConfigConstants.EC_AUTOREPLY_LEN + 1) szAutoReply = new ushort[EC_ConfigConstants.EC_AUTOREPLY_LEN + 1]; byte[] autoReplyBytes = reader.ReadBytes(EC_ConfigConstants.EC_AUTOREPLY_LEN + 1); for (int i = 0; i < autoReplyBytes.Length; i++) szAutoReply[i] = autoReplyBytes[i]; fCamTurnSpeed = reader.ReadSingle(); fCamZoomSpeed = reader.ReadSingle(); nFontSize = reader.ReadByte(); bAtk_Player = reader.ReadBoolean(); bAtk_NoMafia = reader.ReadBoolean(); bAtk_NoWhite = reader.ReadBoolean(); bFontBold = reader.ReadBoolean(); bBls_NoRed = reader.ReadBoolean(); bBls_NoMafia = reader.ReadBoolean(); bBls_Self = reader.ReadBoolean(); if (dwVer >= 17) bBlsRefuse_Neutral = reader.ReadBoolean(); if (dwVer >= 18) bHideAutoGuide = reader.ReadBoolean(); if (dwVer >= 19) bAtk_NoAlliance = reader.ReadBoolean(); if (dwVer >= 20) bBls_NoAlliance = reader.ReadBoolean(); if (dwVer >= 23) bBlsRefuse_NonTeammate = reader.ReadBoolean(); if (dwVer >= 25) { bAtk_NoForce = reader.ReadBoolean(); bBls_NoForce = reader.ReadBoolean(); } if (dwVer >= 32) { bLockQuickBar = reader.ReadBoolean(); bPetAutoSkill = reader.ReadBoolean(); } if (dwVer >= 33) bAutoTeamForTask = reader.ReadBoolean(); if (dwVer >= 34) bDisableAutoWikiHelp = reader.ReadBoolean(); if (dwVer >= 35) bExclusiveAwardMode = reader.ReadBoolean(); if (dwVer >= 36) bHideIceThunderBall = reader.ReadBoolean(); } } // Blacklist Settings Structure [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct EC_BLACKLIST_SETTING { [MarshalAs(UnmanagedType.ByValArray, SizeConst = EC_ConfigConstants.EC_BLACKLIST_LEN)] public int[] idPlayer; public int levelBlock; public void Reset() { idPlayer = new int[EC_ConfigConstants.EC_BLACKLIST_LEN]; levelBlock = 0; } public void Read(BinaryReader reader, uint dwVer) { Reset(); if (dwVer < 15) return; for (int i = 0; i < EC_ConfigConstants.EC_BLACKLIST_LEN; i++) { idPlayer[i] = reader.ReadInt32(); } if (dwVer >= 18) levelBlock = reader.ReadInt32(); } } // Computer Aided Settings Structure [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct EC_COMPUTER_AIDED_SETTING { public const int GROUP_COUNT = 5; [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct ITEM_GROUP { [MarshalAs(UnmanagedType.U1)] public bool enable; public int item; public sbyte percent; public short slot; public void Reset() { enable = false; item = 0; percent = 0; slot = -1; } } [MarshalAs(UnmanagedType.ByValArray, SizeConst = GROUP_COUNT)] public ITEM_GROUP[] group; public void Reset() { group = new ITEM_GROUP[GROUP_COUNT]; for (int i = 0; i < GROUP_COUNT; ++i) { group[i].Reset(); } } public void DisableAll() { for (int i = 0; i < GROUP_COUNT; ++i) { group[i].enable = false; } } public void Read(BinaryReader reader, uint dwVer) { Reset(); if (dwVer < 27) return; if (dwVer < 30) { ITEM_GROUP[] temp = new ITEM_GROUP[4]; for (int i = 0; i < 4; i++) { temp[i].enable = reader.ReadBoolean(); temp[i].item = reader.ReadInt32(); temp[i].percent = reader.ReadSByte(); temp[i].slot = reader.ReadInt16(); } Array.Copy(temp, group, 4); } else { for (int i = 0; i < GROUP_COUNT; i++) { group[i].enable = reader.ReadBoolean(); group[i].item = reader.ReadInt32(); group[i].percent = reader.ReadSByte(); group[i].slot = reader.ReadInt16(); } } DisableAll(); } } // Simple INI File Reader/Writer public class SimpleIniFile { private Dictionary> _sections = new Dictionary>(StringComparer.OrdinalIgnoreCase); public bool Open(string filePath) { try { if (!File.Exists(filePath)) return false; _sections.Clear(); string currentSection = ""; foreach (string line in File.ReadAllLines(filePath)) { string trimmed = line.Trim(); if (string.IsNullOrEmpty(trimmed) || trimmed.StartsWith(";") || trimmed.StartsWith("#")) continue; if (trimmed.StartsWith("[") && trimmed.EndsWith("]")) { currentSection = trimmed.Substring(1, trimmed.Length - 2); if (!_sections.ContainsKey(currentSection)) _sections[currentSection] = new Dictionary(StringComparer.OrdinalIgnoreCase); } else if (trimmed.Contains("=")) { int eqIndex = trimmed.IndexOf('='); string key = trimmed.Substring(0, eqIndex).Trim(); string value = trimmed.Substring(eqIndex + 1).Trim(); if (!_sections.ContainsKey(currentSection)) _sections[currentSection] = new Dictionary(StringComparer.OrdinalIgnoreCase); _sections[currentSection][key] = value; } } return true; } catch { return false; } } public string GetValueAsString(string section, string key, string defaultValue = "") { if (_sections.ContainsKey(section) && _sections[section].ContainsKey(key)) return _sections[section][key]; return defaultValue; } public int GetValueAsInt(string section, string key, int defaultValue = 0) { string value = GetValueAsString(section, key); if (int.TryParse(value, out int result)) return result; return defaultValue; } public float GetValueAsFloat(string section, string key, float defaultValue = 0.0f) { string value = GetValueAsString(section, key); if (float.TryParse(value, out float result)) return result; return defaultValue; } public bool GetValueAsBoolean(string section, string key, bool defaultValue = false) { string value = GetValueAsString(section, key); if (bool.TryParse(value, out bool result)) return result; int intValue = GetValueAsInt(section, key, defaultValue ? 1 : 0); return intValue != 0; } public void GetValueAsIntArray(string section, string key, int count, int[] output) { string value = GetValueAsString(section, key); string[] parts = value.Split(','); for (int i = 0; i < count && i < parts.Length; i++) { if (int.TryParse(parts[i].Trim(), out int result)) output[i] = result; } } public void GetValueAsFloatArray(string section, string key, int count, float[] output) { string value = GetValueAsString(section, key); string[] parts = value.Split(','); for (int i = 0; i < count && i < parts.Length; i++) { if (float.TryParse(parts[i].Trim(), out float result)) output[i] = result; } } public void WriteStringValue(string section, string key, string value) { if (!_sections.ContainsKey(section)) _sections[section] = new Dictionary(StringComparer.OrdinalIgnoreCase); _sections[section][key] = value; } public void WriteIntValue(string section, string key, int value) { WriteStringValue(section, key, value.ToString()); } public void Save(string filePath) { try { string directory = Path.GetDirectoryName(filePath); if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) Directory.CreateDirectory(directory); using (StreamWriter writer = new StreamWriter(filePath)) { foreach (var section in _sections) { writer.WriteLine($"[{section.Key}]"); foreach (var kvp in section.Value) { writer.WriteLine($"{kvp.Key}={kvp.Value}"); } writer.WriteLine(); } } } catch (Exception ex) { Debug.LogError($"Failed to save INI file: {ex.Message}"); } } public void Close() { _sections.Clear(); } } // Main Configs Class public class CECConfigs { // Default environment values public float m_fDefFogStart; public float m_fDefFogEnd; public float m_fDefFogDensity; public uint m_dwDefFogColor; public string m_strDefSkyFile0; public string m_strDefSkyFile1; public string m_strDefSkyFile2; public uint m_dwDefLightDiff; public uint m_dwDefLightSpec; public Vector3 m_vDefLightDir; public uint m_dwDefAmbient; // Test code, temporary parameters [MarshalAs(UnmanagedType.U1)] public bool m_bShowForest = true; [MarshalAs(UnmanagedType.U1)] public bool m_bShowGrassLand = true; // Configs user cannot change private int m_iCodePage = 936; private float m_fSevActiveRad = 213.0f; private float m_fSceLoadRadius = 500.0f; private float m_fMinPVRadius = 5.0f; private float m_fCurPVRadius; private float m_fForestDetail = 1.0f; private float m_fGrassLandDetail = 1.0f; private float m_fTrnLODDist1 = 180.0f; private float m_fTrnLODDist2 = 320.0f; private uint m_dwPlayerText = 0; private uint m_dwNPCText = 0; private int m_iMaxNameLen = 9; private int m_iMultiClient = 1; private string m_strWindowsTitle = "Element Client"; private string m_strMiniDLTitle = ""; private int m_iClientID = -1; [MarshalAs(UnmanagedType.U1)] private bool m_bRegisterUIScriptFunc = false; [MarshalAs(UnmanagedType.U1)] private bool m_bEnableGT = false; [MarshalAs(UnmanagedType.U1)] private bool m_bEnableArc = false; [MarshalAs(UnmanagedType.U1)] private bool m_bEnableArcAsia = false; [MarshalAs(UnmanagedType.U1)] private bool m_bMiniClient = false; [MarshalAs(UnmanagedType.U1)] private bool m_bSendLogicCheckInfo = false; private List m_strThemeFiles = new List(); // DEBUG only settings [MarshalAs(UnmanagedType.U1)] private bool m_bConsole = false; private int m_iRTDebug = 1; [MarshalAs(UnmanagedType.U1)] private bool m_bShowNPCID = false; private float m_fRunSpeed = 5.0f; private float m_fWalkSpeed = 2.0f; [MarshalAs(UnmanagedType.U1)] private bool m_bTestDist = false; [MarshalAs(UnmanagedType.U1)] private bool m_bShowPos = false; [MarshalAs(UnmanagedType.U1)] private bool m_bGameStat = false; [MarshalAs(UnmanagedType.U1)] private bool m_bShowID = false; [MarshalAs(UnmanagedType.U1)] private bool m_bSkipFrame = false; [MarshalAs(UnmanagedType.U1)] private bool m_bModelUpt = true; [MarshalAs(UnmanagedType.U1)] private bool m_bDebugFog = false; private float m_fDebugFogStart = 10.0f; private float m_fDebugFogEnd = 100.0f; private float m_fDebugFogDensity = 0.5f; private uint m_dwDebugFogColor = 0xFFFFFFFF; [MarshalAs(UnmanagedType.U1)] private bool m_bAdjusting = false; // User settings private EC_SYSTEM_SETTING m_ss; private EC_VIDEO_SETTING m_vs; private EC_GAME_SETTING m_gs; private EC_BLACKLIST_SETTING m_bs; private EC_COMPUTER_AIDED_SETTING m_cas; // Blocked name list by adding sequence private List m_BlockedArr = new List(); public CECConfigs() { m_fCurPVRadius = m_fSevActiveRad; m_gs = default; DefaultUserSettings(ref m_ss, ref m_vs, ref m_gs, ref m_bs, ref m_cas); m_gs.bAtk_NoWhite = true; m_gs.bAtk_NoForce = true; } public bool Init(string szCfgFile, string szClientIDFile, string szSSFile) { if (!LoadConfigs(szCfgFile, szClientIDFile)) { Debug.LogError("CECConfigs::Init, Failed to load configs"); return false; } if (!LoadSystemSettings(szSSFile)) { Debug.LogError("CECConfigs::Init, Failed to load system settings"); return false; } return true; } public void DefaultUserSettings(ref EC_SYSTEM_SETTING pss, ref EC_VIDEO_SETTING pvs, ref EC_GAME_SETTING pgs, ref EC_BLACKLIST_SETTING pbs, ref EC_COMPUTER_AIDED_SETTING pcas) { pss = new EC_SYSTEM_SETTING(); pvs = new EC_VIDEO_SETTING(); pgs = new EC_GAME_SETTING(); pbs = new EC_BLACKLIST_SETTING(); pcas = new EC_COMPUTER_AIDED_SETTING(); pss.Reset(); pvs.Reset(); pgs.Reset(); pbs.Reset(); pcas.Reset(); } private bool LoadConfigs(string szFile, string szClientIDFile) { SimpleIniFile iniFile = new SimpleIniFile(); if (!iniFile.Open(szFile)) { Debug.LogWarning($"CECConfigs::LoadConfigs, Failed to open file {szFile}"); return false; } // PathFile section string strSect = "PathFile"; m_strThemeFiles.Clear(); string strValue = iniFile.GetValueAsString(strSect, "theme"); if (!string.IsNullOrEmpty(strValue)) { string[] themes = strValue.Split(','); foreach (string theme in themes) { string trimmed = theme.Trim(); if (!string.IsNullOrEmpty(trimmed)) m_strThemeFiles.Add(trimmed); } } // Settings section strSect = "Settings"; m_iCodePage = iniFile.GetValueAsInt(strSect, "code_page", m_iCodePage); m_bConsole = iniFile.GetValueAsInt(strSect, "console", m_bConsole ? 1 : 0) != 0; m_iRTDebug = iniFile.GetValueAsInt(strSect, "runtime_debug", m_iRTDebug); m_iMaxNameLen = iniFile.GetValueAsInt(strSect, "max_name_len", m_iMaxNameLen); m_iMultiClient = iniFile.GetValueAsInt(strSect, "multi_client", m_iMultiClient); m_strWindowsTitle = iniFile.GetValueAsString(strSect, "windows_title", m_strWindowsTitle); m_strMiniDLTitle = iniFile.GetValueAsString(strSect, "mini_dl_title", m_strMiniDLTitle); m_iClientID = iniFile.GetValueAsInt(strSect, "client_id", m_iClientID); m_bRegisterUIScriptFunc = iniFile.GetValueAsInt(strSect, "register_script", m_bRegisterUIScriptFunc ? 1 : 0) != 0; m_bEnableGT = iniFile.GetValueAsInt(strSect, "enable_gt", m_bEnableGT ? 1 : 0) != 0; m_bEnableArc = iniFile.GetValueAsInt(strSect, "enable_coreclient", m_bEnableArc ? 1 : 0) != 0; m_bEnableArcAsia = iniFile.GetValueAsInt(strSect, "enable_arcasia", m_bEnableArcAsia ? 1 : 0) != 0; m_bMiniClient = iniFile.GetValueAsInt(strSect, "mini_client", m_bMiniClient ? 1 : 0) != 0; m_bSendLogicCheckInfo = iniFile.GetValueAsInt(strSect, "logiccheck_info", m_bSendLogicCheckInfo ? 1 : 0) != 0; // Default environment value strSect = "DefEnv"; m_fDefFogStart = iniFile.GetValueAsFloat(strSect, "fog_start", 250.0f); m_fDefFogEnd = iniFile.GetValueAsFloat(strSect, "fog_end", 600.0f); m_fDefFogDensity = iniFile.GetValueAsFloat(strSect, "fog_density", 1.0f); m_strDefSkyFile0 = iniFile.GetValueAsString(strSect, "sky_image01", "5\\Roof.bmp"); m_strDefSkyFile1 = iniFile.GetValueAsString(strSect, "sky_image02", "5\\01.bmp"); m_strDefSkyFile2 = iniFile.GetValueAsString(strSect, "sky_image03", "5\\02.bmp"); int[] col = new int[3]; iniFile.GetValueAsIntArray(strSect, "fog_color", 3, col); m_dwDefFogColor = (uint)((col[0] << 16) | (col[1] << 8) | col[2]); iniFile.GetValueAsIntArray(strSect, "ambient", 3, col); m_dwDefAmbient = (uint)((col[0] << 16) | (col[1] << 8) | col[2]); iniFile.GetValueAsIntArray(strSect, "light_diffuse", 3, col); m_dwDefLightDiff = (uint)((col[0] << 16) | (col[1] << 8) | col[2]); iniFile.GetValueAsIntArray(strSect, "light_specular", 3, col); m_dwDefLightSpec = (uint)((col[0] << 16) | (col[1] << 8) | col[2]); float[] lightDir = new float[3]; iniFile.GetValueAsFloatArray(strSect, "light_dir", 3, lightDir); m_vDefLightDir = new Vector3(lightDir[0], lightDir[1], lightDir[2]); m_vDefLightDir.Normalize(); iniFile.Close(); if (File.Exists(szClientIDFile)) { SimpleIniFile clientIDFile = new SimpleIniFile(); if (clientIDFile.Open(szClientIDFile)) { m_iClientID = clientIDFile.GetValueAsInt("Default", "client_id", m_iClientID); clientIDFile.Close(); } } if (m_iClientID != -1) { Debug.Log($"client_id = {m_iClientID}"); } m_bDebugFog = false; m_fDebugFogStart = m_fDefFogStart; m_fDebugFogEnd = m_fDefFogEnd; m_fDebugFogDensity = m_fDefFogDensity; m_dwDebugFogColor = m_dwDefFogColor; return true; } private bool LoadSystemSettings(string szFile) { SimpleIniFile iniFile = new SimpleIniFile(); if (!iniFile.Open(szFile)) { Debug.LogWarning($"CECConfigs::LoadSystemSettings, Failed to open file {szFile}"); Debug.LogWarning("we use default settings to let users go in..."); return true; } // Video section string strSect = "Video"; m_ss.nLevel = (byte)iniFile.GetValueAsInt(strSect, "Level", 0); m_ss.nSight = (byte)iniFile.GetValueAsInt(strSect, "Sight", 0); m_ss.nWaterEffect = (byte)iniFile.GetValueAsInt(strSect, "WaterEffect", 0); m_ss.bSimpleTerrain = iniFile.GetValueAsInt(strSect, "SimpleTerrain", 0) != 0; m_ss.nTreeDetail = (byte)iniFile.GetValueAsInt(strSect, "TreeDetail", 0); m_ss.nGrassDetail = (byte)iniFile.GetValueAsInt(strSect, "GrassDetail", 0); m_ss.nCloudDetail = (byte)iniFile.GetValueAsInt(strSect, "CloudDetail", 0); m_ss.bShadow = iniFile.GetValueAsInt(strSect, "Shadow", 0) != 0; m_ss.bMipMapBias = iniFile.GetValueAsInt(strSect, "MipMapBias", 0) != 0; m_ss.bFullGlow = iniFile.GetValueAsInt(strSect, "FullGlow", 0) != 0; m_ss.bSpaceWarp = iniFile.GetValueAsInt(strSect, "SpaceWarp", 0) != 0; m_ss.bSunFlare = iniFile.GetValueAsInt(strSect, "SunFlare", 0) != 0; m_ss.bAdvancedWater = iniFile.GetValueAsInt(strSect, "AdvancedWaterUD", 0) != 0; m_ss.iRndWidth = iniFile.GetValueAsInt(strSect, "RenderWid", 800); m_ss.iRndHeight = iniFile.GetValueAsInt(strSect, "RenderHei", 600); m_ss.iTexDetail = iniFile.GetValueAsInt(strSect, "TexDetail", 2); m_ss.iSoundQuality = iniFile.GetValueAsInt(strSect, "SoundQuality", 2); m_ss.bFullScreen = iniFile.GetValueAsInt(strSect, "FullScreen", 1) != 0; m_ss.bWideScreen = iniFile.GetValueAsInt(strSect, "WideScreen", 0) != 0; m_ss.bScaleUI = iniFile.GetValueAsInt(strSect, "ScaleUI", 0) != 0; m_ss.bVSync = iniFile.GetValueAsInt(strSect, "VerticalSync", 1) != 0; m_ss.iGamma = iniFile.GetValueAsInt(strSect, "Gamma", 41); // Audio section strSect = "Audio"; m_ss.nSoundVol = (byte)iniFile.GetValueAsInt(strSect, "SoundVol", 50); m_ss.nMusicVol = (byte)iniFile.GetValueAsInt(strSect, "MusicVol", 50); // UI section m_ss.iTheme = EC_ConfigConstants.DEFAULT_UI_THEME_ID; // Optimize section strSect = "Optimize"; // Note: CECOptimize class would need to be implemented separately // m_ops.gfx.bExcludeHost = iniFile.GetValueAsBoolean(strSect, "ExcludeHost", false); // ... other optimize settings iniFile.Close(); ConvertSightSetting(m_ss.nSight); m_fForestDetail = m_ss.nTreeDetail * 0.2f; m_fGrassLandDetail = m_ss.nGrassDetail * 0.25f; return true; } public float GetSceneLoadRadius() { // Note: Would need EC_Game reference to check game state return m_fSceLoadRadius; } public void SetPlayerVisRadius(float fRadius) { m_fCurPVRadius = fRadius; m_fCurPVRadius = Mathf.Clamp(m_fCurPVRadius, m_fMinPVRadius, m_fSevActiveRad); } public bool SaveSystemSettings() { SimpleIniFile iniFile = new SimpleIniFile(); // Info section string strSect = "Info"; // Note: Graphics card info would need Unity/System integration iniFile.WriteStringValue(strSect, "card", "Unknown"); iniFile.WriteStringValue(strSect, "driver", "Unknown"); iniFile.WriteStringValue(strSect, "version", "1.0.0.0"); iniFile.WriteStringValue(strSect, "vendor", "00000000"); iniFile.WriteStringValue(strSect, "deviceid", "00000000"); iniFile.WriteStringValue(strSect, "subsysid", "00000000"); iniFile.WriteStringValue(strSect, "guid", "{00000000-0000-0000-0000-000000000000}"); // Video section strSect = "Video"; iniFile.WriteIntValue(strSect, "Level", m_ss.nLevel); iniFile.WriteIntValue(strSect, "Sight", m_ss.nSight); iniFile.WriteIntValue(strSect, "WaterEffect", m_ss.nWaterEffect); iniFile.WriteIntValue(strSect, "SimpleTerrain", m_ss.bSimpleTerrain ? 1 : 0); iniFile.WriteIntValue(strSect, "TreeDetail", m_ss.nTreeDetail); iniFile.WriteIntValue(strSect, "GrassDetail", m_ss.nGrassDetail); iniFile.WriteIntValue(strSect, "CloudDetail", m_ss.nCloudDetail); iniFile.WriteIntValue(strSect, "Shadow", m_ss.bShadow ? 1 : 0); iniFile.WriteIntValue(strSect, "MipMapBias", m_ss.bMipMapBias ? 1 : 0); iniFile.WriteIntValue(strSect, "FullGlow", m_ss.bFullGlow ? 1 : 0); iniFile.WriteIntValue(strSect, "SpaceWarp", m_ss.bSpaceWarp ? 1 : 0); iniFile.WriteIntValue(strSect, "SunFlare", m_ss.bSunFlare ? 1 : 0); iniFile.WriteIntValue(strSect, "AdvancedWaterUD", m_ss.bAdvancedWater ? 1 : 0); iniFile.WriteIntValue(strSect, "RenderWid", m_ss.iRndWidth); iniFile.WriteIntValue(strSect, "RenderHei", m_ss.iRndHeight); iniFile.WriteIntValue(strSect, "TexDetail", m_ss.iTexDetail); iniFile.WriteIntValue(strSect, "SoundQuality", m_ss.iSoundQuality); iniFile.WriteIntValue(strSect, "FullScreen", m_ss.bFullScreen ? 1 : 0); iniFile.WriteIntValue(strSect, "WideScreen", m_ss.bWideScreen ? 1 : 0); iniFile.WriteIntValue(strSect, "ScaleUI", m_ss.bScaleUI ? 1 : 0); iniFile.WriteIntValue(strSect, "VerticalSync", m_ss.bVSync ? 1 : 0); iniFile.WriteIntValue(strSect, "Gamma", m_ss.iGamma); // Audio section strSect = "Audio"; iniFile.WriteIntValue(strSect, "SoundVol", m_ss.nSoundVol); iniFile.WriteIntValue(strSect, "MusicVol", m_ss.nMusicVol); // UI section strSect = "UI"; iniFile.WriteIntValue(strSect, "Theme", m_ss.iTheme); // Optimize section strSect = "Optimize"; // Note: Would need CECOptimize implementation // iniFile.WriteIntValue(strSect, "ExcludeHost", m_ops.gfx.bExcludeHost ? 1 : 0); // ... other optimize settings string filePath = Path.Combine(Application.persistentDataPath, "userdata", "SystemSettings.ini"); iniFile.Save(filePath); iniFile.Close(); return true; } public bool SaveUserConfigData(byte[] pDataBuf, out int piSize) { piSize = 0; int iTotalSize = 0; iTotalSize += sizeof(uint); // Version iTotalSize += Marshal.SizeOf(typeof(EC_VIDEO_SETTING)); iTotalSize += Marshal.SizeOf(typeof(EC_GAME_SETTING)); iTotalSize += Marshal.SizeOf(typeof(EC_BLACKLIST_SETTING)); iTotalSize += Marshal.SizeOf(typeof(EC_COMPUTER_AIDED_SETTING)); if (pDataBuf != null && pDataBuf.Length >= iTotalSize) { using (MemoryStream ms = new MemoryStream(pDataBuf)) using (BinaryWriter writer = new BinaryWriter(ms)) { writer.Write((uint)EC_ConfigConstants.EC_CONFIG_VERSION); // Note: Would need proper serialization for structs // This is a simplified version } } piSize = iTotalSize; return true; } /// /// Write user config data into buffer at given offset. 在指定偏移处写入用户配置数据。 /// public bool SaveUserConfigData(byte[] pDataBuf, int startIndex, out int piSize) { if (startIndex < 0) { piSize = 0; return false; } piSize = 0; int iTotalSize = 0; iTotalSize += sizeof(uint); iTotalSize += Marshal.SizeOf(typeof(EC_VIDEO_SETTING)); iTotalSize += Marshal.SizeOf(typeof(EC_GAME_SETTING)); iTotalSize += Marshal.SizeOf(typeof(EC_BLACKLIST_SETTING)); iTotalSize += Marshal.SizeOf(typeof(EC_COMPUTER_AIDED_SETTING)); if (pDataBuf != null && startIndex + iTotalSize <= pDataBuf.Length) { using (MemoryStream ms = new MemoryStream(pDataBuf, startIndex, pDataBuf.Length - startIndex)) using (BinaryWriter writer = new BinaryWriter(ms)) { writer.Write((uint)EC_ConfigConstants.EC_CONFIG_VERSION); } } piSize = iTotalSize; return true; } public void DefaultUserConfigData() { DefaultUserSettings(ref m_ss, ref m_vs, ref m_gs, ref m_bs, ref m_cas); // This fix a bug in some version m_gs.fCamTurnSpeed = 10.0f; m_gs.fCamZoomSpeed = 1.0f; } public bool LoadUserConfigData(byte[] pDataBuf, int iDataSize) { try { using (MemoryStream ms = new MemoryStream(pDataBuf, 0, iDataSize)) using (BinaryReader reader = new BinaryReader(ms)) { uint dwVer = reader.ReadUInt32(); if (dwVer < 15) { DefaultUserConfigData(); goto End; } else if (dwVer > EC_ConfigConstants.EC_CONFIG_VERSION) { throw new Exception("CECConfigs::LoadUserConfigData, version mismatch dwVer="+ dwVer); } m_vs.Read(reader, dwVer); m_gs.Read(reader, dwVer); m_bs.Read(reader, dwVer); m_cas.Read(reader, dwVer); } } catch (Exception e) { Debug.LogError($"CECConfigs::LoadUserConfigData, data read error: {e.Message}"); DefaultUserConfigData(); } End: /*if (CECCrossServer::Instance().IsOnSpecialServer()) memset(m_bs.idPlayer, 0, sizeof(m_bs.idPlayer));*/ ApplyUserSetting(); return true; } public void ApplyUserSetting() { Verify(); BuildTextFlags(); m_fCurPVRadius = CalcPlayerVisRadius(m_vs.nDistance); // Note: Would need EC_Game reference // g_pGame->GetA3DGFXExMan()->SetPriority(m_vs.nEffect); // Send force attack to server byte forceAttack = EC_Utility.glb_BuildPVPMask(false); byte refuseBless = EC_Utility.glb_BuildRefuseBLSMask(); UnityGameSession.c2s_SendCmdNotifyForceAttack(forceAttack, refuseBless); } public void SetSceneLoadRadius(float fRadius) { m_fSceLoadRadius = fRadius; m_fSceLoadRadius = Mathf.Max(m_fSceLoadRadius, m_fSevActiveRad); // Note: Would need EC_Game reference // g_pGame->GetGameRun()->GetWorld()->SetViewRadius(fRadius); } public void SetForestDetail(float fDetail) { m_fForestDetail = fDetail; m_fForestDetail = Mathf.Clamp(m_fForestDetail, 0.0f, 1.0f); // Note: Would need EC_Game reference // g_pGame->GetGameRun()->GetWorld()->GetForest()->SetLODLevel(m_fForestDetail); } public void SetGrassLandDetail(float fDetail) { m_fGrassLandDetail = fDetail; m_fGrassLandDetail = Mathf.Clamp(m_fGrassLandDetail, 0.0f, 1.0f); // Note: Would need EC_Game reference // g_pGame->GetGameRun()->GetWorld()->GetGrassLand()->SetLODLevel(m_fGrassLandDetail); } public void SetSystemSettings(EC_SYSTEM_SETTING systemSetting, bool bCallFromWndProc = false) { // Note: This is a simplified version - full implementation would need EC_Game references EC_SYSTEM_SETTING ss = systemSetting; ApplyWorldSpecificSetting(ref ss); ss.iTheme = EC_ConfigConstants.DEFAULT_UI_THEME_ID; // Apply settings... m_ss = ss; } public void SetVideoSettings(EC_VIDEO_SETTING vs) { m_vs = vs; BuildTextFlags(); m_fCurPVRadius = CalcPlayerVisRadius(m_vs.nDistance); // Note: Would need EC_Game reference // g_pGame->GetA3DGFXExMan()->SetPriority(m_vs.nEffect); } public void SetGameSettings(EC_GAME_SETTING gs) { m_gs = gs; } public void SetBlackListSettings(EC_BLACKLIST_SETTING bs) { m_bs = bs; } public void SetComputerAidedSetting(EC_COMPUTER_AIDED_SETTING cas) { m_cas = cas; } private void ConvertSightSetting(int iSight) { switch (iSight) { case 0: m_fSceLoadRadius = m_fSevActiveRad; m_fTrnLODDist1 = 40.0f; m_fTrnLODDist2 = 80.0f; break; case 1: m_fSceLoadRadius = 250.0f; m_fTrnLODDist1 = 120.0f; m_fTrnLODDist2 = 240.0f; break; case 2: case 3: case 4: m_fSceLoadRadius = 420.0f; m_fTrnLODDist1 = 180.0f; m_fTrnLODDist2 = 320.0f; break; default: Debug.LogError($"CECConfigs::ConvertSightSetting, invalid sight setting: {iSight}"); break; } } private void BuildTextFlags() { m_dwPlayerText = 0; if (m_vs.bPlayerHeadText) { // Note: Would need CECPlayer constants // m_dwPlayerText |= m_vs.bPlayerName ? CECPlayer::RNF_NAME : 0; // m_dwPlayerText |= m_vs.bPlayerTitle ? CECPlayer::RNF_TITLE : 0; // etc. } if (m_vs.bPlayerShop) { // Note: Would need CECPlayer constants // m_dwPlayerText |= m_vs.bPlayerShop ? CECPlayer::RNF_BUY : 0; // m_dwPlayerText |= m_vs.bPlayerShop ? CECPlayer::RNF_SELL : 0; } m_dwNPCText = 0; // Note: Would need CECNPC constants // m_dwNPCText |= m_vs.bNPCName ? CECNPC::RNF_NPCNAME : 0; // m_dwNPCText |= m_vs.bMonsterName ? CECNPC::RNF_MONSTERNAME : 0; // m_dwNPCText |= CECNPC::RNF_WORDS; } private void Verify() { // Verify game setting string autoReplyStr = EC_GAME_SETTING.SzAutoReplyToString(m_gs.szAutoReply); if (autoReplyStr.Length > EC_ConfigConstants.EC_AUTOREPLY_LEN) { if (m_gs.szAutoReply == null || m_gs.szAutoReply.Length < EC_ConfigConstants.EC_AUTOREPLY_LEN + 1) m_gs.szAutoReply = new ushort[EC_ConfigConstants.EC_AUTOREPLY_LEN + 1]; EC_GAME_SETTING.StringToSzAutoReply(autoReplyStr.Substring(0, EC_ConfigConstants.EC_AUTOREPLY_LEN), m_gs.szAutoReply); } if (m_gs.nFontSize > 4) { Debug.LogWarning($"CECConfigs::Verify, found invalid nFontSize == {m_gs.nFontSize}"); m_gs.nFontSize = 4; } if (m_gs.fCamTurnSpeed != 10.0f) { Debug.LogWarning($"CECConfigs::Verify, found invalid fCamTurnSpeed == {m_gs.fCamTurnSpeed}"); m_gs.fCamTurnSpeed = 10.0f; } if (m_gs.fCamZoomSpeed != 1.0f) { Debug.LogWarning($"CECConfigs::Verify, found invalid fCamZoomSpeed == {m_gs.fCamZoomSpeed}"); m_gs.fCamZoomSpeed = 1.0f; } if (m_vs.nDistance > 100) { Debug.LogWarning($"CECConfigs::Verify, found invalid nDistance == {m_vs.nDistance}"); m_vs.nDistance = 100; } if (m_vs.nEffect > 4) { Debug.LogWarning($"CECConfigs::Verify, found invalid nEffect == {m_vs.nEffect}"); m_vs.nEffect = 4; } // Note: Would need CECHostPlayer constants for tab selection type verification // VerifyComboSkills(); } public float CalcPlayerVisRadius(int iPercent) { return m_fMinPVRadius + (m_fSevActiveRad - m_fMinPVRadius) * iPercent / 100.0f; } public string GetRandomThemeFile() { if (m_strThemeFiles.Count > 0) { int index = UnityEngine.Random.Range(0, m_strThemeFiles.Count); return "music\\" + m_strThemeFiles[index]; } return ""; } public bool IsPlayerBlocked(int idPlayer) { return m_BlockedArr.Contains(idPlayer); } public void SetBlockedName(int idPlayer, string pName) { if (idPlayer <= 0) return; bool bBlocked = IsPlayerBlocked(idPlayer); if (string.IsNullOrEmpty(pName)) { if (bBlocked) { m_BlockedArr.Remove(idPlayer); } } else { if (!bBlocked) { m_BlockedArr.Add(idPlayer); } } } public int LoadBlockedList() { m_BlockedArr.Clear(); for (int i = 0; i < EC_ConfigConstants.EC_BLACKLIST_LEN; i++) { int id = m_bs.idPlayer[i]; if (id <= 0) break; m_BlockedArr.Add(id); } // Note: Would need to load from file // string strFile = Path.Combine(Application.persistentDataPath, "userdata", "blacklist", $"{characterID}.dat"); // Load from file if exists return m_BlockedArr.Count; } public void SaveBlockedID() { // TODO // Note: Would need character ID // string strDir = Path.Combine(Application.persistentDataPath, "userdata", "blacklist"); // Directory.CreateDirectory(strDir); // string strFile = Path.Combine(strDir, $"{characterID}.dat"); // Save to file } public void ApplyWorldSpecificSetting() { if (m_bAdjusting) return; m_bAdjusting = true; // Note: Would need EC_Game references for water effects // if (!IsUseWaterRefract() && CanUseWaterRefract() && ShouldForceWaterRefract()) // UseWaterRefract(); // if (!IsUseWaterReflect() && CanUseWaterReflect() && ShouldForceWaterReflect()) // UseWaterReflect(); m_bAdjusting = false; } private void ApplyWorldSpecificSetting(ref EC_SYSTEM_SETTING ss) { // Note: Would need EC_Game references // if (!IsUseWaterRefract(ss) && CanUseWaterRefract() && ShouldForceWaterRefract()) // UseWaterRefract(ss); // if (!IsUseWaterReflect(ss) && CanUseWaterReflect() && ShouldForceWaterReflect()) // UseWaterReflect(ss); } // Getters public int GetLanCodePage() { return m_iCodePage; } public float GetDefFogStart() { return m_fDefFogStart; } public float GetDefFogEnd() { return m_fDefFogEnd; } public float GetDefFogDensity() { return m_fDefFogDensity; } public uint GetDefFogColor() { return m_dwDefFogColor; } public float GetSevActiveRadius() { return m_fSevActiveRad; } public float GetPlayerVisRadius() { return m_fCurPVRadius; } public float GetForestDetail() { return m_fForestDetail; } public float GetGrassLandDetail() { return m_fGrassLandDetail; } public float GetTerrainLODDist1() { return m_fTrnLODDist1; } public float GetTerrainLODDist2() { return m_fTrnLODDist2; } public uint GetPlayerTextFlags() { return m_dwPlayerText; } public uint GetNPCTextFlags() { return m_dwNPCText; } public int GetMaxNameLen() { return m_iMaxNameLen; } public int GetMultiClient() { return m_iMultiClient; } public string GetWindowsTitle() { return m_strWindowsTitle; } public string GetMiniDLTitle() { return m_strMiniDLTitle; } public int GetClientID() { return m_iClientID; } public bool GetRegisterScriptFunc() { return m_bRegisterUIScriptFunc; } public bool GetEnableGT() { return m_bEnableGT; } public bool GetEnableArc() { return m_bEnableArc; } public bool GetEnableArcAsia() { return m_bEnableArcAsia; } public bool IsMiniClient() { return m_bMiniClient; } public bool IsSendLogicCheckInfo() { return m_bSendLogicCheckInfo; } public bool HasConsole() { return m_bConsole; } public void EnableConsole(bool bEnable) { m_bConsole = bEnable; } public void SetRTDebugLevel(int iLevel) { m_iRTDebug = iLevel; } public int GetRTDebugLevel() { return m_iRTDebug; } public void ShowNPCID(bool bShow) { m_bShowNPCID = bShow; } public bool IsNPCIDShown() { return m_bShowNPCID; } public void SetHostRunSpeed(float fSpeed) { if (fSpeed > 0.0f && fSpeed <= 15.0f) m_fRunSpeed = fSpeed; } public float GetHostRunSpeed() { return m_fRunSpeed; } public float GetHostWalkSpeed() { return m_fWalkSpeed; } public bool GetTestDistFlag() { return m_bTestDist; } public void SetTestDistFlag(bool bFlag) { m_bTestDist = bFlag; } public bool GetShowPosFlag() { return m_bShowPos; } public void SetShowPosFlag(bool bFlag) { m_bShowPos = bFlag; } public bool GetShowForestFlag() { return m_bShowForest; } public void SetShowForestFlag(bool bFlag) { m_bShowForest = bFlag; } public bool GetShowGameStatFlag() { return m_bGameStat; } public void SetShowGameStatFlag(bool bFlag) { m_bGameStat = bFlag; } public bool GetShowIDFlag() { return m_bShowID; } public void SetShowIDFlag(bool bFlag) { m_bShowID = bFlag; } public bool GetSkipFrameFlag() { return m_bSkipFrame; } public void SetSkipFrameFlag(bool bTrue) { m_bSkipFrame = bTrue; } public bool GetModelUpdateFlag() { return m_bModelUpt; } public void SetModelUpdateFlag(bool bTrue) { m_bModelUpt = bTrue; } public void SetDebugFog(bool bDebug) { m_bDebugFog = bDebug; } public bool IsDebugFog() { return m_bDebugFog; } public void SetDebugFogParameter(float fFogStart, float fFogEnd, float fFogDensity, uint fogColor) { m_fDebugFogStart = fFogStart; m_fDebugFogEnd = fFogEnd; m_fDebugFogDensity = fFogDensity; m_dwDebugFogColor = fogColor; } public float GetDebugFogStart() { return m_fDebugFogStart; } public float GetDebugFogEnd() { return m_fDebugFogEnd; } public float GetDebugFogDensity() { return m_fDebugFogDensity; } public uint GetDebugFogColor() { return m_dwDebugFogColor; } public bool IsAdjusting() { return m_bAdjusting; } public EC_SYSTEM_SETTING GetSystemSettings() { return m_ss; } public EC_VIDEO_SETTING GetVideoSettings() { return m_vs; } public EC_GAME_SETTING GetGameSettings() { return m_gs; } public EC_BLACKLIST_SETTING GetBlackListSettings() { return m_bs; } public EC_COMPUTER_AIDED_SETTING GetComputerAidedSetting() { return m_cas; } public List GetBlockedList() { return m_BlockedArr; } } }