Merge branch 'develop' into feature/skill-data
This commit is contained in:
@@ -15,3 +15,5 @@ MonoBehaviour:
|
||||
lstPrefabDialog:
|
||||
- id: DialogNPC
|
||||
prefab: {fileID: 8237288432181259026, guid: 7653e7e64393ec24c903f0606499b8c4, type: 3}
|
||||
- id: DialogNPCShop
|
||||
prefab: {fileID: 8237288432181259026, guid: eaeb778b6aab3d74299373b3a96b72c4, type: 3}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
@@ -13,6 +14,7 @@ namespace BrewMonster.Scripts
|
||||
private bool _isInitialized = false;
|
||||
|
||||
private Dictionary<string, AsyncOperationHandle<GameObject>> _loadedAssets = new();
|
||||
public event Action OnDispose;
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
@@ -63,10 +65,71 @@ namespace BrewMonster.Scripts
|
||||
/// <summary>
|
||||
/// When the asset is no longer needed, call this method to unload it.
|
||||
/// </summary>
|
||||
/// <param name="assetPath"></param>
|
||||
public void UnloadAsset(string assetPath)
|
||||
/// <param name="assetPath">The asset path used when loading the asset</param>
|
||||
public void ReleaseAsset(string assetPath)
|
||||
{
|
||||
Addressables.Release(assetPath);
|
||||
if (_loadedAssets.TryGetValue(assetPath, out var handle))
|
||||
{
|
||||
if (handle.IsValid())
|
||||
{
|
||||
Addressables.Release(handle);
|
||||
}
|
||||
_loadedAssets.Remove(assetPath);
|
||||
BMLogger.Log($"AddressableManager: Released asset: {assetPath}");
|
||||
}
|
||||
else
|
||||
{
|
||||
BMLogger.LogWarning($"AddressableManager: Asset not found in cache: {assetPath}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release a specific asset by its handle directly.
|
||||
/// </summary>
|
||||
/// <param name="handle">The async operation handle to release</param>
|
||||
public void ReleaseAsset(AsyncOperationHandle<GameObject> handle)
|
||||
{
|
||||
if (handle.IsValid())
|
||||
{
|
||||
Addressables.Release(handle);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release all loaded assets from the cache.
|
||||
/// </summary>
|
||||
public void ReleaseAllAssets()
|
||||
{
|
||||
foreach (var kvp in _loadedAssets)
|
||||
{
|
||||
if (kvp.Value.IsValid())
|
||||
{
|
||||
Addressables.Release(kvp.Value);
|
||||
}
|
||||
}
|
||||
_loadedAssets.Clear();
|
||||
BMLogger.Log("AddressableManager: Released all assets");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if an asset is currently loaded in the cache.
|
||||
/// </summary>
|
||||
/// <param name="assetPath">The asset path to check</param>
|
||||
/// <returns>True if the asset is loaded</returns>
|
||||
public bool IsAssetLoaded(string assetPath)
|
||||
{
|
||||
return _loadedAssets.ContainsKey(assetPath) && _loadedAssets[assetPath].IsValid();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the count of currently loaded assets.
|
||||
/// </summary>
|
||||
public int LoadedAssetCount => _loadedAssets.Count;
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
OnDispose?.Invoke();
|
||||
ReleaseAllAssets();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,4 +348,421 @@ namespace BrewMonster.Scripts
|
||||
FIXMSG_INVALID_NAME_LEN,
|
||||
FIXMSG_FC_RENAME_FACTION,
|
||||
}
|
||||
public enum DescriptipionMsg
|
||||
{
|
||||
// Item description ...
|
||||
ITEMDESC_ATKSPD_VERYSLOW = 0, // 0, Attack speed
|
||||
ITEMDESC_ATKSPD_SLOW,
|
||||
ITEMDESC_ATKSPD_NORMAL,
|
||||
ITEMDESC_ATKSPD_FAST,
|
||||
ITEMDESC_ATKSPD_VERYFAST,
|
||||
|
||||
ITEMDESC_COL_LIGHTBLUE,
|
||||
ITEMDESC_COL_YELLOW,
|
||||
ITEMDESC_COL_PURPLE,
|
||||
ITEMDESC_COL_DARKGOLD,
|
||||
ITEMDESC_COL_WHITE,
|
||||
|
||||
ITEMDESC_COL_GRAY,
|
||||
ITEMDESC_COL_BLUE,
|
||||
ITEMDESC_COL_GREEN,
|
||||
ITEMDESC_COL_RED,
|
||||
ITEMDESC_COL_CYANINE,
|
||||
|
||||
ITEMDESC_NAMESOCKET, // 15, name (socket num)
|
||||
ITEMDESC_CLASSNAME, // Item class name
|
||||
ITEMDESC_ATKSPEED, // Attack speed
|
||||
ITEMDESC_PHYDAMAGE,
|
||||
ITEMDESC_MAGICDAMAGE,
|
||||
|
||||
ITEMDESC_ENDURANCE, // 20
|
||||
ITEMDESC_LEVELREQ,
|
||||
ITEMDESC_STRENGTHREQ,
|
||||
ITEMDESC_AGILITYREQ,
|
||||
ITEMDESC_PRICE,
|
||||
|
||||
ITEMDESC_PHYDEFENCE,
|
||||
ITEMDESC_GOLDDEFENCE,
|
||||
ITEMDESC_WOODDEFENCE,
|
||||
ITEMDESC_WATERDEFENCE,
|
||||
ITEMDESC_FIREDEFENCE,
|
||||
|
||||
ITEMDESC_EARTHDEFENCE,
|
||||
ITEMDESC_NAMENUMBER, // name (number)
|
||||
ITEMDESC_REMAINTIME, // Remained time
|
||||
ITEMDESC_NAME,
|
||||
ITEMDESC_ADDPHYDAMAGE,
|
||||
|
||||
ITEMDESC_ADDMAGICDAMAGE,
|
||||
ITEMDESC_DODGE,
|
||||
ITEMDESC_ADDHP,
|
||||
ITEMDESC_ADDMP,
|
||||
ITEMDESC_NOTDEAL,
|
||||
|
||||
ITEMDESC_WEAPONREQ, // 40
|
||||
ITEMDESC_TASKITEM,
|
||||
ITEMDESC_ATKDISTANCE, // Attack distance
|
||||
ITEMDESC_PHYDEFEXTRA,
|
||||
ITEMDESC_RUNSPEEDEXTRA,
|
||||
|
||||
ITEMDESC_DODGEEXTRA,
|
||||
ITEMDESC_PHYRESIST,
|
||||
ITEMDESC_HPRECOVER,
|
||||
ITEMDESC_MPRECOVER,
|
||||
ITEMDESC_ATKTIME,
|
||||
|
||||
ITEMDESC_CASTTIME,
|
||||
ITEMDESC_DEADLYSTRIKE,
|
||||
ITEMDESC_FIREWORKTIME,
|
||||
ITEMDESC_PETLEVEL,
|
||||
ITEMDESC_ATKRATINGEXTRA,
|
||||
|
||||
ITEMDESC_EXP,
|
||||
ITEMDESC_RANDOMPROP,
|
||||
ITEMDESC_PHYDMGEXTRA,
|
||||
ITEMDESC_MAGICDMGEXTRA,
|
||||
ITEMDESC_CASTSKILL,
|
||||
|
||||
ITEMDESC_MAXPHYDAMAGE, // 60
|
||||
ITEMDESC_MAXMAGICDAMAGE,
|
||||
ITEMDESC_ENDURANCEEXTRA,
|
||||
ITEMDESC_REQEXTRA,
|
||||
ITEMDESC_ALLMAGICDEF,
|
||||
|
||||
ITEMDESC_GOLDDEFEXTRA,
|
||||
ITEMDESC_WOODDEFEXTRA,
|
||||
ITEMDESC_WATERDEFEXTRA,
|
||||
ITEMDESC_FIREDEFEXTRA,
|
||||
ITEMDESC_EARTHDEFEXTRA,
|
||||
|
||||
ITEMDESC_HPEXTRA,
|
||||
ITEMDESC_MPEXTRA,
|
||||
ITEMDESC_ADDATKDIST,
|
||||
ITEMDESC_PROJECTILE,
|
||||
ITEMDESC_ATKRATING,
|
||||
|
||||
ITEMDESC_2STRINGS,
|
||||
ITEMDESC_ADDHPINTIME,
|
||||
ITEMDESC_ADDMPINTIME,
|
||||
ITEMDESC_DECHALFPOISON,
|
||||
ITEMDESC_ANTIDOTE,
|
||||
|
||||
ITEMDESC_ALLMAGICRESIST, // 80
|
||||
ITEMDESC_USEEFFECT,
|
||||
ITEMDESC_RECRUITHP,
|
||||
ITEMDESC_RECRUITMP,
|
||||
ITEMDESC_RECRUITHPMP,
|
||||
|
||||
ITEMDESC_LEVEL,
|
||||
ITEMDESC_USEREQUIREMENT,
|
||||
ITEMDESC_MAXLEVELREQ,
|
||||
ITEMDESC_WEAPONLVLREQ,
|
||||
ITEMDESC_ELEMENTTIME,
|
||||
|
||||
ITEMDESC_ADDFLYSPEED,
|
||||
ITEMDESC_MPCONSUME,
|
||||
ITEMDESC_PROFESSIONREQ,
|
||||
ITEMDESC_RUNSPEED,
|
||||
ITEMDESC_GENDERREQ,
|
||||
|
||||
ITEMDESC_ERRORITEM,
|
||||
ITEMDESC_COLOR,
|
||||
ITEMDESC_PENTAGON,
|
||||
ITEMDESC_COLORRECT,
|
||||
ITEMDESC_UNITPRICE,
|
||||
|
||||
ITEMDESC_REPAIRCOST, // 100
|
||||
ITEMDESC_VALIDTIME,
|
||||
ITEMDESC_VITALITYREQ,
|
||||
ITEMDESC_ENERGYREQ,
|
||||
ITEMDESC_ERRORPROP,
|
||||
|
||||
ITEMDESC_STRENGTH,
|
||||
ITEMDESC_AGILITY,
|
||||
ITEMDESC_VITALITY,
|
||||
ITEMDESC_ENERGY,
|
||||
ITEMDESC_CASTSKILL2,
|
||||
|
||||
ITEMDESC_ADDFLYSPEED2,
|
||||
ITEMDESC_MADEFROM,
|
||||
ITEMDESC_WEAKDIST,
|
||||
ITEMDESC_FOODTYPE,
|
||||
ITEMDESC_FOOD_GRASS,
|
||||
|
||||
ITEMDESC_FOOD_MEAT,
|
||||
ITEMDESC_FOOD_VEGETABLE,
|
||||
ITEMDESC_FOOD_FRUIT,
|
||||
ITEMDESC_FOOD_WATER,
|
||||
ITEMDESC_MOVESPEED,
|
||||
|
||||
ITEMDESC_DAMAGE, // 120
|
||||
ITEMDESC_ATTACKRATE,
|
||||
ITEMDESC_PETSKILL,
|
||||
ITEMDESC_NULL02,
|
||||
ITEMDESC_NULL03,
|
||||
|
||||
// Command description ...
|
||||
CMDDESC_SITDOWN,
|
||||
CMDDESC_WALKRUN,
|
||||
CMDDESC_NORMALATTACK,
|
||||
CMDDESC_FINDTARGET,
|
||||
CMDDESC_ASSISTATTACK,
|
||||
|
||||
CMDDESC_INVITETOTEAM,
|
||||
CMDDESC_LEAVETEAM,
|
||||
CMDDESC_KICKTEAMMEM,
|
||||
CMDDESC_FINDTEAM,
|
||||
CMDDESC_STARTTRADE,
|
||||
|
||||
CMDDESC_SELLBOOTH,
|
||||
CMDDESC_BUYBOOTH,
|
||||
CMDDESC_INVITETOFACTION,
|
||||
CMDDESC_FLY,
|
||||
CMDDESC_PICKUP,
|
||||
|
||||
CMDDESC_GATHER, // 140
|
||||
CMDDESC_RUSHFLY,
|
||||
CMDDESC_BINDBUDDY,
|
||||
CMDDESC_SKILLGROUP,
|
||||
CMDDESC_NULL3,
|
||||
|
||||
// Emotion description ...
|
||||
FACEDESC_WAVEHAND,
|
||||
FACEDESC_NOD,
|
||||
FACEDESC_SHADEHEAD,
|
||||
FACEDESC_SHRUG,
|
||||
FACEDESC_LAUGH,
|
||||
|
||||
FACEDESC_ANGRY,
|
||||
FACEDESC_FAINT,
|
||||
FACEDESC_SAD,
|
||||
FACEDESC_KISSHAND,
|
||||
FACEDESC_SHY,
|
||||
|
||||
FACEDESC_SALUTE,
|
||||
FACEDESC_SITDOWN,
|
||||
FACEDESC_CHARGE,
|
||||
FACEDESC_THINK,
|
||||
FACEDESC_CHALLENGE,
|
||||
|
||||
FACEDESC_WIN, // 160
|
||||
FACEDESC_GAPE,
|
||||
FACEDESC_KISS,
|
||||
FACEDESC_FIGHT,
|
||||
FACEDESC_ATTACK1,
|
||||
|
||||
FACEDESC_ATTACK2,
|
||||
FACEDESC_ATTACK3,
|
||||
FACEDESC_ATTACK4,
|
||||
FACEDESC_DEFENCE,
|
||||
FACEDESC_FALL,
|
||||
|
||||
FACEDESC_FALLONGROUND, // 170
|
||||
FACEDESC_LOOKAROUND,
|
||||
FACEDESC_DANCE,
|
||||
FACEDESC_FASHIONWEAPON,
|
||||
FACEDESC_NULL01,
|
||||
|
||||
ITEMDESC_EXPIRETIME_DAY,
|
||||
ITEMDESC_EXPIRETIME_HOUR_MIN,
|
||||
ITEMDESC_EXPIRETIME_MIN_SEC,
|
||||
ITEMDESC_EXPIRETIME_SECOND,
|
||||
ITEMDESC_REFINETICKET1,
|
||||
|
||||
ITEMDESC_REFINETICKET2, // 180
|
||||
ITEMDESC_DESTROYINGNAME,
|
||||
ITEMDESC_EQUIP_BIND,
|
||||
ITEMDESC_AUTOHP1,
|
||||
ITEMDESC_AUTOHP2,
|
||||
|
||||
ITEMDESC_AUTOMP1,
|
||||
ITEMDESC_AUTOMP2,
|
||||
ITEMDESC_COOLTIME,
|
||||
ITEMDESC_DOUBLEEXP_TIME,
|
||||
ITEMDESC_EQUIP_BINDAFTERUSE,
|
||||
|
||||
ITEMDESC_COL2_START, // 190
|
||||
ITEMDESC_COL2_BLUEGREEN,
|
||||
ITEMDESC_COL2_RED,
|
||||
ITEMDESC_COL2_BRIGHTBLUE,
|
||||
ITEMDESC_COL2_NULL02,
|
||||
|
||||
ITEMDESC_COL2_NULL03,
|
||||
ITEMDESC_COL2_NULL04,
|
||||
ITEMDESC_COL2_NULL05,
|
||||
ITEMDESC_COL2_NULL06,
|
||||
ITEMDESC_COL2_NULL07,
|
||||
|
||||
ITEMDESC_SPOUSE_MALE, // 200
|
||||
ITEMDESC_SPOUSE_FEMALE,
|
||||
ITEMDESC_ATK_DEGREE,
|
||||
ITEMDESC_DEF_DEGREE,
|
||||
ITEMDESC_GOBLIN_REFINE_LEVEL,
|
||||
|
||||
ITEMDESC_GOBLIN_LEVEL,
|
||||
ITEMDESC_GOBLIN_RANDPT,
|
||||
ITEMDESC_GOBLIN_ENERGY,
|
||||
ITEMDESC_GOBLIN_ENERGY_RESTORE,
|
||||
ITEMDESC_GOBLIN_STAMINA,
|
||||
|
||||
ITEMDESC_GOBLIN_REFINE_EFFECT, // 210
|
||||
ITEMDESC_GOBLIN_TRADE_PROTECT,
|
||||
ITEMDESC_GOBLIN_TRADE_UNPROTECT,
|
||||
ITEMDESC_GOBLIN_CANTRADE,
|
||||
ITEMDESC_EXPPILL_EXP,
|
||||
|
||||
ITEMDESC_GOBLINEQUIP_POS,
|
||||
ITEMDESC_GOBLINEQUIP_LEVELREQ,
|
||||
ITEMDESC_GOBLINEQUIP_GOLD,
|
||||
ITEMDESC_GOBLINEQUIP_WOOD,
|
||||
ITEMDESC_GOBLINEQUIP_WATER,
|
||||
|
||||
ITEMDESC_GOBLINEQUIP_FIRE, // 220
|
||||
ITEMDESC_GOBLINEQUIP_EARTH,
|
||||
ITEMDESC_GOBLINEQUIP_POS_1,
|
||||
ITEMDESC_GOBLINEQUIP_POS_2,
|
||||
ITEMDESC_GOBLINEQUIP_POS_3,
|
||||
|
||||
ITEMDESC_GOBLINEQUIP_POS_4,
|
||||
ITEMDESC_HASRANDOM_PROP,
|
||||
ITEMDESC_GOBLIN_GROW_DEGREE,
|
||||
ITEMDESC_SPECIAL_PROP,
|
||||
ITEMDESC_DEAD_PROTECT,
|
||||
|
||||
ITEMDESC_NO_DROP, // 230
|
||||
ITEMDESC_NO_TRADE,
|
||||
ITEMDESC_NO_PLAYER_TRADE,
|
||||
ITEMDESC_LEAVE_SCENE_DISAPEAR,
|
||||
ITEMDESC_USE_AFTER_PICK_UP,
|
||||
|
||||
ITEMDESC_DROP_WHEN_DEAD,
|
||||
ITEMDESC_DROP_WHEN_OFFLINE,
|
||||
ITEMDESC_SELL_COL_NUM,
|
||||
ITEMDESC_BUY_COL_NUM,
|
||||
ITEMDESC_MAX_BOOTH_NAME,
|
||||
|
||||
ITEMDESC_UNREPAIRABLE, // 240
|
||||
ITEMDESC_TYPE,
|
||||
ITEMDESC_ATTACK_ITEM,
|
||||
ITEMDESC_DEFENCE_ITEM,
|
||||
ITEMDESC_BATTLE_ITEM,
|
||||
|
||||
ITEMDESC_USE_REGION,
|
||||
ITEMDESC_CONSUME_COUNT,
|
||||
ITEMDESC_SKILL_EFFECT,
|
||||
ITEMDESC_SPECIAL_DESC,
|
||||
ITEMDESC_CONSUME_ANYWAY,
|
||||
|
||||
ITEMDESC_USE_EFFECT, // 250
|
||||
ITEMDESC_CANNOT_USE_IN_BATTLE,
|
||||
ITEMDESC_EQUIP_DESTROYING,
|
||||
ITEMDESC_EQUIP_REPAIR_NEED_ITEM,
|
||||
ITEMDESC_EQUIP_REPAIR_NEED_ITEMCNT,
|
||||
|
||||
ITEMDESC_TOTAL_DEFENCE_ADD,
|
||||
ITEMDESC_NO_USER_TRASH,
|
||||
ITEMDESC_INC_SKILL_ABILITY_REQUIRE_LEVEL,
|
||||
ITEMDESC_INC_SKILL_ABILITY_RATIO,
|
||||
ITEMDESC_REPUTATION_REQ,
|
||||
|
||||
ITEMDESC_ADDDAMAGE, // 260
|
||||
ITEMDESC_PROFVIEW,
|
||||
ITEMDESC_WEDDING_BOOKCARD,
|
||||
ITEMDESC_WEDDING_INVITECARD,
|
||||
ITEMDESC_SOULPOWER,
|
||||
|
||||
ITEMDESC_LASTTIME_DAY,
|
||||
ITEMDESC_LASTTIME_HOUR_MIN,
|
||||
ITEMDESC_LASTTIME_MIN_SEC,
|
||||
ITEMDESC_LASTTIME_SECOND,
|
||||
ITEMDESC_GOLDRESIST,
|
||||
|
||||
ITEMDESC_WOODRESIST, // 270
|
||||
ITEMDESC_WATERRESIST,
|
||||
ITEMDESC_FIRERESIST,
|
||||
ITEMDESC_EARTHRESIST,
|
||||
ITEMDESC_CAN_WEBTRADE,
|
||||
|
||||
ITEMDESC_PRICE_SEPERATOR, // 271
|
||||
ITEMDESC_PENETRATION,
|
||||
ITEMDESC_RESILIENCE,
|
||||
ITEMDESC_FORCE_REQUIRE,
|
||||
ITEMDESC_FORCE_REPU_TOTAL,
|
||||
|
||||
ITEMDESC_FORCE_REPU_INC_RATIO, // 280
|
||||
ITEMDESC_EQUIP_BIND_ONLY,
|
||||
ITEMDESC_REQUIRE_MAX_LEVEL,
|
||||
ITEMDESC_DEFENCE,
|
||||
ITEMDESC_ONE_HANDED_WEAPON,
|
||||
|
||||
ITEMDESC_TWO_HANDED_WEAPON,
|
||||
ITEMDESC_TWO_HANDED_POLEARM,
|
||||
ITEMDESC_TWO_HANDED_HEAVY,
|
||||
ITEMDESC_TWO_HANDED_POLEHEAVY,
|
||||
ITEMDESC_WHIP,
|
||||
|
||||
ITEMDESC_BOW, // 290
|
||||
ITEMDESC_CROSSBOW,
|
||||
ITEMDESC_BOXING_GLOVES,
|
||||
ITEMDESC_PIKE,
|
||||
ITEMDESC_EMPTY_HANDED,
|
||||
|
||||
ITEMDESC_DAGGER, // 295
|
||||
ITEMDESC_TALISMAN,
|
||||
ITEMDESC_EQUIPMARK,
|
||||
ITEMDESC_PET_EVO_NAME,
|
||||
ITEMDESC_PET_ATK_INHERIT,
|
||||
|
||||
ITEMDESC_PET_DEF_INHERIT, // 300
|
||||
ITEMDESC_PET_HP_INHERIT,
|
||||
ITEMDESC_PET_ATKLVL_INHERIT,
|
||||
ITEMDESC_PET_DEFLVL_INHERIT,
|
||||
ITEMDESC_PET_NATURE,
|
||||
|
||||
ITEMDESC_CROSSSERVER_ERROR, // 305
|
||||
ITEMDESC_USE_IN_SANCTUARY_ONLY,
|
||||
ITEMDESC_SYSMODULE_FM_GT, // GT
|
||||
ITEMDESC_SYSMODULE_FM_TOUCH,
|
||||
ITEMDESC_SYSMODULE_FM_ROBOT,
|
||||
|
||||
ITEMDESC_SYSMODULE_FM_WIKI, // 310
|
||||
ITEMDESC_SYSMODULE_FM_OFFLINESHOP,
|
||||
ITEMDESC_SYSMODULE_FM_BORADCAST, // ��������
|
||||
ITEMDESC_SYSMODULE_FM_MATCHSYS,
|
||||
ITEMDESC_SYSMODULE_FM_ADDEXP,
|
||||
|
||||
|
||||
CMDDESC_TWO_KISS, // �������� 315
|
||||
ITEMDESC_GENERALCARD_RANK,
|
||||
ITEMDESC_LEADERSHIP,
|
||||
ITEMDESC_GENERALCARD_MAXLEVEL,
|
||||
ITEMDESC_GENERALCARD_LEVEL,
|
||||
|
||||
ITEMDESC_GENERALCARD_REINCARNATION_COUNT,
|
||||
ITEMDESC_VIGOUR,
|
||||
ITEMDESC_GENERALCARD_EXP_CANGIVE,
|
||||
ITEMDESC_GENERALCARD_SHOWORDER,
|
||||
ITEMDESC_GENERALCARD_SUITE_PROPRADIO,
|
||||
|
||||
CMDDESC_JUMP_TRICK,
|
||||
CMDDESC_RUN_TRICK,
|
||||
ITEMDESC_GENERALCARD_EXP,
|
||||
ITEMDESC_GENERALCARD_TYPE1,
|
||||
ITEMDESC_GENERALCARD_TYPE2,
|
||||
|
||||
ITEMDESC_GENERALCARD_TYPE3,
|
||||
ITEMDESC_GENERALCARD_TYPE4,
|
||||
ITEMDESC_GENERALCARD_TYPE5,
|
||||
ITEMDESC_GENERALCARD_TYPE6,
|
||||
ITEMDESC_CURRENT_PROFESSION,
|
||||
|
||||
ITEMDESC_SYSMODULE_FM_AUTOHPMP, // 335
|
||||
ITEMDESC_FLYSWORD_NOIMPROVED,
|
||||
ITEMDESC_FLYSWORD_IMPROVE,
|
||||
ITEMDESC_VISIT_HTTP_WITH_TOKEN,
|
||||
ITEMDESC_OBOROKNIFE_WEAPON,
|
||||
|
||||
ITEMDESC_SICKLE_WEAPON, // 340
|
||||
ITEMDESC_ADDRIDEONPETSPEED,
|
||||
};
|
||||
}
|
||||
@@ -7,6 +7,8 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using ModelRenderer.Scripts.Common;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BrewMonster
|
||||
@@ -745,6 +747,15 @@ public class CECAttackEvent
|
||||
// TODO: Implement AddSkillGfxEvent
|
||||
// CECGameRun.Instance.GetWorld().GetSkillGfxMan().AddSkillGfxEvent(m_idHost, data.idTarget,
|
||||
// pszFlyGFX, pszHitGFX, m_timeToDoDamage, false, GfxMoveMode.enumLinearMove, 1, 0, null, vFlyScale, vHitScale, data.dwModifier);
|
||||
var target = EC_ManMessageMono.Instance?.GetObject(data.idTarget, 0)?.gameObject.transform;
|
||||
if (target == null)
|
||||
{
|
||||
BMLogger.LogError("Target is null!");
|
||||
return false;
|
||||
}
|
||||
//todo: not set default like this
|
||||
var fullGfx = "程序联入/击中/拳套击中";
|
||||
CECGameRun.Instance.ShowVfx(fullGfx, target.position, null, 1f);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -762,7 +773,8 @@ public class CECAttackEvent
|
||||
{
|
||||
TARGET_DATA data = m_targets[i];
|
||||
|
||||
// string fullGfx = pWeaponType.GetFileHitGfx();
|
||||
var fullGfx = ByteToStringUtils.ByteArrayToCP936String(pWeaponType.file_hitgfx);
|
||||
fullGfx = fullGfx.Substring(4);
|
||||
// szGFX = fullGfx.Length > 4 ? fullGfx.Substring(4) : string.Empty; // skip gfx/
|
||||
// szGFX = pWeaponType.file_hitgfx
|
||||
// if ((data.dwModifier & (uint)MOD.MOD_NULLITY) != 0)
|
||||
@@ -780,9 +792,10 @@ public class CECAttackEvent
|
||||
BMLogger.LogError("Target is null!");
|
||||
return false;
|
||||
}
|
||||
|
||||
szGFX = "程序联入/击中/拳套击中";
|
||||
CECGameRun.Instance.ShowVfx(szGFX, target.position, null, 1f);
|
||||
|
||||
//todo: not set default like this
|
||||
fullGfx = "程序联入/击中/拳套击中";
|
||||
CECGameRun.Instance.ShowVfx(fullGfx, target.position, null, 1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,24 +28,9 @@ namespace BrewMonster.Scripts.Managers
|
||||
[SerializeField] private TextOutlet nameText;
|
||||
[SerializeField] private TextOutlet descriptionText;
|
||||
[SerializeField] private TextOutlet extendedDescText;
|
||||
[SerializeField] private TextOutlet countText;
|
||||
[SerializeField] private TextOutlet stateText;
|
||||
[SerializeField] private TextOutlet expireText;
|
||||
[SerializeField] private Button equipButton;
|
||||
[SerializeField] private Button dropButton;
|
||||
|
||||
[Header("Equipment Details (assign in Inspector)")]
|
||||
[SerializeField] private TextOutlet levelReqText;
|
||||
[SerializeField] private TextOutlet statsReqText;
|
||||
[SerializeField] private TextOutlet enduranceText;
|
||||
[SerializeField] private TextOutlet repairCostText;
|
||||
[SerializeField] private TextOutlet propertiesText;
|
||||
[SerializeField] private TextOutlet refineText;
|
||||
[SerializeField] private TextOutlet makerText;
|
||||
[SerializeField] private TextOutlet priceText;
|
||||
[SerializeField] private TextOutlet holesText;
|
||||
[SerializeField] private TextOutlet suiteText;
|
||||
|
||||
[Header("Inventory Settings")]
|
||||
[SerializeField] private bool autoRefresh = true;
|
||||
[SerializeField] private float refreshInterval = 1.0f;
|
||||
@@ -68,6 +53,12 @@ namespace BrewMonster.Scripts.Managers
|
||||
private static bool s_hasPendingCash;
|
||||
private static int s_pendingCashAmount;
|
||||
|
||||
// Flags to prevent log spam for extended description warnings
|
||||
// 防止扩展描述警告日志刷屏的标志
|
||||
private static bool m_HasLoggedExtDescNull = false;
|
||||
private static bool m_HasLoggedExtDescNotInit = false;
|
||||
private static bool m_HasLoggedExtDescError = false;
|
||||
|
||||
private InventoryModel model;
|
||||
private InventoryView view;
|
||||
|
||||
@@ -681,11 +672,20 @@ namespace BrewMonster.Scripts.Managers
|
||||
}
|
||||
}
|
||||
|
||||
public void RefreshLayout(GameObject gameObject)
|
||||
{
|
||||
var parent = gameObject.GetComponent<RectTransform>();
|
||||
|
||||
// Force Unity to rebuild layout immediately
|
||||
parent.ForceUpdateRectTransforms();
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(parent);
|
||||
}
|
||||
private void ShowDetailPanel(bool show)
|
||||
{
|
||||
if (detailPanelRoot != null)
|
||||
{
|
||||
detailPanelRoot.SetActive(show);
|
||||
RefreshLayout(detailPanelRoot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -807,268 +807,108 @@ namespace BrewMonster.Scripts.Managers
|
||||
return;
|
||||
}
|
||||
|
||||
// Get user-friendly text from string tables
|
||||
// Get user-friendly name
|
||||
string itemName = EC_IvtrItemUtils.Instance.ResolveItemName(item.m_tid);
|
||||
string itemDescription = GetItemDescription(item.m_tid);
|
||||
string itemExtendedDesc = GetItemExtendedDescription(item.m_tid);
|
||||
|
||||
// Display basic content
|
||||
nameText?.Set(string.IsNullOrEmpty(itemName) ? $"Item {item.m_tid}" : itemName);
|
||||
descriptionText?.Set(itemDescription ?? "No description available");
|
||||
extendedDescText?.Set(itemExtendedDesc ?? "");
|
||||
countText?.Set($"Count: {item.m_iCount}");
|
||||
|
||||
// Format state properly
|
||||
string stateTextValue = GetItemStateText(item.State);
|
||||
stateText?.Set(stateTextValue);
|
||||
|
||||
// Format expiration date properly
|
||||
if (item.m_expire_date > 0)
|
||||
{
|
||||
DateTime expireDate = DateTimeOffset.FromUnixTimeSeconds(item.m_expire_date).DateTime;
|
||||
expireText?.Set($"Expires: {expireDate:yyyy-MM-dd HH:mm}");
|
||||
}
|
||||
else
|
||||
{
|
||||
expireText?.Set("No expiration");
|
||||
}
|
||||
|
||||
// Display equipment-specific information
|
||||
// Centralised description:
|
||||
// - For equipment, prefer EC_IvtrEquip description (includes stats, addons, sockets, etc.)
|
||||
// - For other items, use EC_IvtrItem.GetDesc which reads from string tables.
|
||||
string fullDesc = null;
|
||||
if (showEquipmentDetails && currentSelectedEquipment != null)
|
||||
{
|
||||
FillEquipmentDetails();
|
||||
fullDesc = currentSelectedEquipment.GetNormalDesc();
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearEquipmentDetails();
|
||||
fullDesc = item.GetDesc();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(fullDesc))
|
||||
{
|
||||
// Replace C++ style "\r" line separators with real newlines for TMP
|
||||
fullDesc = fullDesc.Replace("\\r", "\n");
|
||||
descriptionText?.Set(fullDesc);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback to legacy string-table description if centralised pipeline returns empty
|
||||
string itemDescription = GetItemDescription(item.m_tid);
|
||||
descriptionText?.Set(itemDescription ?? "No description available");
|
||||
}
|
||||
|
||||
// Get extended description exactly like C++ code: g_pGame->GetItemExtDesc(m_tid)
|
||||
// C++ code doesn't check IsInitialized() - it just calls GetWideString() directly
|
||||
// 完全按照C++代码获取扩展描述:g_pGame->GetItemExtDesc(m_tid)
|
||||
// C++代码不检查IsInitialized() - 它直接调用GetWideString()
|
||||
string szExtDesc = null;
|
||||
try
|
||||
{
|
||||
var itemExtDescTab = EC_Game.GetItemExtDesc();
|
||||
if (itemExtDescTab != null)
|
||||
{
|
||||
// First try to get mapped message ID (like TryGetItemExtDesc does)
|
||||
// 首先尝试获取映射的消息ID(如TryGetItemExtDesc所做)
|
||||
if (EC_Game.TryGetItemMsg(item.m_tid, out int messageId, out int displayMode))
|
||||
{
|
||||
szExtDesc = itemExtDescTab.GetWideString(messageId);
|
||||
}
|
||||
|
||||
// Fallback: direct lookup using tid (exactly like C++: m_ItemExtDesc.GetWideString(tid))
|
||||
// 回退:直接使用tid查找(完全像C++:m_ItemExtDesc.GetWideString(tid))
|
||||
if (string.IsNullOrEmpty(szExtDesc))
|
||||
{
|
||||
szExtDesc = itemExtDescTab.GetWideString(item.m_tid);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
// Only log once to avoid spam
|
||||
// 仅记录一次以避免垃圾日志
|
||||
if (!m_HasLoggedExtDescError)
|
||||
{
|
||||
Debug.LogWarning($"[InventoryUI] Error getting extended description: {ex.Message}");
|
||||
m_HasLoggedExtDescError = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Display extended description if found (exactly like C++ checks: if (!szExtDesc || !szExtDesc[0]))
|
||||
// 如果找到扩展描述则显示(完全像C++检查:if (!szExtDesc || !szExtDesc[0]))
|
||||
string displayText = !string.IsNullOrEmpty(szExtDesc)
|
||||
? szExtDesc.Replace("\\r", "\n")
|
||||
: "";
|
||||
|
||||
// Debug logging to diagnose issues
|
||||
// 调试日志以诊断问题
|
||||
if (string.IsNullOrEmpty(displayText))
|
||||
{
|
||||
Debug.Log($"[InventoryUI] Extended description is empty for tid={item.m_tid}. szExtDesc was null/empty.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"[InventoryUI] Found extended description for tid={item.m_tid}, length={displayText.Length}");
|
||||
}
|
||||
|
||||
// Setup equip and drop buttons
|
||||
SetupEquipButton(package, item);
|
||||
SetupDropButton(package, item);
|
||||
|
||||
// Show panel first
|
||||
// 先显示面板
|
||||
ShowDetailPanel(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fill equipment-specific details
|
||||
/// </summary>
|
||||
private void FillEquipmentDetails()
|
||||
{
|
||||
if (currentSelectedEquipment == null)
|
||||
// Set text directly - if this causes rebuild issues, we'll use coroutine
|
||||
// 直接设置文本 - 如果这导致重建问题,我们将使用协程
|
||||
if (extendedDescText != null)
|
||||
{
|
||||
ClearEquipmentDetails();
|
||||
return;
|
||||
extendedDescText.Set(displayText);
|
||||
Debug.Log($"[InventoryUI] Set extended description text, extendedDescText is {(extendedDescText == null ? "null" : "not null")}");
|
||||
}
|
||||
|
||||
// Level Requirements
|
||||
if (levelReqText != null)
|
||||
else
|
||||
{
|
||||
string levelReq = "";
|
||||
if (currentSelectedEquipment.LevelReq > 0)
|
||||
{
|
||||
levelReq = $"Level: {currentSelectedEquipment.LevelReq}";
|
||||
}
|
||||
if (currentSelectedEquipment.ProfReq > 0)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(levelReq)) levelReq += "\\r";
|
||||
levelReq += $"Profession: {currentSelectedEquipment.ProfReq}";
|
||||
}
|
||||
levelReqText.Set(levelReq);
|
||||
Debug.LogWarning("[InventoryUI] extendedDescText is null! Check Inspector assignment.");
|
||||
}
|
||||
|
||||
// Stats Requirements
|
||||
if (statsReqText != null)
|
||||
{
|
||||
List<string> reqs = new List<string>();
|
||||
if (currentSelectedEquipment.StrengthReq > 0) reqs.Add($"Str: {currentSelectedEquipment.StrengthReq}");
|
||||
if (currentSelectedEquipment.AgilityReq > 0) reqs.Add($"Agi: {currentSelectedEquipment.AgilityReq}");
|
||||
if (currentSelectedEquipment.VitalityReq > 0) reqs.Add($"Vit: {currentSelectedEquipment.VitalityReq}");
|
||||
if (currentSelectedEquipment.EnergyReq > 0) reqs.Add($"Ene: {currentSelectedEquipment.EnergyReq}");
|
||||
if (currentSelectedEquipment.ReputationReq > 0) reqs.Add($"Rep: {currentSelectedEquipment.ReputationReq}");
|
||||
|
||||
statsReqText.Set(string.Join("\\r", reqs));
|
||||
}
|
||||
|
||||
// Endurance
|
||||
if (enduranceText != null)
|
||||
{
|
||||
if (currentSelectedEquipment.MaxEndurance > 0)
|
||||
{
|
||||
int curEndurance = EC_IvtrEquip.VisualizeEndurance(currentSelectedEquipment.CurEndurance);
|
||||
int maxEndurance = EC_IvtrEquip.VisualizeEndurance(currentSelectedEquipment.MaxEndurance);
|
||||
string endurance = $"Endurance: {curEndurance}/{maxEndurance}";
|
||||
|
||||
if (currentSelectedEquipment.IsDestroying())
|
||||
{
|
||||
endurance += " (DESTROYED)";
|
||||
}
|
||||
else if (currentSelectedEquipment.CurEndurance < currentSelectedEquipment.MaxEndurance)
|
||||
{
|
||||
endurance += " (Damaged)";
|
||||
}
|
||||
|
||||
enduranceText.Set(endurance);
|
||||
}
|
||||
else
|
||||
{
|
||||
enduranceText.Set("Endurance: N/A");
|
||||
}
|
||||
}
|
||||
|
||||
// Repair Cost
|
||||
if (repairCostText != null)
|
||||
{
|
||||
if (currentSelectedEquipment.IsRepairable())
|
||||
{
|
||||
int repairCost = currentSelectedEquipment.GetRepairCost();
|
||||
repairCostText.Set($"Repair Cost: {repairCost}");
|
||||
}
|
||||
else
|
||||
{
|
||||
repairCostText.Set("Repair Cost: N/A");
|
||||
}
|
||||
}
|
||||
|
||||
// Properties + Base Stats + Engraved + Stones (more closely matching original)
|
||||
if (propertiesText != null)
|
||||
{
|
||||
List<string> lines = new List<string>();
|
||||
|
||||
// Base stats decoded from element essence (damage/defense/speed/range/resists)
|
||||
string baseStats = currentSelectedEquipment.GetBaseStatsForDisplay();
|
||||
if (!string.IsNullOrEmpty(baseStats))
|
||||
{
|
||||
lines.Add(baseStats);
|
||||
}
|
||||
|
||||
// Add-on properties from detail payload (non-embedded, non-suite, non-engraved)
|
||||
if (currentSelectedEquipment.Props.Count > 0)
|
||||
{
|
||||
foreach (var prop in currentSelectedEquipment.Props)
|
||||
{
|
||||
if (!prop.Embed && !prop.Suite && !prop.Engraved)
|
||||
{
|
||||
string propDesc = currentSelectedEquipment.FormatPropDesc(prop);
|
||||
if (!string.IsNullOrEmpty(propDesc))
|
||||
{
|
||||
lines.Add(propDesc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Engraved properties (displayed after normal add-ons)
|
||||
foreach (var prop in currentSelectedEquipment.Props)
|
||||
{
|
||||
if (prop.Engraved)
|
||||
{
|
||||
string propDesc = currentSelectedEquipment.FormatPropDesc(prop);
|
||||
if (!string.IsNullOrEmpty(propDesc))
|
||||
{
|
||||
lines.Add(propDesc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Socketed stones (show name and a basic description)
|
||||
if (currentSelectedEquipment.Holes != null && currentSelectedEquipment.Holes.Count > 0)
|
||||
{
|
||||
foreach (int holeTid in currentSelectedEquipment.Holes)
|
||||
{
|
||||
if (holeTid == 0) continue;
|
||||
string stoneName = EC_IvtrItemUtils.Instance.ResolveItemName(holeTid);
|
||||
// Try to fetch a description from string tables; fallback to name if unavailable
|
||||
string stoneDesc = GetItemDescription(holeTid) ?? stoneName;
|
||||
if (!string.IsNullOrEmpty(stoneName))
|
||||
{
|
||||
lines.Add($"{stoneName}: {stoneDesc}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string combined = string.Join("\\r", lines);
|
||||
propertiesText.Set(combined);
|
||||
}
|
||||
|
||||
// Refinement
|
||||
if (refineText != null)
|
||||
{
|
||||
if (currentSelectedEquipment.RefineLvl > 0)
|
||||
{
|
||||
refineText.Set($"Refinement Level: +{currentSelectedEquipment.RefineLvl}");
|
||||
}
|
||||
else
|
||||
{
|
||||
refineText.Set("Refinement: None");
|
||||
}
|
||||
}
|
||||
|
||||
// Maker Information
|
||||
if (makerText != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(currentSelectedEquipment.Maker))
|
||||
{
|
||||
makerText.Set($"Maker: {currentSelectedEquipment.Maker}");
|
||||
}
|
||||
else
|
||||
{
|
||||
makerText.Set("Maker: Unknown");
|
||||
}
|
||||
}
|
||||
|
||||
// Price
|
||||
if (priceText != null)
|
||||
{
|
||||
int scaledPrice = currentSelectedEquipment.GetScaledPrice();
|
||||
priceText.Set($"Price: {scaledPrice}");
|
||||
}
|
||||
|
||||
// Holes
|
||||
if (holesText != null)
|
||||
{
|
||||
if (currentSelectedEquipment.Holes.Count > 0)
|
||||
{
|
||||
int emptyHoles = currentSelectedEquipment.GetEmptyHoleNum();
|
||||
int totalHoles = currentSelectedEquipment.Holes.Count;
|
||||
holesText.Set($"Holes: {totalHoles - emptyHoles}/{totalHoles} filled");
|
||||
}
|
||||
else
|
||||
{
|
||||
holesText.Set("Holes: None");
|
||||
}
|
||||
}
|
||||
|
||||
// Suite Information
|
||||
if (suiteText != null)
|
||||
{
|
||||
int suiteId = currentSelectedEquipment.GetSuiteID();
|
||||
if (suiteId > 0)
|
||||
{
|
||||
suiteText.Set($"Suite Equipment: {suiteId}");
|
||||
}
|
||||
else
|
||||
{
|
||||
suiteText.Set("Suite: None");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear equipment details
|
||||
/// </summary>
|
||||
private void ClearEquipmentDetails()
|
||||
{
|
||||
levelReqText?.Set("");
|
||||
statsReqText?.Set("");
|
||||
enduranceText?.Set("");
|
||||
repairCostText?.Set("");
|
||||
propertiesText?.Set("");
|
||||
refineText?.Set("");
|
||||
makerText?.Set("");
|
||||
priceText?.Set("");
|
||||
holesText?.Set("");
|
||||
suiteText?.Set("");
|
||||
}
|
||||
|
||||
private void SetupEquipButton(byte package, EC_IvtrItem item)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,8 @@ using BrewMonster;
|
||||
using ModelRenderer.Scripts.Common;
|
||||
using ModelRenderer.Scripts.GameData;
|
||||
using UnityEngine;
|
||||
using PerfectWorld.Scripts.Managers;
|
||||
using BrewMonster.Network;
|
||||
|
||||
namespace BrewMonster.Scripts.Managers
|
||||
{
|
||||
@@ -1178,7 +1180,35 @@ namespace BrewMonster.Scripts.Managers
|
||||
/// </summary>
|
||||
protected virtual string GetNormalDesc(bool bRepair)
|
||||
{
|
||||
return string.IsNullOrEmpty(m_strDesc) ? null : m_strDesc;
|
||||
// Build a simple but centralized description:
|
||||
// - Name
|
||||
// - String-table description (if any)
|
||||
// - Extended description (if any)
|
||||
m_strDesc = string.Empty;
|
||||
|
||||
// Item name line
|
||||
string name = GetName();
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
AddDescText(0, true, name);
|
||||
}
|
||||
|
||||
// Core description from item_desc.txt (via EC_Game / TryGetItemMsg)
|
||||
string mainDesc = TryGetItemMainDesc();
|
||||
if (!string.IsNullOrEmpty(mainDesc))
|
||||
{
|
||||
AddDescText(0, true, mainDesc);
|
||||
}
|
||||
|
||||
// Extended description from item_ext_desc.txt
|
||||
string extDesc = TryGetItemExtDesc();
|
||||
if (!string.IsNullOrEmpty(extDesc))
|
||||
{
|
||||
AddDescText(0, true, extDesc);
|
||||
}
|
||||
|
||||
TrimLastReturn();
|
||||
return m_strDesc;
|
||||
}
|
||||
|
||||
protected virtual string GetBoothBuyDesc()
|
||||
@@ -1193,7 +1223,10 @@ namespace BrewMonster.Scripts.Managers
|
||||
|
||||
protected virtual void AddPriceDesc(int col, bool bRepair)
|
||||
{
|
||||
// Full text/color building uses string tables; keep a minimal stub for now.
|
||||
// Basic price string using scaled price; color is ignored at this level.
|
||||
int price = GetScaledPrice();
|
||||
if (price <= 0) return;
|
||||
AddDescText(col, true, "Price: {0}", price);
|
||||
}
|
||||
|
||||
protected virtual void AddProfReqDesc(int iProfReq)
|
||||
@@ -1217,29 +1250,215 @@ namespace BrewMonster.Scripts.Managers
|
||||
m_strDesc += "\n";
|
||||
}
|
||||
|
||||
// Add extend description to description string / 添加扩展描述到描述字符串
|
||||
protected void AddExtDescText()
|
||||
{
|
||||
// Extension description comes from game configs; keep stubbed for now.
|
||||
// Get extended description from item_ext_desc.txt using tid / 使用tid从item_ext_desc.txt获取扩展描述
|
||||
string szExtDesc = TryGetItemExtDesc();
|
||||
// Note: Original C++ had early return commented out / 注意:原始C++代码的早期返回被注释掉了
|
||||
// if (!szExtDesc || !szExtDesc[0])
|
||||
// return;
|
||||
|
||||
m_strDesc += "\\r";
|
||||
|
||||
bool bAddLine = true;
|
||||
// Add special properties description / 添加特殊属性描述
|
||||
var pDescTab = EC_Game.GetItemDesc();
|
||||
// Note: ITEMDESC_COL2_BRIGHTBLUE constant - adjust based on actual string table / 注意:ITEMDESC_COL2_BRIGHTBLUE常量 - 根据实际字符串表调整
|
||||
int green = 1000; // ITEMDESC_COL2_BRIGHTBLUE placeholder - adjust this value
|
||||
|
||||
if (m_iCID != (int)InventoryClassId.ICID_GOBLIN) // goblin does not need to display these special properties / 地精不需要显示这些特殊属性
|
||||
{
|
||||
// Exact C++ logic: (PROC_NO_USER_TRASH) || (!PROC_BINDING && (PROC_DROPWHENDIE || ...))
|
||||
// 精确的C++逻辑:(PROC_NO_USER_TRASH) || (!PROC_BINDING && (PROC_DROPWHENDIE || ...))
|
||||
if ((m_iProcType & (int)ProcType.PROC_NO_USER_TRASH) != 0
|
||||
|| (!((m_iProcType & (int)ProcType.PROC_BINDING) != 0)
|
||||
&& ((m_iProcType & (int)ProcType.PROC_DROPWHENDIE) != 0
|
||||
|| (m_iProcType & (int)ProcType.PROC_DROPPABLE) != 0
|
||||
|| (m_iProcType & (int)ProcType.PROC_SELLABLE) != 0
|
||||
|| (m_iProcType & (int)ProcType.PROC_TRADEABLE) != 0
|
||||
|| (m_iProcType & (int)ProcType.PROC_DISAPEAR) != 0
|
||||
|| (m_iProcType & (int)ProcType.PROC_USE) != 0
|
||||
|| (m_iProcType & (int)ProcType.PROC_DEADDROP) != 0
|
||||
|| (m_iProcType & (int)ProcType.PROC_OFFLINE) != 0
|
||||
|| (m_iProcType & (int)ProcType.PROC_UNREPAIRABLE) != 0)))
|
||||
{
|
||||
bAddLine = false;
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
string szCol = pDescTab.GetWideString(green);
|
||||
if (!string.IsNullOrEmpty(szCol))
|
||||
{
|
||||
m_strDesc += szCol;
|
||||
}
|
||||
}
|
||||
|
||||
// Note: These message IDs are placeholders - adjust based on actual string table / 注意:这些消息ID是占位符 - 根据实际字符串表调整
|
||||
if ((m_iProcType & (int)ProcType.PROC_DROPWHENDIE) != 0)
|
||||
{
|
||||
m_strDesc += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
// ITEMDESC_DEAD_PROTECT placeholder - adjust this value
|
||||
string desc = pDescTab.GetWideString(2000); // Placeholder ID
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
m_strDesc += desc;
|
||||
}
|
||||
}
|
||||
if ((m_iProcType & (int)ProcType.PROC_DROPPABLE) != 0)
|
||||
{
|
||||
m_strDesc += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
// ITEMDESC_NO_DROP placeholder - adjust this value
|
||||
string desc = pDescTab.GetWideString(2001); // Placeholder ID
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
m_strDesc += desc;
|
||||
}
|
||||
}
|
||||
if ((m_iProcType & (int)ProcType.PROC_SELLABLE) != 0)
|
||||
{
|
||||
m_strDesc += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
// ITEMDESC_NO_TRADE placeholder - adjust this value
|
||||
string desc = pDescTab.GetWideString(2002); // Placeholder ID
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
m_strDesc += desc;
|
||||
}
|
||||
}
|
||||
if ((m_iProcType & (int)ProcType.PROC_TRADEABLE) != 0)
|
||||
{
|
||||
m_strDesc += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
// ITEMDESC_NO_PLAYER_TRADE placeholder - adjust this value
|
||||
string desc = pDescTab.GetWideString(2003); // Placeholder ID
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
m_strDesc += desc;
|
||||
}
|
||||
}
|
||||
if ((m_iProcType & (int)ProcType.PROC_DISAPEAR) != 0)
|
||||
{
|
||||
m_strDesc += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
// ITEMDESC_LEAVE_SCENE_DISAPEAR placeholder - adjust this value
|
||||
string desc = pDescTab.GetWideString(2004); // Placeholder ID
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
m_strDesc += desc;
|
||||
}
|
||||
}
|
||||
if ((m_iProcType & (int)ProcType.PROC_USE) != 0)
|
||||
{
|
||||
m_strDesc += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
// ITEMDESC_USE_AFTER_PICK_UP placeholder - adjust this value
|
||||
string desc = pDescTab.GetWideString(2005); // Placeholder ID
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
m_strDesc += desc;
|
||||
}
|
||||
}
|
||||
if ((m_iProcType & (int)ProcType.PROC_DEADDROP) != 0)
|
||||
{
|
||||
m_strDesc += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
// ITEMDESC_DROP_WHEN_DEAD placeholder - adjust this value
|
||||
string desc = pDescTab.GetWideString(2006); // Placeholder ID
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
m_strDesc += desc;
|
||||
}
|
||||
}
|
||||
if ((m_iProcType & (int)ProcType.PROC_OFFLINE) != 0)
|
||||
{
|
||||
m_strDesc += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
// ITEMDESC_DROP_WHEN_OFFLINE placeholder - adjust this value
|
||||
string desc = pDescTab.GetWideString(2007); // Placeholder ID
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
m_strDesc += desc;
|
||||
}
|
||||
}
|
||||
if ((m_iProcType & (int)ProcType.PROC_UNREPAIRABLE) != 0)
|
||||
{
|
||||
m_strDesc += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
// ITEMDESC_UNREPAIRABLE placeholder - adjust this value
|
||||
string desc = pDescTab.GetWideString(2008); // Placeholder ID
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
m_strDesc += desc;
|
||||
}
|
||||
}
|
||||
if ((m_iProcType & (int)ProcType.PROC_NO_USER_TRASH) != 0)
|
||||
{
|
||||
m_strDesc += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
// ITEMDESC_NO_USER_TRASH placeholder - adjust this value
|
||||
string desc = pDescTab.GetWideString(2009); // Placeholder ID
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
m_strDesc += desc;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TrimLastReturn();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TrimLastReturn();
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(szExtDesc))
|
||||
return;
|
||||
|
||||
// Extend description is below special properties / 扩展描述在特殊属性下方
|
||||
if (bAddLine)
|
||||
m_strDesc += "\\r\\r";
|
||||
else
|
||||
m_strDesc += "\\r";
|
||||
m_strDesc += szExtDesc;
|
||||
}
|
||||
|
||||
protected void AddExpireTimeDesc()
|
||||
{
|
||||
// Placeholder: expiry description can be added here when time system is fully wired.
|
||||
}
|
||||
|
||||
protected void AddExpireTimeDesc(int expire_date)
|
||||
{
|
||||
// Overwrite, call standard one; we don't change m_expire_date in this simplified port.
|
||||
AddExpireTimeDesc();
|
||||
}
|
||||
|
||||
protected void AddIDDescText()
|
||||
{
|
||||
// Optional: show internal id for debugging
|
||||
AddDescText(0, true, "ID: {0}", m_tid);
|
||||
}
|
||||
|
||||
protected void AddBindDescText()
|
||||
{
|
||||
// Simple binding flags display based on ProcType
|
||||
if ((m_iProcType & (int)ProcType.PROC_BINDING) != 0)
|
||||
{
|
||||
AddDescText(0, true, "[Bound]");
|
||||
}
|
||||
else if ((m_iProcType & (int)ProcType.PROC_BIND) != 0)
|
||||
{
|
||||
AddDescText(0, true, "[Bind on Use]");
|
||||
}
|
||||
}
|
||||
|
||||
protected void AddActionTypeDescText(int action_type)
|
||||
{
|
||||
// Action/weapon type formatting can be added later as needed.
|
||||
}
|
||||
|
||||
protected void TrimLastReturn()
|
||||
@@ -1247,7 +1466,10 @@ namespace BrewMonster.Scripts.Managers
|
||||
if (string.IsNullOrEmpty(m_strDesc))
|
||||
return;
|
||||
|
||||
if (m_strDesc.EndsWith("\n", StringComparison.Ordinal))
|
||||
// Remove trailing "\r" or "\n" for consistency with C++ style
|
||||
if (m_strDesc.EndsWith("\\r", StringComparison.Ordinal))
|
||||
m_strDesc = m_strDesc.Substring(0, m_strDesc.Length - 2);
|
||||
else if (m_strDesc.EndsWith("\n", StringComparison.Ordinal))
|
||||
m_strDesc = m_strDesc.Substring(0, m_strDesc.Length - 1);
|
||||
}
|
||||
|
||||
@@ -1263,6 +1485,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
|
||||
protected int GetColorStrID(int tid)
|
||||
{
|
||||
// Placeholder: color index lookup; return -1 (white) by default.
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1273,6 +1496,253 @@ namespace BrewMonster.Scripts.Managers
|
||||
return (int)(f * 100.0f + 0.5f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to get the main description line for this item from item_desc.txt via EC_Game.
|
||||
/// This mirrors the behaviour used by Inventory UI and NPC shop, but centralised here.
|
||||
/// </summary>
|
||||
private string TryGetItemMainDesc()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (EC_Game.TryGetItemMsg(m_tid, out int messageId, out int displayMode))
|
||||
{
|
||||
var tab = EC_Game.GetItemDesc();
|
||||
if (tab != null && tab.IsInitialized())
|
||||
{
|
||||
var s = tab.GetWideString(messageId);
|
||||
if (!string.IsNullOrEmpty(s)) return s;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: direct template id lookup
|
||||
{
|
||||
var tab = EC_Game.GetItemDesc();
|
||||
if (tab != null && tab.IsInitialized())
|
||||
{
|
||||
var s = tab.GetWideString(m_tid);
|
||||
if (!string.IsNullOrEmpty(s)) return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"[EC_IvtrItem] Error getting main description for tid={m_tid}: {ex.Message}");
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to get the extended description line from item_ext_desc.txt.
|
||||
/// </summary>
|
||||
private string TryGetItemExtDesc()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (EC_Game.TryGetItemMsg(m_tid, out int messageId, out int displayMode))
|
||||
{
|
||||
var tab = EC_Game.GetItemExtDesc();
|
||||
if (tab != null && tab.IsInitialized())
|
||||
{
|
||||
var s = tab.GetWideString(messageId);
|
||||
if (!string.IsNullOrEmpty(s)) return s;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: direct id
|
||||
{
|
||||
var tab = EC_Game.GetItemExtDesc();
|
||||
if (tab != null && tab.IsInitialized())
|
||||
{
|
||||
var s = tab.GetWideString(m_tid);
|
||||
if (!string.IsNullOrEmpty(s)) return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"[EC_IvtrItem] Error getting extended description for tid={m_tid}: {ex.Message}");
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get extended description text for UI display.
|
||||
/// This method mirrors AddExtDescText() logic but returns a string instead of modifying m_strDesc.
|
||||
/// 此方法镜像AddExtDescText()逻辑,但返回字符串而不是修改m_strDesc
|
||||
/// </summary>
|
||||
public string GetExtendedDescText()
|
||||
{
|
||||
string result = string.Empty;
|
||||
|
||||
// Get extended description from item_ext_desc.txt using tid / 使用tid从item_ext_desc.txt获取扩展描述
|
||||
string szExtDesc = TryGetItemExtDesc();
|
||||
// Note: Original C++ had early return commented out / 注意:原始C++代码的早期返回被注释掉了
|
||||
// if (!szExtDesc || !szExtDesc[0])
|
||||
// return;
|
||||
|
||||
result += "\\r";
|
||||
|
||||
bool bAddLine = true;
|
||||
// Add special properties description / 添加特殊属性描述
|
||||
var pDescTab = EC_Game.GetItemDesc();
|
||||
// Note: ITEMDESC_COL2_BRIGHTBLUE constant - adjust based on actual string table / 注意:ITEMDESC_COL2_BRIGHTBLUE常量 - 根据实际字符串表调整
|
||||
int green = 1000; // ITEMDESC_COL2_BRIGHTBLUE placeholder - adjust this value
|
||||
|
||||
if (m_iCID != (int)InventoryClassId.ICID_GOBLIN) // goblin does not need to display these special properties / 地精不需要显示这些特殊属性
|
||||
{
|
||||
// Exact C++ logic: (PROC_NO_USER_TRASH) || (!PROC_BINDING && (PROC_DROPWHENDIE || ...))
|
||||
// 精确的C++逻辑:(PROC_NO_USER_TRASH) || (!PROC_BINDING && (PROC_DROPWHENDIE || ...))
|
||||
if ((m_iProcType & (int)ProcType.PROC_NO_USER_TRASH) != 0
|
||||
|| (!((m_iProcType & (int)ProcType.PROC_BINDING) != 0)
|
||||
&& ((m_iProcType & (int)ProcType.PROC_DROPWHENDIE) != 0
|
||||
|| (m_iProcType & (int)ProcType.PROC_DROPPABLE) != 0
|
||||
|| (m_iProcType & (int)ProcType.PROC_SELLABLE) != 0
|
||||
|| (m_iProcType & (int)ProcType.PROC_TRADEABLE) != 0
|
||||
|| (m_iProcType & (int)ProcType.PROC_DISAPEAR) != 0
|
||||
|| (m_iProcType & (int)ProcType.PROC_USE) != 0
|
||||
|| (m_iProcType & (int)ProcType.PROC_DEADDROP) != 0
|
||||
|| (m_iProcType & (int)ProcType.PROC_OFFLINE) != 0
|
||||
|| (m_iProcType & (int)ProcType.PROC_UNREPAIRABLE) != 0)))
|
||||
{
|
||||
bAddLine = false;
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
string szCol = pDescTab.GetWideString(green);
|
||||
if (!string.IsNullOrEmpty(szCol))
|
||||
{
|
||||
result += szCol;
|
||||
}
|
||||
}
|
||||
|
||||
// Note: These message IDs are placeholders - adjust based on actual string table / 注意:这些消息ID是占位符 - 根据实际字符串表调整
|
||||
if ((m_iProcType & (int)ProcType.PROC_DROPWHENDIE) != 0)
|
||||
{
|
||||
result += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
// ITEMDESC_DEAD_PROTECT placeholder - adjust this value
|
||||
string desc = pDescTab.GetWideString(2000); // Placeholder ID
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
result += desc;
|
||||
}
|
||||
}
|
||||
if ((m_iProcType & (int)ProcType.PROC_DROPPABLE) != 0)
|
||||
{
|
||||
result += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
// ITEMDESC_NO_DROP placeholder - adjust this value
|
||||
string desc = pDescTab.GetWideString(2001); // Placeholder ID
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
result += desc;
|
||||
}
|
||||
}
|
||||
if ((m_iProcType & (int)ProcType.PROC_SELLABLE) != 0)
|
||||
{
|
||||
result += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
// ITEMDESC_NO_TRADE placeholder - adjust this value
|
||||
string desc = pDescTab.GetWideString(2002); // Placeholder ID
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
result += desc;
|
||||
}
|
||||
}
|
||||
if ((m_iProcType & (int)ProcType.PROC_TRADEABLE) != 0)
|
||||
{
|
||||
result += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
// ITEMDESC_NO_PLAYER_TRADE placeholder - adjust this value
|
||||
string desc = pDescTab.GetWideString(2003); // Placeholder ID
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
result += desc;
|
||||
}
|
||||
}
|
||||
if ((m_iProcType & (int)ProcType.PROC_DISAPEAR) != 0)
|
||||
{
|
||||
result += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
// ITEMDESC_LEAVE_SCENE_DISAPEAR placeholder - adjust this value
|
||||
string desc = pDescTab.GetWideString(2004); // Placeholder ID
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
result += desc;
|
||||
}
|
||||
}
|
||||
if ((m_iProcType & (int)ProcType.PROC_USE) != 0)
|
||||
{
|
||||
result += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
// ITEMDESC_USE_AFTER_PICK_UP placeholder - adjust this value
|
||||
string desc = pDescTab.GetWideString(2005); // Placeholder ID
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
result += desc;
|
||||
}
|
||||
}
|
||||
if ((m_iProcType & (int)ProcType.PROC_DEADDROP) != 0)
|
||||
{
|
||||
result += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
// ITEMDESC_DROP_WHEN_DEAD placeholder - adjust this value
|
||||
string desc = pDescTab.GetWideString(2006); // Placeholder ID
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
result += desc;
|
||||
}
|
||||
}
|
||||
if ((m_iProcType & (int)ProcType.PROC_OFFLINE) != 0)
|
||||
{
|
||||
result += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
// ITEMDESC_DROP_WHEN_OFFLINE placeholder - adjust this value
|
||||
string desc = pDescTab.GetWideString(2007); // Placeholder ID
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
result += desc;
|
||||
}
|
||||
}
|
||||
if ((m_iProcType & (int)ProcType.PROC_UNREPAIRABLE) != 0)
|
||||
{
|
||||
result += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
// ITEMDESC_UNREPAIRABLE placeholder - adjust this value
|
||||
string desc = pDescTab.GetWideString(2008); // Placeholder ID
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
result += desc;
|
||||
}
|
||||
}
|
||||
if ((m_iProcType & (int)ProcType.PROC_NO_USER_TRASH) != 0)
|
||||
{
|
||||
result += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
// ITEMDESC_NO_USER_TRASH placeholder - adjust this value
|
||||
string desc = pDescTab.GetWideString(2009); // Placeholder ID
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
result += desc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(szExtDesc))
|
||||
return result;
|
||||
|
||||
// Extend description is below special properties / 扩展描述在特殊属性下方
|
||||
if (bAddLine)
|
||||
result += "\\r\\r";
|
||||
else
|
||||
result += "\\r";
|
||||
result += szExtDesc;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
@@ -704,6 +704,41 @@ namespace CSNetwork.C2SCommand
|
||||
{
|
||||
public bool agree;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct npc_trade_item
|
||||
{
|
||||
public int tid;
|
||||
public uint index;
|
||||
public uint count;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct npc_sell_item
|
||||
{
|
||||
public int tid;
|
||||
public uint index;
|
||||
public uint count;
|
||||
public int price;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct NPCSevBuyCONTENT
|
||||
{
|
||||
public uint money; // Not use now
|
||||
public int consume_contrib;
|
||||
public int cumulate_contrib;
|
||||
public int force_id;
|
||||
public int force_repu;
|
||||
public int force_contrib;
|
||||
public uint item_count;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct NPCSevSellCONTENT
|
||||
{
|
||||
public uint item_count;
|
||||
}
|
||||
}
|
||||
|
||||
namespace CSNetwork.S2CCommand
|
||||
|
||||
@@ -616,6 +616,76 @@ namespace CSNetwork.C2SCommand
|
||||
};
|
||||
return SerializeCommand(CommandID.SEVNPC_SERVE, cmd, content);
|
||||
}
|
||||
|
||||
public static Octets CreateNPCSevBuyCmd(int itemNum, CSNetwork.C2SCommand.npc_trade_item[] items)
|
||||
{
|
||||
if (itemNum <= 0 || items == null || items.Length < itemNum)
|
||||
throw new ArgumentException("Invalid itemNum or items array");
|
||||
|
||||
uint contentSize = (uint)Marshal.SizeOf<NPCSevBuyCONTENT>();
|
||||
uint itemSize = (uint)Marshal.SizeOf<CSNetwork.C2SCommand.npc_trade_item>();
|
||||
uint totalLen = contentSize + (uint)itemNum * itemSize;
|
||||
|
||||
var cmd = new cmd_sevnpc_serve
|
||||
{
|
||||
service_type = NPC_service_type.GP_NPCSEV_SELL, // NPC sells to player = player buys
|
||||
len = totalLen
|
||||
};
|
||||
|
||||
NPCSevBuyCONTENT content = new NPCSevBuyCONTENT()
|
||||
{
|
||||
money = 0, // Not use now
|
||||
consume_contrib = 0,
|
||||
cumulate_contrib = 0,
|
||||
force_id = 0,
|
||||
force_repu = 0,
|
||||
force_contrib = 0,
|
||||
item_count = (uint)itemNum
|
||||
};
|
||||
|
||||
// Serialize command + content
|
||||
var octets = SerializeCommand(CommandID.SEVNPC_SERVE, cmd, content);
|
||||
|
||||
// Append items array
|
||||
for (int i = 0; i < itemNum; i++)
|
||||
{
|
||||
WriteStruct(octets, items[i]);
|
||||
}
|
||||
|
||||
return octets;
|
||||
}
|
||||
|
||||
public static Octets CreateNPCSevSellCmd(int itemNum, npc_sell_item[] items)
|
||||
{
|
||||
if (itemNum <= 0 || items == null || items.Length < itemNum)
|
||||
throw new ArgumentException("Invalid itemNum or items array");
|
||||
|
||||
uint contentSize = (uint)Marshal.SizeOf<NPCSevSellCONTENT>();
|
||||
uint itemSize = (uint)Marshal.SizeOf<npc_sell_item>();
|
||||
uint totalLen = contentSize + (uint)itemNum * itemSize;
|
||||
|
||||
var cmd = new cmd_sevnpc_serve
|
||||
{
|
||||
service_type = NPC_service_type.GP_NPCSEV_BUY, // NPC buys from player = player sells
|
||||
len = totalLen
|
||||
};
|
||||
|
||||
NPCSevSellCONTENT content = new NPCSevSellCONTENT()
|
||||
{
|
||||
item_count = (uint)itemNum
|
||||
};
|
||||
|
||||
// Serialize command + content
|
||||
var octets = SerializeCommand(CommandID.SEVNPC_SERVE, cmd, content);
|
||||
|
||||
// Append items array
|
||||
for (int i = 0; i < itemNum; i++)
|
||||
{
|
||||
WriteStruct(octets, items[i]);
|
||||
}
|
||||
|
||||
return octets;
|
||||
}
|
||||
|
||||
// TODO: Check orginal C++ implementation
|
||||
public static Octets CreateTaskNotifyCmd(byte[] pData, uint dwDataSize)
|
||||
|
||||
@@ -5,7 +5,7 @@ using System.Text;
|
||||
|
||||
namespace CSNetwork.Common
|
||||
{
|
||||
internal class ExpTypes
|
||||
public class ExpTypes
|
||||
{
|
||||
// ts
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)]
|
||||
@@ -110,73 +110,74 @@ namespace CSNetwork.Common
|
||||
public int life;
|
||||
}
|
||||
}
|
||||
|
||||
public enum SERVICE_TYPE : int
|
||||
{
|
||||
// ½»Ì¸·þÎñ
|
||||
NPC_TALK = int.MinValue,
|
||||
// ³öÊÛÉÌÆ·µÄ·þÎñ
|
||||
NPC_SELL,
|
||||
// ÊÕ¹ºÉÌÆ·µÄ·þÎñ
|
||||
NPC_BUY,
|
||||
// ÐÞÀíÉÌÆ·µÄ·þÎñ
|
||||
NPC_REPAIR,
|
||||
// ÏâǶ·þÎñ
|
||||
NPC_INSTALL,
|
||||
// ²ð³ý·þÎñ
|
||||
NPC_UNINSTALL,
|
||||
// ÈÎÎñÏà¹Ø·þÎñ,·Ö·¢ÈÎÎñºÍÍê³ÉÈÎÎñÒÔ¼°·¢·ÅÈÎÎñÎïÆ·
|
||||
NPC_GIVE_TASK,
|
||||
NPC_COMPLETE_TASK,
|
||||
NPC_GIVE_TASK_MATTER,
|
||||
// ½ÌÊÚÏà¹Ø·þÎñ
|
||||
NPC_SKILL,
|
||||
// ÖÎÁÆ·þÎñ
|
||||
NPC_HEAL,
|
||||
// ´«ËÍ·þÎñ
|
||||
NPC_TRANSMIT,
|
||||
// ÔËÊä·þÎñ
|
||||
NPC_TRANSPORT,
|
||||
// ´úÊÛ·þÎñ
|
||||
NPC_PROXY,
|
||||
// ´æ´¢ÎïÆ·¡¢½ðÇ®
|
||||
NPC_STORAGE,
|
||||
// Éú²ú·þÎñ
|
||||
NPC_MAKE,
|
||||
// ·Ö½â·þÎñ
|
||||
NPC_DECOMPOSE,
|
||||
// TALK·µ»Ø
|
||||
TALK_RETURN,
|
||||
// ½áÊø¶Ô»°
|
||||
TALK_EXIT,
|
||||
// ²Ö¿âÃÜÂë
|
||||
NPC_STORAGE_PASSWORD,
|
||||
// ¼ø¶¨·þÎñ
|
||||
NPC_IDENTIFY,
|
||||
// ·ÅÆúÈÎÎñ
|
||||
TALK_GIVEUP_TASK,
|
||||
// ³ÇÕ½ÅÚËþ½¨Ôì·þÎñ
|
||||
NPC_WAR_TOWERBUILD,
|
||||
// Ï´µã·þÎñ
|
||||
NPC_RESETPROP,
|
||||
// ³èÎï¸ÄÃû·þÎñ
|
||||
NPC_PETNAME,
|
||||
// ³èÎïѧϰ¼¼ÄÜ·þÎñ
|
||||
NPC_PETLEARNSKILL,
|
||||
// ³èÎïÒÅÍü¼¼ÄÜ·þÎñ
|
||||
NPC_PETFORGETSKILL,
|
||||
// ×°±¸°ó¶¨·þÎñ
|
||||
NPC_EQUIPBIND,
|
||||
// ×°±¸Ïú»Ù·þÎñ
|
||||
NPC_EQUIPDESTROY,
|
||||
// ×°±¸½â³ýÏú»Ù·þÎñ
|
||||
NPC_EQUIPUNDESTROY,
|
||||
// ÕʺŲֿâ
|
||||
NPC_ACCOUNT_STORAGE,
|
||||
// ïÔ¿Ì·þÎñ
|
||||
NPC_ENGRAVE,
|
||||
// ×°±¸ÖØÖý£¨Ëæ»úÊôÐÔ£©
|
||||
NPC_RANDPROP,
|
||||
};
|
||||
}
|
||||
|
||||
public enum SERVICE_TYPE : int
|
||||
{
|
||||
// ½»Ì¸·þÎñ
|
||||
NPC_TALK = int.MinValue,
|
||||
// ³öÊÛÉÌÆ·µÄ·þÎñ
|
||||
NPC_SELL,
|
||||
// ÊÕ¹ºÉÌÆ·µÄ·þÎñ
|
||||
NPC_BUY,
|
||||
// ÐÞÀíÉÌÆ·µÄ·þÎñ
|
||||
NPC_REPAIR,
|
||||
// ÏâǶ·þÎñ
|
||||
NPC_INSTALL,
|
||||
// ²ð³ý·þÎñ
|
||||
NPC_UNINSTALL,
|
||||
// ÈÎÎñÏà¹Ø·þÎñ,·Ö·¢ÈÎÎñºÍÍê³ÉÈÎÎñÒÔ¼°·¢·ÅÈÎÎñÎïÆ·
|
||||
NPC_GIVE_TASK,
|
||||
NPC_COMPLETE_TASK,
|
||||
NPC_GIVE_TASK_MATTER,
|
||||
// ½ÌÊÚÏà¹Ø·þÎñ
|
||||
NPC_SKILL,
|
||||
// ÖÎÁÆ·þÎñ
|
||||
NPC_HEAL,
|
||||
// ´«ËÍ·þÎñ
|
||||
NPC_TRANSMIT,
|
||||
// ÔËÊä·þÎñ
|
||||
NPC_TRANSPORT,
|
||||
// ´úÊÛ·þÎñ
|
||||
NPC_PROXY,
|
||||
// ´æ´¢ÎïÆ·¡¢½ðÇ®
|
||||
NPC_STORAGE,
|
||||
// Éú²ú·þÎñ
|
||||
NPC_MAKE,
|
||||
// ·Ö½â·þÎñ
|
||||
NPC_DECOMPOSE,
|
||||
// TALK·µ»Ø
|
||||
TALK_RETURN,
|
||||
// ½áÊø¶Ô»°
|
||||
TALK_EXIT,
|
||||
// ²Ö¿âÃÜÂë
|
||||
NPC_STORAGE_PASSWORD,
|
||||
// ¼ø¶¨·þÎñ
|
||||
NPC_IDENTIFY,
|
||||
// ·ÅÆúÈÎÎñ
|
||||
TALK_GIVEUP_TASK,
|
||||
// ³ÇÕ½ÅÚËþ½¨Ôì·þÎñ
|
||||
NPC_WAR_TOWERBUILD,
|
||||
// Ï´µã·þÎñ
|
||||
NPC_RESETPROP,
|
||||
// ³èÎï¸ÄÃû·þÎñ
|
||||
NPC_PETNAME,
|
||||
// ³èÎïѧϰ¼¼ÄÜ·þÎñ
|
||||
NPC_PETLEARNSKILL,
|
||||
// ³èÎïÒÅÍü¼¼ÄÜ·þÎñ
|
||||
NPC_PETFORGETSKILL,
|
||||
// ×°±¸°ó¶¨·þÎñ
|
||||
NPC_EQUIPBIND,
|
||||
// ×°±¸Ïú»Ù·þÎñ
|
||||
NPC_EQUIPDESTROY,
|
||||
// ×°±¸½â³ýÏú»Ù·þÎñ
|
||||
NPC_EQUIPUNDESTROY,
|
||||
// ÕʺŲֿâ
|
||||
NPC_ACCOUNT_STORAGE,
|
||||
// ïÔ¿Ì·þÎñ
|
||||
NPC_ENGRAVE,
|
||||
// ×°±¸ÖØÖý£¨Ëæ»úÊôÐÔ£©
|
||||
NPC_RANDPROP,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1266,6 +1266,26 @@ namespace CSNetwork
|
||||
SendProtocol(gamedatasend);
|
||||
}
|
||||
|
||||
public void c2s_SendCmdNPCSevBuy(int itemNum, C2SCommand.npc_trade_item[] items)
|
||||
{
|
||||
if (itemNum <= 0 || items == null || items.Length < itemNum)
|
||||
return;
|
||||
|
||||
gamedatasend gamedatasend = new gamedatasend();
|
||||
gamedatasend.Data = C2SCommandFactory.CreateNPCSevBuyCmd(itemNum, items);
|
||||
SendProtocol(gamedatasend);
|
||||
}
|
||||
|
||||
public void c2s_SendCmdNPCSevSell(int itemNum, C2SCommand.npc_sell_item[] items)
|
||||
{
|
||||
if (itemNum <= 0 || items == null || items.Length < itemNum)
|
||||
return;
|
||||
|
||||
gamedatasend gamedatasend = new gamedatasend();
|
||||
gamedatasend.Data = C2SCommandFactory.CreateNPCSevSellCmd(itemNum, items);
|
||||
SendProtocol(gamedatasend);
|
||||
}
|
||||
|
||||
public void GetRoleCustomizeData(int iNumRole, List<int> aRoleIDs)
|
||||
{
|
||||
if (iNumRole <= 0 || aRoleIDs == null || aRoleIDs.Count == 0) return;
|
||||
|
||||
@@ -46,7 +46,6 @@ namespace BrewMonster.Managers
|
||||
private void OnDestroy()
|
||||
{
|
||||
EC_ManMessage.Dispose();
|
||||
CECGameRun.Dispose();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using BrewMonster;
|
||||
using BrewMonster;
|
||||
using BrewMonster.Common;
|
||||
using CSNetwork;
|
||||
using CSNetwork.C2SCommand;
|
||||
@@ -319,6 +319,19 @@ namespace BrewMonster.Network
|
||||
Instance._gameSession.c2s_SendCmdNPCSevTaskMatter(idTask);
|
||||
}
|
||||
|
||||
public static void c2s_CmdNPCSevBuy(int itemNum, npc_trade_item[] items)
|
||||
{
|
||||
if (items == null || itemNum <= 0)
|
||||
return;
|
||||
Instance._gameSession.c2s_SendCmdNPCSevBuy(itemNum, items);
|
||||
}
|
||||
|
||||
public static void c2s_CmdNPCSevSell(int itemNum, npc_sell_item[] items)
|
||||
{
|
||||
if (items == null || itemNum <= 0)
|
||||
return;
|
||||
Instance._gameSession.c2s_SendCmdNPCSevSell(itemNum, items);
|
||||
}
|
||||
public static void c2s_CmdStandUp()
|
||||
{
|
||||
Instance._gameSession.c2s_SendCmdStandUp();
|
||||
|
||||
@@ -1,18 +1,149 @@
|
||||
// File : CECObserver.cs
|
||||
// Creator : Xu Wenbin
|
||||
// Date : 2014/4/10
|
||||
// Converted to C#: 2024
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BrewMonster
|
||||
{
|
||||
public class CECObservableChange : IDisposable
|
||||
// class CECObservableChange
|
||||
// 封装 CECObservable 的变化通知数据 / Encapsulates change notification data for CECObservable
|
||||
public class CECObservableChange : IDisposable
|
||||
{
|
||||
public virtual void Dispose()
|
||||
{
|
||||
// Cleanup logic here
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public class CECObserver
|
||||
// class CECObserver
|
||||
// 观察者基类:观察的 Model / Observer base class: observes Model
|
||||
public class CECObserver<Model>
|
||||
{
|
||||
|
||||
public virtual void OnRegistered(Model model) { }
|
||||
public virtual void OnModelChange(Model model, CECObservableChange change) { }
|
||||
public virtual void OnUnregister(Model model) { }
|
||||
}
|
||||
|
||||
// class CECObservable
|
||||
// 可观察者基类:被观察的 Model / Observable base class: the Model being observed
|
||||
// 提供观察者注册及通知功能 / Provides observer registration and notification functionality
|
||||
public class CECObservable<Model> where Model : class
|
||||
{
|
||||
private struct ObserverImpl
|
||||
{
|
||||
public CECObserver<Model> observer; // 观察者指针 / Observer pointer
|
||||
public int observeTimes; // 观察次数:为正数时,收到指定次数的 OnModelChange 消息后自动取消注册,为负数时为永久,为0值时无效 / Observe times: when positive, auto-unregister after receiving specified OnModelChange messages; when negative, permanent; when 0, invalid
|
||||
public bool autoDeleteOnUnregister; // 取消注册时是否自动 delete / Whether to auto-delete on unregister
|
||||
|
||||
public ObserverImpl(CECObserver<Model> observer, int observeTimes, bool autoDeleteOnUnregister)
|
||||
{
|
||||
this.observer = observer;
|
||||
this.observeTimes = observeTimes;
|
||||
this.autoDeleteOnUnregister = autoDeleteOnUnregister;
|
||||
|
||||
if (this.observeTimes == 0)
|
||||
{
|
||||
Debug.Assert(false, "Observer times cannot be 0");
|
||||
this.observeTimes = 1; // 默认观察次数为1 / Default observe times to 1
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(ObserverImpl rhs)
|
||||
{
|
||||
return this.observer == rhs.observer;
|
||||
}
|
||||
|
||||
public bool Equals(CECObserver<Model> pObserver)
|
||||
{
|
||||
return this.observer == pObserver;
|
||||
}
|
||||
|
||||
public bool WillNotObserve()
|
||||
{
|
||||
// 是否不再观察目标 / Whether no longer observing target
|
||||
return observeTimes == 0;
|
||||
}
|
||||
|
||||
public bool ObserveOnce()
|
||||
{
|
||||
// 收到一次观察消息时调用,返回是否不再观察 / Called when receiving one observe message, returns whether no longer observing
|
||||
if (observeTimes > 0)
|
||||
{
|
||||
--observeTimes;
|
||||
}
|
||||
return WillNotObserve();
|
||||
}
|
||||
}
|
||||
|
||||
private List<ObserverImpl> m_observers = new List<ObserverImpl>();
|
||||
|
||||
protected Model AsModel()
|
||||
{
|
||||
return this as Model;
|
||||
}
|
||||
|
||||
public bool IsObserverRegistered(CECObserver<Model> pObserver)
|
||||
{
|
||||
return m_observers.Any(impl => impl.Equals(pObserver));
|
||||
}
|
||||
|
||||
public bool RegisterObserver(CECObserver<Model> pObserver, int observeTimes = -1, bool autoDeleteOnUnregister = false)
|
||||
{
|
||||
bool bRegistered = false;
|
||||
if (!IsObserverRegistered(pObserver))
|
||||
{
|
||||
m_observers.Add(new ObserverImpl(pObserver, observeTimes, autoDeleteOnUnregister));
|
||||
pObserver.OnRegistered(AsModel());
|
||||
bRegistered = true;
|
||||
}
|
||||
return bRegistered;
|
||||
}
|
||||
|
||||
public bool UnregisterObserver(CECObserver<Model> pObserver)
|
||||
{
|
||||
int index = m_observers.FindIndex(impl => impl.Equals(pObserver));
|
||||
if (index >= 0)
|
||||
{
|
||||
return UnregisterObserverImpl(index);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void NotifyObservers(CECObservableChange pChange)
|
||||
{
|
||||
Model pModel = AsModel();
|
||||
for (int i = m_observers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
ObserverImpl observerImpl = m_observers[i];
|
||||
observerImpl.observer.OnModelChange(pModel, pChange);
|
||||
if (observerImpl.ObserveOnce())
|
||||
{
|
||||
UnregisterObserverImpl(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool UnregisterObserverImpl(int index)
|
||||
{
|
||||
bool bUnRegistered = false;
|
||||
if (index >= 0 && index < m_observers.Count)
|
||||
{
|
||||
ObserverImpl observerImpl = m_observers[index];
|
||||
observerImpl.observer.OnUnregister(AsModel());
|
||||
if (observerImpl.autoDeleteOnUnregister)
|
||||
{
|
||||
// In C#, we don't manually delete objects, but we can set to null
|
||||
// The GC will handle cleanup
|
||||
}
|
||||
m_observers.RemoveAt(index);
|
||||
bUnRegistered = true;
|
||||
}
|
||||
return bUnRegistered;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1be878d234498514c8356a798b2e4515
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,230 @@
|
||||
// Filename : CECBackShop.cs
|
||||
// Creator : Xu Wenbin
|
||||
// Date : 2013/12/11
|
||||
// Converted to C#: 2024
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using BrewMonster.Network;
|
||||
|
||||
namespace PerfectWorld.Scripts.Shop
|
||||
{
|
||||
// CECBackShop
|
||||
public class CECBackShop : CECShopArrayItems
|
||||
{
|
||||
private CECShopItemCategory m_fashionShopCategory = new CECShopItemCategory();
|
||||
private static CECBackShop s_instance;
|
||||
private GShopLoader m_shopLoader;
|
||||
|
||||
private CECBackShop() : base(null)
|
||||
{
|
||||
// Initialize with empty list first
|
||||
m_pItems = new List<GShopItem>();
|
||||
|
||||
// Try to get shop loader data (may not be available immediately)
|
||||
RefreshShopData();
|
||||
|
||||
if (IsFashionShopEnabled())
|
||||
{
|
||||
m_fashionShopCategory.InitFromQShopConfig(CECQShopConfig.CID_BACKSHOP_FASHION);
|
||||
}
|
||||
}
|
||||
|
||||
public void RefreshShopData()
|
||||
{
|
||||
// Initialize with shop loader data
|
||||
GameObject shopLoaderObj = GameObject.FindFirstObjectByType<GShopLoader>()?.gameObject;
|
||||
if (shopLoaderObj != null)
|
||||
{
|
||||
m_shopLoader = shopLoaderObj.GetComponent<GShopLoader>();
|
||||
if (m_shopLoader != null && m_shopLoader.secondaryShop != null && m_shopLoader.secondaryShop.items != null)
|
||||
{
|
||||
m_pItems = m_shopLoader.secondaryShop.items;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static CECBackShop Instance()
|
||||
{
|
||||
if (s_instance == null)
|
||||
{
|
||||
s_instance = new CECBackShop();
|
||||
}
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
public override bool GetFromServer(int beginIndex, int endIndex)
|
||||
{
|
||||
bool result = false;
|
||||
// TODO: Implement cooldown check (GP_CT_GET_DIVIDEND_MALL_PRICE)
|
||||
// For now, always allow if indices are provided
|
||||
if (beginIndex == 0 && endIndex == 0)
|
||||
{
|
||||
// Request all prices
|
||||
// TODO: Implement UnityGameSession.RequestGetDividendMallItemPrice(0, 0)
|
||||
result = true;
|
||||
}
|
||||
else if (beginIndex != 0 || endIndex != 0)
|
||||
{
|
||||
// Request specific range
|
||||
// TODO: Implement UnityGameSession.RequestGetDividendMallItemPrice(beginIndex, endIndex)
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public override uint GetLocalTimeStamp()
|
||||
{
|
||||
if (m_shopLoader != null && m_shopLoader.secondaryShop != null)
|
||||
{
|
||||
return m_shopLoader.secondaryShop.timestamp;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override uint GetServerTimeStamp()
|
||||
{
|
||||
// TODO: Get from game run state
|
||||
// return g_pGame->GetGameRun()->GetGShopTimeStamp2();
|
||||
return GetLocalTimeStamp(); // Stub for now
|
||||
}
|
||||
|
||||
public bool UpdateFromServer() // TODO: Add S2C::cmd_dividend_mall_item_price parameter when available
|
||||
{
|
||||
// TODO: Implement server update logic when S2C command structure is available
|
||||
// For now, this is a stub
|
||||
/*
|
||||
List<GShopItem> items = new List<GShopItem>(m_pItems);
|
||||
if (!ApplyChangesFromServer(items, pCmd))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (IsSame(items, m_pItems))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
m_pItems = items;
|
||||
OnItemChange();
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ApplyChangesFromServer(List<GShopItem> pItems) // TODO: Add S2C::cmd_dividend_mall_item_price parameter when available
|
||||
{
|
||||
// TODO: Implement when S2C command structure is available
|
||||
/*
|
||||
int i = 0;
|
||||
for (i = pCmd->start_index; i < pCmd->end_index; i++)
|
||||
{
|
||||
if (i < pItems.Count)
|
||||
{
|
||||
GShopItem data = pItems[i];
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
if (data.buy != null && j < data.buy.Length && data.buy[j].type != -1)
|
||||
{
|
||||
GShopBuyOption buyOption = data.buy[j];
|
||||
buyOption.type = -1;
|
||||
buyOption.price = 0;
|
||||
data.buy[j] = buyOption;
|
||||
}
|
||||
}
|
||||
pItems[i] = data;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < pCmd->count; i++)
|
||||
{
|
||||
const S2C::cmd_dividend_mall_item_price::good_info& tempList = pCmd->list[i];
|
||||
if (tempList.good_index < pItems.Count)
|
||||
{
|
||||
GShopItem data = pItems[tempList.good_index];
|
||||
if (data.id == (uint)tempList.good_id)
|
||||
{
|
||||
if (data.buy != null && tempList.good_slot < data.buy.Length)
|
||||
{
|
||||
GShopBuyOption buyOption = data.buy[tempList.good_slot];
|
||||
buyOption.price = tempList.good_price;
|
||||
buyOption.status = tempList.good_status;
|
||||
buyOption.time = tempList.expire_time;
|
||||
buyOption.type = 3;
|
||||
data.buy[tempList.good_slot] = buyOption;
|
||||
}
|
||||
pItems[tempList.good_index] = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(data.id == (uint)tempList.good_id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
public override int GetCash()
|
||||
{
|
||||
// TODO: Integrate with CECHostPlayer.GetDividend()
|
||||
// For now, return 0 as stub
|
||||
// return CECHostPlayer.Instance().GetDividend();
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override bool Buy(int itemIndex, int buyIndex)
|
||||
{
|
||||
bool bOK = false;
|
||||
|
||||
if (ReadyToBuy(itemIndex, buyIndex))
|
||||
{
|
||||
GShopItem? pItem = GetItem(itemIndex);
|
||||
if (pItem.HasValue)
|
||||
{
|
||||
GShopItem item = pItem.Value;
|
||||
if (!CECShopItemOwnerNPC.HasOwnerNPC(item))
|
||||
{
|
||||
// Regular dividend mall shopping
|
||||
// TODO: Implement UnityGameSession.RequestDividendMallShopping(1, itemIndex, item.id, buyIndex)
|
||||
// For now, use regular mall shopping as fallback
|
||||
UnityGameSession.Instance.RequestMallShopping(1, (int)item.id, itemIndex, buyIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
// NPC server dividend mall shopping
|
||||
// TODO: Implement UnityGameSession.RequestNPCSevDividendMallShopping(1, itemIndex, item.id, buyIndex)
|
||||
UnityGameSession.Instance.RequestMallShopping(1, (int)item.id, itemIndex, buyIndex);
|
||||
}
|
||||
CECQShopConfig.Instance().OnItemBuyed(item.id);
|
||||
// TODO: CECShoppingItemsMover::Instance().OnItemBuyed(this, itemIndex, buyIndex);
|
||||
bOK = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(false, "Item not ready to buy");
|
||||
}
|
||||
return bOK;
|
||||
}
|
||||
|
||||
public override bool IsFashionShopEnabled()
|
||||
{
|
||||
return CECUIConfig.Instance().GetGameUI().bEnableBackShopFashionShop;
|
||||
}
|
||||
|
||||
public override bool IsFashionShopFlashSaleEnabled()
|
||||
{
|
||||
return CECUIConfig.Instance().GetGameUI().bEnableBackShopFashionShopFlashSale;
|
||||
}
|
||||
|
||||
public override string GetFashionShopFlashSaleTitle()
|
||||
{
|
||||
return CECUIConfig.Instance().GetGameUI().strBackShopFashionShopFlashSaleTitle;
|
||||
}
|
||||
|
||||
public override CECShopItemCategory GetFashionShopCategory()
|
||||
{
|
||||
return m_fashionShopCategory;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 01372a5632621c3438a20d995d97bdee
|
||||
@@ -0,0 +1,228 @@
|
||||
// Filename : CECQShop.cs
|
||||
// Creator : Xu Wenbin
|
||||
// Date : 2013/12/11
|
||||
// Converted to C#: 2024
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using BrewMonster.Network;
|
||||
|
||||
namespace PerfectWorld.Scripts.Shop
|
||||
{
|
||||
// CECQShop
|
||||
public class CECQShop : CECShopArrayItems
|
||||
{
|
||||
private CECShopItemCategory m_fashionShopCategory = new CECShopItemCategory();
|
||||
private static CECQShop s_instance;
|
||||
private GShopLoader m_shopLoader;
|
||||
|
||||
private CECQShop() : base(null)
|
||||
{
|
||||
// Initialize with empty list first
|
||||
m_pItems = new List<GShopItem>();
|
||||
|
||||
// Try to get shop loader data (may not be available immediately)
|
||||
RefreshShopData();
|
||||
|
||||
if (IsFashionShopEnabled())
|
||||
{
|
||||
m_fashionShopCategory.InitFromQShopConfig(CECQShopConfig.CID_QSHOP_FASHION);
|
||||
}
|
||||
}
|
||||
|
||||
public void RefreshShopData()
|
||||
{
|
||||
// Initialize with shop loader data
|
||||
GameObject shopLoaderObj = GameObject.FindFirstObjectByType<GShopLoader>()?.gameObject;
|
||||
if (shopLoaderObj != null)
|
||||
{
|
||||
m_shopLoader = shopLoaderObj.GetComponent<GShopLoader>();
|
||||
if (m_shopLoader != null && m_shopLoader.primaryShop != null && m_shopLoader.primaryShop.items != null)
|
||||
{
|
||||
m_pItems = m_shopLoader.primaryShop.items;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static CECQShop Instance()
|
||||
{
|
||||
if (s_instance == null)
|
||||
{
|
||||
s_instance = new CECQShop();
|
||||
}
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
public override bool GetFromServer(int beginIndex, int endIndex)
|
||||
{
|
||||
bool result = false;
|
||||
// TODO: Implement cooldown check (GP_CT_GET_MALL_PRICE)
|
||||
// For now, always allow if indices are provided
|
||||
if (beginIndex == 0 && endIndex == 0)
|
||||
{
|
||||
// Request all prices
|
||||
// TODO: Implement UnityGameSession.RequestGetMallItemPrice(0, 0)
|
||||
result = true;
|
||||
}
|
||||
else if (beginIndex != 0 || endIndex != 0)
|
||||
{
|
||||
// Request specific range
|
||||
// TODO: Implement UnityGameSession.RequestGetMallItemPrice(beginIndex, endIndex)
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public override uint GetLocalTimeStamp()
|
||||
{
|
||||
if (m_shopLoader != null && m_shopLoader.primaryShop != null)
|
||||
{
|
||||
return m_shopLoader.primaryShop.timestamp;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override uint GetServerTimeStamp()
|
||||
{
|
||||
// TODO: Get from game run state
|
||||
// return g_pGame->GetGameRun()->GetGShopTimeStamp();
|
||||
return GetLocalTimeStamp(); // Stub for now
|
||||
}
|
||||
|
||||
public bool UpdateFromServer() // TODO: Add S2C::cmd_mall_item_price parameter when available
|
||||
{
|
||||
// TODO: Implement server update logic when S2C command structure is available
|
||||
// For now, this is a stub
|
||||
/*
|
||||
List<GShopItem> items = new List<GShopItem>(m_pItems);
|
||||
if (!ApplyChangesFromServer(items, pCmd))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (IsSame(items, m_pItems))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
m_pItems = items;
|
||||
OnItemChange();
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ApplyChangesFromServer(List<GShopItem> pItems) // TODO: Add S2C::cmd_mall_item_price parameter when available
|
||||
{
|
||||
// TODO: Implement when S2C command structure is available
|
||||
/*
|
||||
int i = 0;
|
||||
for (i = pCmd->start_index; i < pCmd->end_index; i++)
|
||||
{
|
||||
if (i < pItems.Count)
|
||||
{
|
||||
GShopItem data = pItems[i];
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
if (data.buy != null && j < data.buy.Length && data.buy[j].type != -1)
|
||||
{
|
||||
GShopBuyOption buyOption = data.buy[j];
|
||||
buyOption.type = -1;
|
||||
buyOption.price = 0;
|
||||
data.buy[j] = buyOption;
|
||||
}
|
||||
}
|
||||
pItems[i] = data;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < pCmd->count; i++)
|
||||
{
|
||||
const S2C::cmd_mall_item_price::good_item& tempList = pCmd->list[i];
|
||||
if (tempList.good_index < pItems.Count)
|
||||
{
|
||||
GShopItem data = pItems[tempList.good_index];
|
||||
if (data.id == (uint)tempList.good_id)
|
||||
{
|
||||
if (data.buy != null && tempList.good_slot < data.buy.Length)
|
||||
{
|
||||
GShopBuyOption buyOption = data.buy[tempList.good_slot];
|
||||
buyOption.price = tempList.goods_price;
|
||||
buyOption.status = tempList.good_status;
|
||||
buyOption.time = tempList.expire_time;
|
||||
buyOption.type = 3;
|
||||
data.buy[tempList.good_slot] = buyOption;
|
||||
}
|
||||
pItems[tempList.good_index] = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(data.id == (uint)tempList.good_id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
public override int GetCash()
|
||||
{
|
||||
// TODO: Integrate with CECHostPlayer.GetCash()
|
||||
// For now, return 0 as stub
|
||||
// return CECHostPlayer.Instance().GetCash();
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override bool Buy(int itemIndex, int buyIndex)
|
||||
{
|
||||
bool bOK = false;
|
||||
|
||||
if (ReadyToBuy(itemIndex, buyIndex))
|
||||
{
|
||||
GShopItem? pItem = GetItem(itemIndex);
|
||||
if (pItem.HasValue)
|
||||
{
|
||||
GShopItem item = pItem.Value;
|
||||
if (!CECShopItemOwnerNPC.HasOwnerNPC(item))
|
||||
{
|
||||
// Regular mall shopping
|
||||
UnityGameSession.Instance.RequestMallShopping(1, (int)item.id, itemIndex, buyIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
// NPC server mall shopping
|
||||
// TODO: Implement UnityGameSession.RequestNPCSevMallShopping(1, itemIndex, item.id, buyIndex)
|
||||
UnityGameSession.Instance.RequestMallShopping(1, (int)item.id, itemIndex, buyIndex);
|
||||
}
|
||||
CECQShopConfig.Instance().OnItemBuyed(item.id);
|
||||
// TODO: CECShoppingItemsMover::Instance().OnItemBuyed(this, itemIndex, buyIndex);
|
||||
bOK = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(false, "Item not ready to buy");
|
||||
}
|
||||
return bOK;
|
||||
}
|
||||
|
||||
public override bool IsFashionShopEnabled()
|
||||
{
|
||||
return CECUIConfig.Instance().GetGameUI().bEnableQShopFashionShop;
|
||||
}
|
||||
|
||||
public override bool IsFashionShopFlashSaleEnabled()
|
||||
{
|
||||
return CECUIConfig.Instance().GetGameUI().bEnableQShopFashionShopFlashSale;
|
||||
}
|
||||
|
||||
public override string GetFashionShopFlashSaleTitle()
|
||||
{
|
||||
return CECUIConfig.Instance().GetGameUI().strQShopFashionShopFlashSaleTitle;
|
||||
}
|
||||
|
||||
public override CECShopItemCategory GetFashionShopCategory()
|
||||
{
|
||||
return m_fashionShopCategory;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2d753dbecaf480345acbcb47feb6340f
|
||||
@@ -0,0 +1,40 @@
|
||||
// Filename : CECQShopConfig.cs
|
||||
// Creator : Xu Wenbin
|
||||
// Date : 2013/12/11
|
||||
// Converted to C#: 2024
|
||||
|
||||
using System;
|
||||
|
||||
namespace PerfectWorld.Scripts.Shop
|
||||
{
|
||||
// Stub implementation - to be properly implemented later
|
||||
public class CECQShopConfig
|
||||
{
|
||||
public const int CID_QSHOP_FASHION = 0; // Stub value
|
||||
public const int CID_BACKSHOP_FASHION = 1; // Stub value
|
||||
|
||||
private static CECQShopConfig s_instance;
|
||||
|
||||
public static CECQShopConfig Instance()
|
||||
{
|
||||
if (s_instance == null)
|
||||
{
|
||||
s_instance = new CECQShopConfig();
|
||||
}
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
public void FindCategory(int idCategory, out int mainType, out int subType)
|
||||
{
|
||||
// Stub implementation - to be properly implemented later
|
||||
mainType = -1;
|
||||
subType = -1;
|
||||
}
|
||||
|
||||
public void OnItemBuyed(uint goodsId)
|
||||
{
|
||||
// Stub implementation - to be properly implemented later
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c8e321ee92225d04ab0eb82b965fe32d
|
||||
@@ -0,0 +1,60 @@
|
||||
// Filename : CECShopArrayItems.cs
|
||||
// Creator : Xu Wenbin
|
||||
// Date : 2013/12/11
|
||||
// Converted to C#: 2024
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PerfectWorld.Scripts.Shop
|
||||
{
|
||||
public abstract class CECShopArrayItems : CECShopBase
|
||||
{
|
||||
protected List<GShopItem> m_pItems;
|
||||
|
||||
public CECShopArrayItems(List<GShopItem> pItems)
|
||||
{
|
||||
m_pItems = pItems;
|
||||
}
|
||||
|
||||
public override int GetCount()
|
||||
{
|
||||
return m_pItems != null ? m_pItems.Count : 0;
|
||||
}
|
||||
|
||||
public override GShopItem? GetItem(int index)
|
||||
{
|
||||
if (m_pItems != null && index >= 0 && index < m_pItems.Count)
|
||||
{
|
||||
return m_pItems[index];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool IsSame(List<GShopItem> lhs, List<GShopItem> rhs)
|
||||
{
|
||||
bool result = false;
|
||||
if (lhs == rhs)
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
else if (lhs != null && rhs != null)
|
||||
{
|
||||
if (lhs.Count == rhs.Count)
|
||||
{
|
||||
result = true;
|
||||
for (int u = 0; u < lhs.Count; ++u)
|
||||
{
|
||||
if (!CECShopBase.IsSame(lhs[u], rhs[u]))
|
||||
{
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f70fb69e0e992a24c8e03b2558f3ee6f
|
||||
@@ -0,0 +1,356 @@
|
||||
// Filename : CECShopBase.cs
|
||||
// Creator : Xu Wenbin
|
||||
// Date : 2013/12/11
|
||||
// Converted to C#: 2024
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using BrewMonster;
|
||||
|
||||
namespace PerfectWorld.Scripts.Shop
|
||||
{
|
||||
// 封装 gshop 和 backshop 等的数据访问 / Encapsulates data access for gshop and backshop etc.
|
||||
public static class CECShopConstants
|
||||
{
|
||||
public const int CECSHOP_INVALID_PRICE = -1;
|
||||
public const int BUY_COUNT = 4;
|
||||
}
|
||||
|
||||
public abstract class CECShopBase : CECObservable<CECShopBase>
|
||||
{
|
||||
private CECShopItemOwnerNPC m_ownerNPC = new CECShopItemOwnerNPC(0);
|
||||
|
||||
// CECShopBase
|
||||
|
||||
public void OnItemChange()
|
||||
{
|
||||
CECShopBaseChange change = new CECShopBaseChange((uint)CECShopBaseChange.ChangeMask.ITEM_CHANGED);
|
||||
NotifyObservers(change);
|
||||
}
|
||||
|
||||
public bool ValidateTimeStamp()
|
||||
{
|
||||
return GetLocalTimeStamp() == GetServerTimeStamp();
|
||||
}
|
||||
|
||||
public void SetOwnerNPCID(uint ownerNPCID)
|
||||
{
|
||||
if (ownerNPCID == m_ownerNPC.GetOwnerNPCID())
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_ownerNPC.SetOwnerNPCID(ownerNPCID);
|
||||
OnItemChange();
|
||||
}
|
||||
|
||||
public uint GetOwnerNPCID()
|
||||
{
|
||||
return m_ownerNPC.GetOwnerNPCID();
|
||||
}
|
||||
|
||||
public bool HasOwnerNPC()
|
||||
{
|
||||
return !m_ownerNPC.IsEmpty();
|
||||
}
|
||||
|
||||
public CECShopItemOwnerNPC GetOwnerNPC()
|
||||
{
|
||||
return m_ownerNPC;
|
||||
}
|
||||
|
||||
public bool MatchOwnerNPC(GShopItem rhs)
|
||||
{
|
||||
return m_ownerNPC.MatchItem(rhs);
|
||||
}
|
||||
|
||||
public bool IsValid(int itemIndex, int buyIndex)
|
||||
{
|
||||
bool bValid = false;
|
||||
GShopItem? pItem = GetItem(itemIndex);
|
||||
if (pItem.HasValue)
|
||||
{
|
||||
if (buyIndex >= 0 && buyIndex < CECShopConstants.BUY_COUNT)
|
||||
{
|
||||
bValid = true;
|
||||
}
|
||||
}
|
||||
return bValid;
|
||||
}
|
||||
|
||||
public bool ReadyToBuy(int itemIndex, int buyIndex)
|
||||
{
|
||||
bool result = false;
|
||||
int[] buyType = new int[CECShopConstants.BUY_COUNT];
|
||||
if (IsValid(itemIndex, buyIndex) && CalcBuyType(itemIndex, buyType))
|
||||
{
|
||||
for (int i = 0; i < CECShopConstants.BUY_COUNT; ++i)
|
||||
{
|
||||
if (buyType[i] == buyIndex)
|
||||
{
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool CalcBuyType(int itemIndex, int[] buyTypes)
|
||||
{
|
||||
bool bOK = false;
|
||||
GShopItem? pItem = GetItem(itemIndex);
|
||||
if (pItem.HasValue)
|
||||
{
|
||||
GShopItem item = pItem.Value;
|
||||
int[] typeDefault = new int[CECShopConstants.BUY_COUNT];
|
||||
int[] typeNew = new int[CECShopConstants.BUY_COUNT];
|
||||
int index1 = 0;
|
||||
int index2 = 0;
|
||||
for (int i = 0; i < CECShopConstants.BUY_COUNT; i++)
|
||||
{
|
||||
typeDefault[i] = -1;
|
||||
typeNew[i] = -1;
|
||||
if (item.buy != null && i < item.buy.Length)
|
||||
{
|
||||
if (item.buy[i].type == 3 && item.buy[i].price > 0)
|
||||
{
|
||||
typeNew[index1] = i;
|
||||
index1++;
|
||||
}
|
||||
else if (item.buy[i].type == -1 && item.buy[i].price > 0)
|
||||
{
|
||||
typeDefault[index2] = i;
|
||||
index2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (index1 > 0)
|
||||
{
|
||||
Array.Copy(typeNew, buyTypes, CECShopConstants.BUY_COUNT);
|
||||
bOK = true;
|
||||
}
|
||||
else if (index2 > 0)
|
||||
{
|
||||
Array.Copy(typeDefault, buyTypes, CECShopConstants.BUY_COUNT);
|
||||
bOK = true;
|
||||
}
|
||||
}
|
||||
return bOK;
|
||||
}
|
||||
|
||||
public bool HasSameBuyType(int itemIndexA, int itemIndexB)
|
||||
{
|
||||
int[] typeA = new int[CECShopConstants.BUY_COUNT];
|
||||
int[] typeB = new int[CECShopConstants.BUY_COUNT];
|
||||
if (CalcBuyType(itemIndexA, typeA) &&
|
||||
CalcBuyType(itemIndexB, typeB) &&
|
||||
typeA.SequenceEqual(typeB))
|
||||
{
|
||||
GShopItem? pItemA = GetItem(itemIndexA);
|
||||
GShopItem? pItemB = GetItem(itemIndexB);
|
||||
if (pItemA.HasValue && pItemB.HasValue)
|
||||
{
|
||||
GShopItem itemA = pItemA.Value;
|
||||
GShopItem itemB = pItemB.Value;
|
||||
for (int i = 0; i < CECShopConstants.BUY_COUNT; ++i)
|
||||
{
|
||||
int buyIndex = typeA[i];
|
||||
if (buyIndex != -1)
|
||||
{
|
||||
bool priceAValid = (itemA.buy != null && buyIndex < itemA.buy.Length && itemA.buy[buyIndex].price != 0);
|
||||
bool priceBValid = (itemB.buy != null && buyIndex < itemB.buy.Length && itemB.buy[buyIndex].price != 0);
|
||||
if (priceAValid != priceBValid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!priceAValid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (itemA.buy[buyIndex].time != itemB.buy[buyIndex].time)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool CalcBuyIndex(int itemIndex, out int buyIndex, int cash = -1)
|
||||
{
|
||||
buyIndex = -1;
|
||||
bool bOK = false;
|
||||
GShopItem? pItem = GetItem(itemIndex);
|
||||
if (pItem.HasValue)
|
||||
{
|
||||
GShopItem item = pItem.Value;
|
||||
// 参考实现 CDlgQShopItem::SetItem / Reference implementation CDlgQShopItem::SetItem
|
||||
|
||||
// 判断要显示的购买方式 / Determine which purchase method to display
|
||||
int[] m_TypeDefault = new int[CECShopConstants.BUY_COUNT];
|
||||
int[] m_TypeNew = new int[CECShopConstants.BUY_COUNT];
|
||||
int index1 = 0;
|
||||
int index2 = 0;
|
||||
for (int i = 0; i < CECShopConstants.BUY_COUNT; i++)
|
||||
{
|
||||
m_TypeDefault[i] = -1;
|
||||
m_TypeNew[i] = -1;
|
||||
if (item.buy != null && i < item.buy.Length)
|
||||
{
|
||||
if (item.buy[i].type == 3 && item.buy[i].price > 0)
|
||||
{
|
||||
m_TypeNew[index1] = i;
|
||||
index1++;
|
||||
}
|
||||
else if (item.buy[i].type == -1 && item.buy[i].price > 0)
|
||||
{
|
||||
m_TypeDefault[index2] = i;
|
||||
index2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
int m_BuyType = -1;
|
||||
if (index1 > 0)
|
||||
{
|
||||
m_BuyType = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_BuyType = 1;
|
||||
}
|
||||
|
||||
if (cash == -1)
|
||||
{
|
||||
cash = GetCash();
|
||||
}
|
||||
for (int i = 0; i < CECShopConstants.BUY_COUNT; i++)
|
||||
{
|
||||
int BuyIndex = 0;
|
||||
if (m_BuyType == 0)
|
||||
{
|
||||
BuyIndex = m_TypeNew[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
BuyIndex = m_TypeDefault[i];
|
||||
}
|
||||
if (BuyIndex != -1 && item.buy != null && BuyIndex < item.buy.Length)
|
||||
{
|
||||
uint price = item.buy[BuyIndex].price;
|
||||
if (price != 0 && price <= cash)
|
||||
{
|
||||
bOK = true;
|
||||
buyIndex = BuyIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return bOK;
|
||||
}
|
||||
|
||||
public int GetPrice(int itemIndex, int buyIndex)
|
||||
{
|
||||
int price = CECShopConstants.CECSHOP_INVALID_PRICE;
|
||||
if (IsValid(itemIndex, buyIndex))
|
||||
{
|
||||
GShopItem? pItem = GetItem(itemIndex);
|
||||
if (pItem.HasValue && pItem.Value.buy != null && buyIndex < pItem.Value.buy.Length)
|
||||
{
|
||||
price = (int)pItem.Value.buy[buyIndex].price;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEngine.Debug.Assert(false);
|
||||
}
|
||||
return price;
|
||||
}
|
||||
|
||||
public uint GetStatus(int itemIndex, int buyIndex)
|
||||
{
|
||||
uint status = 0xFFFFFFFF; // -1 as uint
|
||||
if (IsValid(itemIndex, buyIndex))
|
||||
{
|
||||
GShopItem? pItem = GetItem(itemIndex);
|
||||
if (pItem.HasValue && pItem.Value.buy != null && buyIndex < pItem.Value.buy.Length)
|
||||
{
|
||||
status = pItem.Value.buy[buyIndex].status;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEngine.Debug.Assert(false);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
public static bool IsSame(GShopItem lhs, GShopItem rhs)
|
||||
{
|
||||
bool result = false;
|
||||
if (lhs.mainType != rhs.mainType ||
|
||||
lhs.subType != rhs.subType ||
|
||||
lhs.id != rhs.id ||
|
||||
lhs.num != rhs.num ||
|
||||
lhs.idGift != rhs.idGift ||
|
||||
lhs.giftNum != rhs.giftNum)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
result = true;
|
||||
if (lhs.buy != null && rhs.buy != null)
|
||||
{
|
||||
int buyCount = Math.Min(lhs.buy.Length, rhs.buy.Length);
|
||||
for (int i = 0; i < buyCount && i < CECShopConstants.BUY_COUNT; ++i)
|
||||
{
|
||||
if (lhs.buy[i].type != rhs.buy[i].type ||
|
||||
lhs.buy[i].price != rhs.buy[i].price ||
|
||||
lhs.buy[i].time != rhs.buy[i].time ||
|
||||
lhs.buy[i].status != rhs.buy[i].status)
|
||||
{
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int GetOriginalPrice(int finalPrice, uint discountStatus)
|
||||
{
|
||||
int result = finalPrice;
|
||||
if (discountStatus >= 4 && discountStatus <= 12)
|
||||
{
|
||||
float originalPrice = finalPrice * (10.0f / (discountStatus - 3));
|
||||
if (CECUIConfig.Instance().GetGameUI().bEnableCeilPriceBeforeDiscountToGold)
|
||||
{
|
||||
result = (int)Math.Ceiling(originalPrice) + 99;
|
||||
result /= 100;
|
||||
result *= 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = (int)Math.Ceiling(originalPrice);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Abstract methods
|
||||
public abstract bool GetFromServer(int beginIndex, int endIndex);
|
||||
public abstract uint GetLocalTimeStamp();
|
||||
public abstract uint GetServerTimeStamp();
|
||||
public abstract int GetCount();
|
||||
public abstract GShopItem? GetItem(int index);
|
||||
public abstract int GetCash();
|
||||
public abstract bool Buy(int itemIndex, int buyIndex);
|
||||
public abstract bool IsFashionShopEnabled();
|
||||
public abstract CECShopItemCategory GetFashionShopCategory();
|
||||
public abstract bool IsFashionShopFlashSaleEnabled();
|
||||
public abstract string GetFashionShopFlashSaleTitle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16ba0a2da752c6a49ad053e4af8be921
|
||||
@@ -0,0 +1,36 @@
|
||||
// Filename : CECShopBaseChange.cs
|
||||
// Creator : Xu Wenbin
|
||||
// Date : 2013/12/11
|
||||
// Converted to C#: 2024
|
||||
|
||||
using BrewMonster;
|
||||
|
||||
namespace PerfectWorld.Scripts.Shop
|
||||
{
|
||||
// 封装 CECShopBase 的变化 / Encapsulates changes to CECShopBase
|
||||
public class CECShopBaseChange : CECObservableChange
|
||||
{
|
||||
public enum ChangeMask
|
||||
{
|
||||
ITEM_CHANGED = 0x01,
|
||||
}
|
||||
|
||||
private uint m_changeMask;
|
||||
|
||||
public CECShopBaseChange(uint changeMask)
|
||||
{
|
||||
m_changeMask = changeMask;
|
||||
}
|
||||
|
||||
public uint GetChangeMask()
|
||||
{
|
||||
return m_changeMask;
|
||||
}
|
||||
|
||||
public bool ItemChanged()
|
||||
{
|
||||
return (GetChangeMask() & (uint)ChangeMask.ITEM_CHANGED) != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e995fc9c415a8a143a75c18021965074
|
||||
@@ -0,0 +1,59 @@
|
||||
// Filename : CECShopItemCategory.cs
|
||||
// Creator : Xu Wenbin
|
||||
// Date : 2013/12/11
|
||||
// Converted to C#: 2024
|
||||
|
||||
using System;
|
||||
|
||||
namespace PerfectWorld.Scripts.Shop
|
||||
{
|
||||
// 封装 GSHOP_ITEM 的分类信息 / Encapsulates category information for GSHOP_ITEM
|
||||
public class CECShopItemCategory
|
||||
{
|
||||
private int m_mainType;
|
||||
private int m_subType;
|
||||
|
||||
public CECShopItemCategory(int mainType = -1, int subType = -1)
|
||||
{
|
||||
m_mainType = mainType;
|
||||
m_subType = subType;
|
||||
}
|
||||
|
||||
public void InitFromQShopConfig(int idCategory)
|
||||
{
|
||||
int mainType = -1;
|
||||
int subType = -1;
|
||||
CECQShopConfig.Instance().FindCategory(idCategory, out mainType, out subType);
|
||||
SetCategory(mainType, subType);
|
||||
}
|
||||
|
||||
public void SetCategory(int mainType, int subType)
|
||||
{
|
||||
m_mainType = mainType;
|
||||
m_subType = subType;
|
||||
}
|
||||
|
||||
public int GetMainType()
|
||||
{
|
||||
return m_mainType;
|
||||
}
|
||||
|
||||
public int GetSubType()
|
||||
{
|
||||
return m_subType;
|
||||
}
|
||||
|
||||
public bool MatchMainType(int mainType)
|
||||
{
|
||||
return m_mainType >= 0 && m_mainType == mainType;
|
||||
}
|
||||
|
||||
public bool MatchItem(GShopItem item)
|
||||
{
|
||||
// 匹配主类型有效时检查 / Check when main type matching is valid
|
||||
return MatchMainType(item.mainType)
|
||||
&& (m_subType < 0 || item.subType == m_subType); // 子类型小于0时表示不限制子类型 / When sub type < 0, means no sub type restriction
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c8734b057cb3bec4d83b03b28b652b6a
|
||||
@@ -0,0 +1,79 @@
|
||||
// Filename : CECShopItemOwnerNPC.cs
|
||||
// Creator : Xu Wenbin
|
||||
// Date : 2013/12/11
|
||||
// Converted to C#: 2024
|
||||
|
||||
using System;
|
||||
|
||||
namespace PerfectWorld.Scripts.Shop
|
||||
{
|
||||
// 封装 GSHOP_ITEM 的挂靠 NPC 查询 / Encapsulates NPC ownership query for GSHOP_ITEM
|
||||
public class CECShopItemOwnerNPC
|
||||
{
|
||||
private const int TREASURE_ITEM_OWNER_NPC_COUNT = 8;
|
||||
|
||||
private uint m_ownerNPCID;
|
||||
|
||||
public CECShopItemOwnerNPC(uint ownerNPCID = 0)
|
||||
{
|
||||
m_ownerNPCID = ownerNPCID;
|
||||
}
|
||||
|
||||
public void SetOwnerNPCID(uint ownerNPCID)
|
||||
{
|
||||
m_ownerNPCID = ownerNPCID;
|
||||
}
|
||||
|
||||
public uint GetOwnerNPCID()
|
||||
{
|
||||
return m_ownerNPCID;
|
||||
}
|
||||
|
||||
public bool IsEmpty()
|
||||
{
|
||||
return GetOwnerNPCID() == 0;
|
||||
}
|
||||
|
||||
public bool Equals(CECShopItemOwnerNPC rhs)
|
||||
{
|
||||
return GetOwnerNPCID() == rhs.GetOwnerNPCID();
|
||||
}
|
||||
|
||||
public bool MatchID(uint id)
|
||||
{
|
||||
return id == m_ownerNPCID;
|
||||
}
|
||||
|
||||
public bool MatchItem(GShopItem item)
|
||||
{
|
||||
if (IsEmpty())
|
||||
{
|
||||
return !HasOwnerNPC(item);
|
||||
}
|
||||
bool result = false;
|
||||
if (item.ownerNpcs != null)
|
||||
{
|
||||
for (int i = 0; i < TREASURE_ITEM_OWNER_NPC_COUNT && i < item.ownerNpcs.Length; ++i)
|
||||
{
|
||||
if (MatchID(item.ownerNpcs[i]))
|
||||
{
|
||||
result = true; // 找到了 / Found
|
||||
break;
|
||||
}
|
||||
if (item.ownerNpcs[i] == 0)
|
||||
{
|
||||
break; // 已经到最后一个了,后面都是0 / Already reached the last one, rest are 0
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static bool HasOwnerNPC(GShopItem item)
|
||||
{
|
||||
// 编辑器保证数组中,第一个是非零的在前 / Editor ensures that in the array, the first non-zero is at the front
|
||||
return item.ownerNpcs != null && item.ownerNpcs.Length > 0 && item.ownerNpcs[0] != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f4bc2fd669173534b9f94313c78af59e
|
||||
@@ -0,0 +1,39 @@
|
||||
// Filename : CECUIConfig.cs
|
||||
// Creator : Xu Wenbin
|
||||
// Date : 2013/12/11
|
||||
// Converted to C#: 2024
|
||||
|
||||
namespace PerfectWorld.Scripts.Shop
|
||||
{
|
||||
// Stub implementation - to be properly implemented later
|
||||
public class CECUIConfig
|
||||
{
|
||||
private static CECUIConfig s_instance;
|
||||
|
||||
public static CECUIConfig Instance()
|
||||
{
|
||||
if (s_instance == null)
|
||||
{
|
||||
s_instance = new CECUIConfig();
|
||||
}
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
public GameUIConfig GetGameUI()
|
||||
{
|
||||
return new GameUIConfig();
|
||||
}
|
||||
}
|
||||
|
||||
public class GameUIConfig
|
||||
{
|
||||
public bool bEnableQShopFashionShop = false;
|
||||
public bool bEnableQShopFashionShopFlashSale = false;
|
||||
public string strQShopFashionShopFlashSaleTitle = "";
|
||||
public bool bEnableBackShopFashionShop = false;
|
||||
public bool bEnableBackShopFashionShopFlashSale = false;
|
||||
public string strBackShopFashionShopFlashSaleTitle = "";
|
||||
public bool bEnableCeilPriceBeforeDiscountToGold = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 28ee6f77dbdc1204eb2a2f933941e610
|
||||
@@ -1460,6 +1460,32 @@ namespace BrewMonster.UI
|
||||
//pButton = (Button)pShow2.GetDlgItem("Btn_QuestItem");
|
||||
//pButton.SetPushed(false);
|
||||
//GetGameUIMan().m_pDlgInventory.SetShowItem(CDlgInventory::INVENTORY_ITEM_NORMAL);
|
||||
if (pCurNPCEssence.HasValue)
|
||||
{
|
||||
uint npcID = pCurNPCEssence.Value.id;
|
||||
NPCShopUIManager shopManager = FindFirstObjectByType<NPCShopUIManager>();
|
||||
if (shopManager == null)
|
||||
{
|
||||
// Instantiate NPCShopUIManager if not found, similar to how DlgNPC is instantiated
|
||||
CECGameUIMan gameUIMan = GetGameUIMan();
|
||||
DialogScriptTableObject dialogResource = gameUIMan.GetDialogResource();
|
||||
Canvas canvas = gameUIMan.GetCanvas();
|
||||
|
||||
if (dialogResource != null && canvas != null)
|
||||
{
|
||||
GameObject ob = dialogResource.GetPrefabDialog("DialogNPCShop");
|
||||
if (ob != null)
|
||||
{
|
||||
shopManager = GameObject.Instantiate(ob, canvas.transform).GetComponent<NPCShopUIManager>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shopManager != null)
|
||||
{
|
||||
shopManager.OpenNPCShop(npcID);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (idFunction == (int)SERVICE_TYPE.NPC_INSTALL)
|
||||
{
|
||||
@@ -3072,6 +3098,7 @@ namespace BrewMonster.UI
|
||||
SetData(NPC_DIALOG.NPC_DIALOG_TALK, "");
|
||||
}
|
||||
}
|
||||
//Show Mua item id = 0
|
||||
else
|
||||
{
|
||||
object pData1 = m_pLst_Main.GetItemDataPtr(nCurSel, 0, "");
|
||||
@@ -3111,6 +3138,32 @@ namespace BrewMonster.UI
|
||||
//pButton = (PAUISTILLIMAGEBUTTON)pShow2.GetDlgItem("Btn_QuestItem");
|
||||
//pButton.SetPushed(false);
|
||||
//GetGameUIMan().m_pDlgInventory.SetShowItem(CDlgInventory::INVENTORY_ITEM_NORMAL);
|
||||
if (pCurNPCEssence.HasValue)
|
||||
{
|
||||
uint npcID = pCurNPCEssence.Value.id;
|
||||
NPCShopUIManager shopManager = FindFirstObjectByType<NPCShopUIManager>();
|
||||
if (shopManager == null)
|
||||
{
|
||||
// Instantiate NPCShopUIManager if not found, similar to how DlgNPC is instantiated
|
||||
CECGameUIMan gameUIMan = GetGameUIMan();
|
||||
DialogScriptTableObject dialogResource = gameUIMan.GetDialogResource();
|
||||
Canvas canvas = gameUIMan.GetCanvas();
|
||||
|
||||
if (dialogResource != null && canvas != null)
|
||||
{
|
||||
GameObject ob = dialogResource.GetPrefabDialog("DialogNPCShop");
|
||||
if (ob != null)
|
||||
{
|
||||
shopManager = GameObject.Instantiate(ob, canvas.transform).GetComponent<NPCShopUIManager>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shopManager != null)
|
||||
{
|
||||
shopManager.OpenNPCShop(npcID);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (idFunction == (int)SERVICE_TYPE.NPC_INSTALL)
|
||||
{
|
||||
|
||||
@@ -103,6 +103,15 @@ namespace BrewMonster.UI
|
||||
return m_pDlgTask.UpdateQuestView();
|
||||
}
|
||||
}
|
||||
public DialogScriptTableObject GetDialogResource()
|
||||
{
|
||||
return m_dialogResouce;
|
||||
}
|
||||
|
||||
public Canvas GetCanvas()
|
||||
{
|
||||
return m_canvas;
|
||||
}
|
||||
}
|
||||
public enum EC_GAMEUI_ICONS
|
||||
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
// Filename : NPCShopDetailPanel.cs
|
||||
// Creator : Converted from C++ EC_Shop
|
||||
// Date : 2024
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using PerfectWorld.Scripts.Shop;
|
||||
using PerfectWorld.Scripts.Managers;
|
||||
using BrewMonster.Scripts.Managers;
|
||||
using BrewMonster.Network;
|
||||
using CSNetwork.C2SCommand;
|
||||
|
||||
public class NPCShopDetailPanel : MonoBehaviour
|
||||
{
|
||||
[Header("UI Components")]
|
||||
public TextOutlet itemDescriptionText;
|
||||
public Image itemIconImage;
|
||||
//public TextMeshProUGUI itemPriceText;
|
||||
|
||||
[Header("Action Buttons")]
|
||||
public Button closeButton;
|
||||
public Button buyButton;
|
||||
|
||||
private GShopItem currentItem;
|
||||
private NPCShopUIManager shopManager;
|
||||
|
||||
void Start()
|
||||
{
|
||||
SetupEventListeners();
|
||||
}
|
||||
|
||||
void SetupEventListeners()
|
||||
{
|
||||
if (closeButton != null)
|
||||
closeButton.onClick.AddListener(OnCloseClicked);
|
||||
|
||||
if (buyButton != null)
|
||||
buyButton.onClick.AddListener(OnBuyButtonClicked);
|
||||
}
|
||||
|
||||
public void SetupDetailPanel(GShopItem item, NPCShopUIManager manager)
|
||||
{
|
||||
currentItem = item;
|
||||
shopManager = manager;
|
||||
|
||||
UpdateDisplay();
|
||||
}
|
||||
|
||||
void UpdateDisplay()
|
||||
{
|
||||
if (currentItem.id == 0)
|
||||
{
|
||||
Debug.LogWarning("[NPCShopDetailPanel] Current item ID is 0, skipping display update");
|
||||
return;
|
||||
}
|
||||
|
||||
// Set item description
|
||||
if (itemDescriptionText != null)
|
||||
{
|
||||
string description = GetItemDescription((int)currentItem.id);
|
||||
itemDescriptionText.Set(description);
|
||||
}
|
||||
|
||||
// Set price (use first available price)
|
||||
uint price = 0;
|
||||
if (currentItem.buy != null && currentItem.buy.Length > 0)
|
||||
{
|
||||
for (int i = 0; i < currentItem.buy.Length; i++)
|
||||
{
|
||||
if (currentItem.buy[i].price > 0)
|
||||
{
|
||||
price = currentItem.buy[i].price;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if (itemPriceText != null)
|
||||
// itemPriceText.text = price > 0 ? $"Price: {price}" : "Price: N/A";
|
||||
|
||||
// Load icon
|
||||
if (itemIconImage != null)
|
||||
{
|
||||
LoadItemIcon(itemIconImage, (int)currentItem.id);
|
||||
}
|
||||
}
|
||||
|
||||
void LoadItemIcon(Image iconImage, int itemId)
|
||||
{
|
||||
if (itemId <= 0 || iconImage == null)
|
||||
return;
|
||||
|
||||
// Ensure the Image component is enabled and properly configured
|
||||
iconImage.enabled = true;
|
||||
iconImage.preserveAspect = true;
|
||||
iconImage.type = Image.Type.Simple;
|
||||
|
||||
// Use the existing icon loading system from EC_IvtrItemUtils
|
||||
Sprite iconSprite = EC_IvtrItemUtils.Instance.ResolveItemIconSprite(itemId);
|
||||
|
||||
if (iconSprite != null)
|
||||
{
|
||||
iconImage.sprite = iconSprite;
|
||||
iconImage.color = Color.white;
|
||||
}
|
||||
else
|
||||
{
|
||||
iconImage.sprite = null;
|
||||
}
|
||||
}
|
||||
|
||||
string GetItemDescription(int itemId)
|
||||
{
|
||||
// First check if description is already in the item data
|
||||
if (!string.IsNullOrEmpty(currentItem.desc))
|
||||
{
|
||||
return currentItem.desc;
|
||||
}
|
||||
|
||||
// Try to use EC_IvtrEquip description for equipment items (like EC_InventoryUI does)
|
||||
try
|
||||
{
|
||||
// Create EC_IvtrEquip for equipment items - this will work for weapons and armor
|
||||
EC_IvtrEquip equipment = new EC_IvtrEquip(itemId, 0);
|
||||
|
||||
// For NPC shop items, we typically have static data only
|
||||
// Try to get description using GetNormalDesc() which works with base stats
|
||||
string equipDesc = equipment.GetNormalDesc();
|
||||
if (!string.IsNullOrEmpty(equipDesc))
|
||||
{
|
||||
// Replace C++ style "\r" line separators with real newlines for TMP
|
||||
return equipDesc.Replace("\\r", "\n");
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
// If EC_IvtrEquip fails, fall through to EC_IvtrItem method
|
||||
Debug.LogWarning($"[NPCShopDetailPanel] Failed to get equipment description for item {itemId}: {ex.Message}");
|
||||
}
|
||||
|
||||
// Fallback: build description using EC_IvtrItem (for non-equipment items)
|
||||
try
|
||||
{
|
||||
EC_IvtrItem item = EC_IvtrItem.CreateItem(itemId, 0, 1);
|
||||
if (item != null)
|
||||
{
|
||||
// For NPC shop, we typically have static data only, so load from local DB
|
||||
item.GetDetailDataFromLocal();
|
||||
string description = item.GetDesc();
|
||||
if (!string.IsNullOrEmpty(description))
|
||||
{
|
||||
return description.Replace("\\r", "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"[NPCShopDetailPanel] Failed to get description for item {itemId}: {ex.Message}");
|
||||
}
|
||||
|
||||
return "No description available.";
|
||||
}
|
||||
|
||||
void OnCloseClicked()
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
void OnBuyButtonClicked()
|
||||
{
|
||||
if (currentItem.id == 0)
|
||||
{
|
||||
Debug.LogWarning("[NPCShopDetailPanel] Cannot buy item with ID 0");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get price from item
|
||||
uint price = 0;
|
||||
if (currentItem.buy != null && currentItem.buy.Length > 0)
|
||||
{
|
||||
for (int i = 0; i < currentItem.buy.Length; i++)
|
||||
{
|
||||
if (currentItem.buy[i].price > 0)
|
||||
{
|
||||
price = currentItem.buy[i].price;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create npc_trade_item array for buying from NPC
|
||||
// The tid is the item template ID, index is shop item index (0 for now), count is quantity to buy
|
||||
npc_trade_item[] items = new npc_trade_item[1];
|
||||
items[0] = new npc_trade_item
|
||||
{
|
||||
tid = (int)currentItem.id,
|
||||
index = 0, // Shop item index - may need to be determined from shop item position
|
||||
count = 1 // Quantity to buy
|
||||
};
|
||||
|
||||
// Send the buy command
|
||||
UnityGameSession.c2s_CmdNPCSevBuy(1, items);
|
||||
|
||||
Debug.Log($"[NPCShopDetailPanel] Sent buy command for item {currentItem.id}, price {price}");
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
// Clean up event listeners
|
||||
if (closeButton != null)
|
||||
closeButton.onClick.RemoveListener(OnCloseClicked);
|
||||
|
||||
if (buyButton != null)
|
||||
buyButton.onClick.RemoveListener(OnBuyButtonClicked);
|
||||
}
|
||||
|
||||
// === Text Outlet Helper ===
|
||||
[System.Serializable]
|
||||
public class TextOutlet
|
||||
{
|
||||
[SerializeField] public Text legacy;
|
||||
[SerializeField] public TMPro.TextMeshProUGUI tmp;
|
||||
|
||||
public void Set(string value)
|
||||
{
|
||||
if (legacy != null)
|
||||
{
|
||||
legacy.text = EC_Utility.FormatForLegacyText(value ?? string.Empty);
|
||||
}
|
||||
if (tmp != null)
|
||||
{
|
||||
tmp.text = EC_Utility.FormatForTextMeshPro(value ?? string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8456b18cc099671499414348dc8b6833
|
||||
@@ -0,0 +1,171 @@
|
||||
// Filename : NPCShopItemPanel.cs
|
||||
// Creator : Converted from C++ EC_Shop
|
||||
// Date : 2024
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using System.Collections;
|
||||
using PerfectWorld.Scripts.Shop;
|
||||
using BrewMonster.Scripts.Managers;
|
||||
|
||||
public class NPCShopItemPanel : MonoBehaviour
|
||||
{
|
||||
[Header("UI Components")]
|
||||
public TextMeshProUGUI itemNameText;
|
||||
public TextMeshProUGUI itemPriceText;
|
||||
public Image itemIconImage;
|
||||
|
||||
private GShopItem itemData;
|
||||
private Coroutine iconLoadCoroutine;
|
||||
private NPCShopUIManager shopManager;
|
||||
|
||||
void Start()
|
||||
{
|
||||
SetupClickHandler();
|
||||
}
|
||||
|
||||
void SetupClickHandler()
|
||||
{
|
||||
// Check for existing Button component
|
||||
Button button = GetComponent<Button>();
|
||||
if (button == null)
|
||||
button = GetComponentInChildren<Button>();
|
||||
|
||||
if (button != null)
|
||||
{
|
||||
button.onClick.AddListener(OnItemClicked);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If no button exists, add one to the Image or create a clickable area
|
||||
if (itemIconImage != null)
|
||||
{
|
||||
// Try to add Button component to the icon image
|
||||
Button iconButton = itemIconImage.GetComponent<Button>();
|
||||
if (iconButton == null)
|
||||
iconButton = itemIconImage.gameObject.AddComponent<Button>();
|
||||
|
||||
if (iconButton != null)
|
||||
{
|
||||
iconButton.onClick.AddListener(OnItemClicked);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetupItem(GShopItem item, NPCShopUIManager manager)
|
||||
{
|
||||
itemData = item;
|
||||
shopManager = manager;
|
||||
UpdateDisplay();
|
||||
}
|
||||
|
||||
void OnItemClicked()
|
||||
{
|
||||
if (shopManager != null && itemData.id != 0)
|
||||
{
|
||||
shopManager.ShowItemDetail(itemData);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateDisplay()
|
||||
{
|
||||
if (itemData.id == 0)
|
||||
return;
|
||||
|
||||
// Set item name
|
||||
if (itemNameText != null)
|
||||
itemNameText.text = itemData.name;
|
||||
|
||||
// Set price (use first available price)
|
||||
uint price = 0;
|
||||
if (itemData.buy != null && itemData.buy.Length > 0)
|
||||
{
|
||||
for (int i = 0; i < itemData.buy.Length; i++)
|
||||
{
|
||||
if (itemData.buy[i].price > 0)
|
||||
{
|
||||
price = itemData.buy[i].price;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (itemPriceText != null)
|
||||
itemPriceText.text = price > 0 ? price.ToString() : "N/A";
|
||||
|
||||
// Load icon - use coroutine to ensure UI is ready
|
||||
if (itemIconImage != null)
|
||||
{
|
||||
// Stop any existing icon load coroutine
|
||||
if (iconLoadCoroutine != null)
|
||||
{
|
||||
StopCoroutine(iconLoadCoroutine);
|
||||
}
|
||||
|
||||
// Start new icon load coroutine
|
||||
iconLoadCoroutine = StartCoroutine(LoadItemIconCoroutine(itemIconImage, (int)itemData.id));
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator LoadItemIconCoroutine(Image iconImage, int itemId)
|
||||
{
|
||||
// Wait one frame to ensure UI is fully initialized
|
||||
yield return null;
|
||||
|
||||
LoadItemIcon(iconImage, itemId);
|
||||
}
|
||||
|
||||
void LoadItemIcon(Image iconImage, int itemId)
|
||||
{
|
||||
if (itemId <= 0 || iconImage == null)
|
||||
return;
|
||||
|
||||
// Ensure the Image component is enabled and properly configured
|
||||
iconImage.enabled = true;
|
||||
iconImage.preserveAspect = true;
|
||||
iconImage.type = Image.Type.Simple;
|
||||
|
||||
// Use the existing icon loading system from EC_IvtrItemUtils
|
||||
Sprite iconSprite = EC_IvtrItemUtils.Instance.ResolveItemIconSprite(itemId);
|
||||
|
||||
if (iconSprite != null)
|
||||
{
|
||||
iconImage.sprite = iconSprite;
|
||||
iconImage.color = Color.white;
|
||||
}
|
||||
else
|
||||
{
|
||||
iconImage.sprite = null;
|
||||
}
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (iconLoadCoroutine != null)
|
||||
{
|
||||
StopCoroutine(iconLoadCoroutine);
|
||||
}
|
||||
|
||||
// Clean up button listeners
|
||||
Button button = GetComponent<Button>();
|
||||
if (button == null)
|
||||
button = GetComponentInChildren<Button>();
|
||||
|
||||
if (button != null)
|
||||
{
|
||||
button.onClick.RemoveListener(OnItemClicked);
|
||||
}
|
||||
|
||||
if (itemIconImage != null)
|
||||
{
|
||||
Button iconButton = itemIconImage.GetComponent<Button>();
|
||||
if (iconButton != null)
|
||||
{
|
||||
iconButton.onClick.RemoveListener(OnItemClicked);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14c449229b5a12a42892e4fa263d6cbd
|
||||
@@ -0,0 +1,464 @@
|
||||
// Filename : NPCShopUIManager.cs
|
||||
// Creator : Converted from C++ EC_Shop
|
||||
// Date : 2024
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using PerfectWorld.Scripts.Shop;
|
||||
using BrewMonster;
|
||||
using static CSNetwork.Common.ExpTypes;
|
||||
using ModelRenderer.Scripts.Common;
|
||||
|
||||
public class NPCShopUIManager : MonoBehaviour
|
||||
{
|
||||
[Header("UI Panels")]
|
||||
public GameObject npcShopMainPanel;
|
||||
public GameObject npcShopDetailPanel;
|
||||
|
||||
[Header("Tabs")]
|
||||
public Transform tabButtonContainer;
|
||||
public GameObject tabButtonPrefab;
|
||||
public string tabButtonTextComponentName = "Text";
|
||||
|
||||
[Header("Item Display")]
|
||||
public Transform itemContainer;
|
||||
public GameObject itemPanelPrefab;
|
||||
|
||||
[Header("Main Panel Components")]
|
||||
public Button closeShopButton;
|
||||
|
||||
private List<GameObject> currentItemPanels = new List<GameObject>();
|
||||
private List<GameObject> currentTabButtons = new List<GameObject>();
|
||||
private List<int> tabPageMapping = new List<int>(); // Maps tab index to page index
|
||||
private int currentTabIndex = 0;
|
||||
private uint currentNPCID = 0;
|
||||
private NPC_SELL_SERVICE? cachedSellService = null;
|
||||
private NPCShopDetailPanel detailPanelScript;
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (closeShopButton != null)
|
||||
closeShopButton.onClick.AddListener(CloseShop);
|
||||
|
||||
// Initialize detail panel reference
|
||||
if (npcShopDetailPanel != null)
|
||||
{
|
||||
detailPanelScript = npcShopDetailPanel.GetComponent<NPCShopDetailPanel>();
|
||||
if (detailPanelScript == null)
|
||||
detailPanelScript = npcShopDetailPanel.GetComponentInChildren<NPCShopDetailPanel>();
|
||||
|
||||
// Initially hide the detail panel
|
||||
if (npcShopDetailPanel.activeSelf)
|
||||
npcShopDetailPanel.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenNPCShop(uint npcID)
|
||||
{
|
||||
if (npcShopMainPanel == null)
|
||||
{
|
||||
Debug.LogError("NPCShopUIManager: npcShopMainPanel is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get NPC ID from CECUIManager
|
||||
uint actualNPCID = npcID;
|
||||
CECUIManager uiManager = CECUIManager.Instance;
|
||||
if (uiManager != null)
|
||||
{
|
||||
int managerNPCID = uiManager.GetCurrentTargetNPCID();
|
||||
if (managerNPCID > 0)
|
||||
{
|
||||
actualNPCID = (uint)managerNPCID;
|
||||
}
|
||||
}
|
||||
|
||||
currentNPCID = actualNPCID;
|
||||
|
||||
// Load sell service data
|
||||
if (!LoadSellService(actualNPCID))
|
||||
{
|
||||
Debug.LogError($"NPCShopUIManager: Failed to load sell service for NPC {actualNPCID}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Show panel
|
||||
npcShopMainPanel.SetActive(true);
|
||||
|
||||
// Create tabs
|
||||
CreateTabs();
|
||||
|
||||
// Select first tab
|
||||
if (tabPageMapping.Count > 0)
|
||||
{
|
||||
OnTabSelected(0);
|
||||
}
|
||||
RefreshShopDisplay();
|
||||
}
|
||||
|
||||
bool LoadSellService(uint npcID)
|
||||
{
|
||||
try
|
||||
{
|
||||
var elementDataMan = ElementDataManProvider.GetElementDataMan();
|
||||
if (elementDataMan == null)
|
||||
return false;
|
||||
|
||||
// Get NPC_ESSENCE
|
||||
DATA_TYPE dataType = DATA_TYPE.DT_INVALID;
|
||||
object npcData = elementDataMan.get_data_ptr(npcID, ID_SPACE.ID_SPACE_ESSENCE, ref dataType);
|
||||
|
||||
if (dataType != DATA_TYPE.DT_NPC_ESSENCE || npcData == null)
|
||||
return false;
|
||||
|
||||
NPC_ESSENCE npcEssence = (NPC_ESSENCE)npcData;
|
||||
uint sellServiceID = npcEssence.id_sell_service;
|
||||
|
||||
if (sellServiceID == 0)
|
||||
return false;
|
||||
|
||||
// Get NPC_SELL_SERVICE
|
||||
DATA_TYPE serviceDataType = DATA_TYPE.DT_INVALID;
|
||||
object serviceData = elementDataMan.get_data_ptr(sellServiceID, ID_SPACE.ID_SPACE_ESSENCE, ref serviceDataType);
|
||||
|
||||
if (serviceDataType != DATA_TYPE.DT_NPC_SELL_SERVICE || serviceData == null)
|
||||
return false;
|
||||
|
||||
cachedSellService = (NPC_SELL_SERVICE)serviceData;
|
||||
return true;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogError($"NPCShopUIManager: Exception loading sell service: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void CreateTabs()
|
||||
{
|
||||
ClearTabs();
|
||||
|
||||
if (currentNPCID == 0 || tabButtonContainer == null || tabButtonPrefab == null)
|
||||
return;
|
||||
|
||||
if (!cachedSellService.HasValue || cachedSellService.Value.pages == null)
|
||||
return;
|
||||
|
||||
var sellService = cachedSellService.Value;
|
||||
tabPageMapping.Clear();
|
||||
|
||||
// Create tabs for each page that has items
|
||||
for (int pageIndex = 0; pageIndex < sellService.pages.Length; pageIndex++)
|
||||
{
|
||||
var page = sellService.pages[pageIndex];
|
||||
|
||||
// Check if page has any items
|
||||
bool hasItems = false;
|
||||
if (page.goods != null)
|
||||
{
|
||||
foreach (var good in page.goods)
|
||||
{
|
||||
if (good.id != 0)
|
||||
{
|
||||
hasItems = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasItems)
|
||||
continue;
|
||||
|
||||
tabPageMapping.Add(pageIndex);
|
||||
|
||||
// Get page title
|
||||
string pageTitle = ByteToStringUtils.UshortArrayToUnicodeString(page.page_title);
|
||||
if (string.IsNullOrEmpty(pageTitle))
|
||||
{
|
||||
pageTitle = $"Page {pageIndex + 1}";
|
||||
}
|
||||
|
||||
// Create tab button
|
||||
GameObject tabButtonObj = Instantiate(tabButtonPrefab, tabButtonContainer);
|
||||
tabButtonObj.name = $"Tab_Page_{pageIndex}";
|
||||
tabButtonObj.SetActive(true);
|
||||
|
||||
// Get button component
|
||||
Button tabButton = tabButtonObj.GetComponent<Button>();
|
||||
if (tabButton == null)
|
||||
tabButton = tabButtonObj.GetComponentInChildren<Button>();
|
||||
|
||||
// Get text component
|
||||
TextMeshProUGUI tabText = tabButtonObj.GetComponent<TextMeshProUGUI>();
|
||||
if (tabText == null)
|
||||
{
|
||||
Transform textTransform = tabButtonObj.transform.Find(tabButtonTextComponentName);
|
||||
if (textTransform != null)
|
||||
tabText = textTransform.GetComponent<TextMeshProUGUI>();
|
||||
else
|
||||
tabText = tabButtonObj.GetComponentInChildren<TextMeshProUGUI>();
|
||||
}
|
||||
|
||||
if (tabText != null)
|
||||
tabText.text = pageTitle;
|
||||
|
||||
// Setup click listener
|
||||
if (tabButton != null)
|
||||
{
|
||||
int tabIndex = tabPageMapping.Count - 1; // Current tab index
|
||||
tabButton.onClick.AddListener(() => OnTabSelected(tabIndex));
|
||||
}
|
||||
|
||||
currentTabButtons.Add(tabButtonObj);
|
||||
}
|
||||
}
|
||||
|
||||
void ClearTabs()
|
||||
{
|
||||
foreach (GameObject tabButton in currentTabButtons)
|
||||
{
|
||||
if (tabButton != null)
|
||||
Destroy(tabButton);
|
||||
}
|
||||
currentTabButtons.Clear();
|
||||
tabPageMapping.Clear();
|
||||
}
|
||||
|
||||
void OnTabSelected(int tabIndex)
|
||||
{
|
||||
if (tabIndex == currentTabIndex || tabIndex < 0 || tabIndex >= tabPageMapping.Count)
|
||||
return;
|
||||
|
||||
currentTabIndex = tabIndex;
|
||||
RefreshShopDisplay();
|
||||
|
||||
// Update tab button states
|
||||
for (int i = 0; i < currentTabButtons.Count; i++)
|
||||
{
|
||||
if (currentTabButtons[i] != null)
|
||||
{
|
||||
Button tabButton = currentTabButtons[i].GetComponent<Button>();
|
||||
if (tabButton == null)
|
||||
tabButton = currentTabButtons[i].GetComponentInChildren<Button>();
|
||||
|
||||
if (tabButton != null)
|
||||
tabButton.interactable = (i != tabIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RefreshShopDisplay()
|
||||
{
|
||||
// Clear existing items
|
||||
ClearItems();
|
||||
|
||||
if (itemContainer == null || itemPanelPrefab == null)
|
||||
return;
|
||||
|
||||
if (currentTabIndex < 0 || currentTabIndex >= tabPageMapping.Count)
|
||||
return;
|
||||
|
||||
if (!cachedSellService.HasValue)
|
||||
return;
|
||||
|
||||
int pageIndex = tabPageMapping[currentTabIndex];
|
||||
var sellService = cachedSellService.Value;
|
||||
|
||||
if (pageIndex >= sellService.pages.Length)
|
||||
return;
|
||||
|
||||
var page = sellService.pages[pageIndex];
|
||||
|
||||
// Create item panels
|
||||
if (page.goods != null)
|
||||
{
|
||||
var elementDataMan = ElementDataManProvider.GetElementDataMan();
|
||||
if (elementDataMan == null)
|
||||
return;
|
||||
|
||||
foreach (var good in page.goods)
|
||||
{
|
||||
if (good.id == 0)
|
||||
continue;
|
||||
|
||||
// Get item essence
|
||||
DATA_TYPE itemDataType = DATA_TYPE.DT_INVALID;
|
||||
object itemData = elementDataMan.get_data_ptr(good.id, ID_SPACE.ID_SPACE_ESSENCE, ref itemDataType);
|
||||
|
||||
if (itemData == null)
|
||||
continue;
|
||||
|
||||
// Create GShopItem
|
||||
GShopItem shopItem = CreateShopItemFromGood(good, itemData, itemDataType);
|
||||
|
||||
// Create panel
|
||||
CreateItemPanel(shopItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GShopItem CreateShopItemFromGood(NPC_SELL_SERVICE.SellGood good, object itemData, DATA_TYPE itemDataType)
|
||||
{
|
||||
GShopItem shopItem = new GShopItem();
|
||||
shopItem.id = good.id;
|
||||
shopItem.num = 1;
|
||||
|
||||
// Initialize buy array with at least one option
|
||||
shopItem.buy = new GShopBuyOption[4]; // GShopItem supports up to 4 buy options
|
||||
|
||||
// Get item name and price based on type
|
||||
string itemName = "Unknown";
|
||||
int shopPrice = 0;
|
||||
|
||||
switch (itemDataType)
|
||||
{
|
||||
case DATA_TYPE.DT_WEAPON_ESSENCE:
|
||||
var weaponEssence = (WEAPON_ESSENCE)itemData;
|
||||
itemName = weaponEssence.Name;
|
||||
shopPrice = weaponEssence.shop_price;
|
||||
break;
|
||||
case DATA_TYPE.DT_ARMOR_ESSENCE:
|
||||
var armorEssence = (ARMOR_ESSENCE)itemData;
|
||||
itemName = armorEssence.Name;
|
||||
shopPrice = armorEssence.shop_price;
|
||||
break;
|
||||
case DATA_TYPE.DT_MEDICINE_ESSENCE:
|
||||
var medicineEssence = (MEDICINE_ESSENCE)itemData;
|
||||
itemName = ByteToStringUtils.UshortArrayToUnicodeString(medicineEssence.name);
|
||||
shopPrice = medicineEssence.shop_price;
|
||||
break;
|
||||
case DATA_TYPE.DT_DECORATION_ESSENCE:
|
||||
var decorationEssence = (DECORATION_ESSENCE)itemData;
|
||||
itemName = ByteToStringUtils.UshortArrayToUnicodeString(decorationEssence.name);
|
||||
shopPrice = decorationEssence.shop_price;
|
||||
break;
|
||||
case DATA_TYPE.DT_STONE_ESSENCE:
|
||||
var stoneEssence = (STONE_ESSENCE)itemData;
|
||||
itemName = ByteToStringUtils.UshortArrayToUnicodeString(stoneEssence.name);
|
||||
shopPrice = stoneEssence.shop_price;
|
||||
break;
|
||||
case DATA_TYPE.DT_MATERIAL_ESSENCE:
|
||||
var materialEssence = (MATERIAL_ESSENCE)itemData;
|
||||
itemName = ByteToStringUtils.UshortArrayToUnicodeString(materialEssence.name);
|
||||
shopPrice = materialEssence.shop_price;
|
||||
break;
|
||||
default:
|
||||
itemName = $"Item_{good.id}";
|
||||
break;
|
||||
}
|
||||
|
||||
shopItem.name = itemName;
|
||||
|
||||
// Set price from contribution cost or shop price
|
||||
if (good.contrib_cost > 0)
|
||||
{
|
||||
shopItem.buy[0].price = (uint)good.contrib_cost;
|
||||
shopItem.buy[0].type = 0; // 0 = permanent
|
||||
}
|
||||
else if (shopPrice > 0)
|
||||
{
|
||||
shopItem.buy[0].price = (uint)shopPrice;
|
||||
shopItem.buy[0].type = 0; // 0 = permanent
|
||||
}
|
||||
else
|
||||
{
|
||||
// Initialize with invalid type if no price
|
||||
shopItem.buy[0].type = -1; // -1 = invalid
|
||||
shopItem.buy[0].price = 0;
|
||||
}
|
||||
|
||||
return shopItem;
|
||||
}
|
||||
|
||||
void CreateItemPanel(GShopItem item)
|
||||
{
|
||||
if (itemPanelPrefab == null || itemContainer == null)
|
||||
return;
|
||||
|
||||
GameObject itemPanel = Instantiate(itemPanelPrefab, itemContainer);
|
||||
itemPanel.SetActive(true);
|
||||
|
||||
NPCShopItemPanel itemPanelScript = itemPanel.GetComponent<NPCShopItemPanel>();
|
||||
if (itemPanelScript == null)
|
||||
itemPanelScript = itemPanel.GetComponentInChildren<NPCShopItemPanel>();
|
||||
|
||||
if (itemPanelScript != null)
|
||||
{
|
||||
itemPanelScript.SetupItem(item, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"[NPCShopUIManager] NPCShopItemPanel component not found on prefab! Prefab name: {itemPanelPrefab.name}");
|
||||
}
|
||||
|
||||
currentItemPanels.Add(itemPanel);
|
||||
}
|
||||
|
||||
void ClearItems()
|
||||
{
|
||||
foreach (GameObject panel in currentItemPanels)
|
||||
{
|
||||
if (panel != null)
|
||||
Destroy(panel);
|
||||
}
|
||||
currentItemPanels.Clear();
|
||||
}
|
||||
|
||||
public void ShowItemDetail(GShopItem item)
|
||||
{
|
||||
if (item.id == 0)
|
||||
return;
|
||||
|
||||
if (npcShopDetailPanel == null)
|
||||
{
|
||||
Debug.LogWarning("[NPCShopUIManager] npcShopDetailPanel is not assigned!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure detail panel script is available
|
||||
if (detailPanelScript == null)
|
||||
{
|
||||
detailPanelScript = npcShopDetailPanel.GetComponent<NPCShopDetailPanel>();
|
||||
if (detailPanelScript == null)
|
||||
detailPanelScript = npcShopDetailPanel.GetComponentInChildren<NPCShopDetailPanel>();
|
||||
}
|
||||
|
||||
if (detailPanelScript != null)
|
||||
{
|
||||
npcShopDetailPanel.SetActive(true);
|
||||
detailPanelScript.SetupDetailPanel(item, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("[NPCShopUIManager] NPCShopDetailPanel component not found on npcShopDetailPanel GameObject!");
|
||||
}
|
||||
}
|
||||
|
||||
public void CloseDetailPanel()
|
||||
{
|
||||
if (npcShopDetailPanel != null)
|
||||
npcShopDetailPanel.SetActive(false);
|
||||
}
|
||||
|
||||
public void CloseShop()
|
||||
{
|
||||
if (npcShopMainPanel != null)
|
||||
npcShopMainPanel.SetActive(false);
|
||||
|
||||
// Close detail panel when closing shop
|
||||
CloseDetailPanel();
|
||||
|
||||
ClearTabs();
|
||||
ClearItems();
|
||||
cachedSellService = null;
|
||||
currentNPCID = 0;
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
ClearTabs();
|
||||
ClearItems();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 009ffcf832a02b545a5cce71d5e32877
|
||||
@@ -44,7 +44,7 @@ namespace BrewMonster.PerfectWorld.Scripts.Vfx
|
||||
{
|
||||
if(!string.IsNullOrEmpty(_vfxPath))
|
||||
{
|
||||
AddressableManager.Instance.UnloadAsset(_vfxPath);
|
||||
AddressableManager.Instance.ReleaseAsset(_vfxPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eaeb778b6aab3d74299373b3a96b72c4
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+504
-2547
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,259 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &532136160345846687
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 7369895020851334411}
|
||||
- component: {fileID: 214429650323778303}
|
||||
- component: {fileID: 1383770310334783977}
|
||||
- component: {fileID: 436672111126768001}
|
||||
m_Layer: 0
|
||||
m_Name: Tab_Button
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &7369895020851334411
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 532136160345846687}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 5379974387884416915}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: -10.5289, y: 0}
|
||||
m_SizeDelta: {x: 78.9422, y: 40}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &214429650323778303
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 532136160345846687}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &1383770310334783977
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 532136160345846687}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 21300000, guid: 8f24853d9cfea43389e8fb3101ffaae1, type: 3}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!114 &436672111126768001
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 532136160345846687}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Navigation:
|
||||
m_Mode: 3
|
||||
m_WrapAround: 0
|
||||
m_SelectOnUp: {fileID: 0}
|
||||
m_SelectOnDown: {fileID: 0}
|
||||
m_SelectOnLeft: {fileID: 0}
|
||||
m_SelectOnRight: {fileID: 0}
|
||||
m_Transition: 1
|
||||
m_Colors:
|
||||
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
|
||||
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
|
||||
m_ColorMultiplier: 1
|
||||
m_FadeDuration: 0.1
|
||||
m_SpriteState:
|
||||
m_HighlightedSprite: {fileID: 0}
|
||||
m_PressedSprite: {fileID: 0}
|
||||
m_SelectedSprite: {fileID: 0}
|
||||
m_DisabledSprite: {fileID: 0}
|
||||
m_AnimationTriggers:
|
||||
m_NormalTrigger: Normal
|
||||
m_HighlightedTrigger: Highlighted
|
||||
m_PressedTrigger: Pressed
|
||||
m_SelectedTrigger: Selected
|
||||
m_DisabledTrigger: Disabled
|
||||
m_Interactable: 1
|
||||
m_TargetGraphic: {fileID: 1383770310334783977}
|
||||
m_OnClick:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
--- !u!1 &5614972339824250559
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 5379974387884416915}
|
||||
- component: {fileID: 8079830739419442855}
|
||||
- component: {fileID: 7398759648826456359}
|
||||
m_Layer: 0
|
||||
m_Name: Text (TMP)
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &5379974387884416915
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5614972339824250559}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 7369895020851334411}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 1.3087}
|
||||
m_SizeDelta: {x: 0, y: -2.6175}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &8079830739419442855
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5614972339824250559}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &7398759648826456359
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5614972339824250559}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text: Button
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2}
|
||||
m_sharedMaterial: {fileID: 9092487103257209053, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2}
|
||||
m_fontSharedMaterials: []
|
||||
m_fontMaterial: {fileID: 0}
|
||||
m_fontMaterials: []
|
||||
m_fontColor32:
|
||||
serializedVersion: 2
|
||||
rgba: 4281348144
|
||||
m_fontColor: {r: 0.18867922, g: 0.18867922, b: 0.18867922, a: 1}
|
||||
m_enableVertexGradient: 0
|
||||
m_colorMode: 3
|
||||
m_fontColorGradient:
|
||||
topLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
topRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_fontColorGradientPreset: {fileID: 0}
|
||||
m_spriteAsset: {fileID: 0}
|
||||
m_tintAllSprites: 0
|
||||
m_StyleSheet: {fileID: 0}
|
||||
m_TextStyleHashCode: -1183493901
|
||||
m_overrideHtmlColors: 0
|
||||
m_faceColor:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontSize: 22
|
||||
m_fontSizeBase: 22
|
||||
m_fontWeight: 400
|
||||
m_enableAutoSizing: 0
|
||||
m_fontSizeMin: 18
|
||||
m_fontSizeMax: 72
|
||||
m_fontStyle: 0
|
||||
m_HorizontalAlignment: 2
|
||||
m_VerticalAlignment: 512
|
||||
m_textAlignment: 65535
|
||||
m_characterSpacing: 0
|
||||
m_wordSpacing: 0
|
||||
m_lineSpacing: 0
|
||||
m_lineSpacingMax: 0
|
||||
m_paragraphSpacing: 0
|
||||
m_charWidthMaxAdj: 0
|
||||
m_TextWrappingMode: 1
|
||||
m_wordWrappingRatios: 0.4
|
||||
m_overflowMode: 0
|
||||
m_linkedTextComponent: {fileID: 0}
|
||||
parentLinkedComponent: {fileID: 0}
|
||||
m_enableKerning: 0
|
||||
m_ActiveFontFeatures: 6e72656b
|
||||
m_enableExtraPadding: 0
|
||||
checkPaddingRequired: 0
|
||||
m_isRichText: 1
|
||||
m_EmojiFallbackSupport: 1
|
||||
m_parseCtrlCharacters: 1
|
||||
m_isOrthographic: 1
|
||||
m_isCullingEnabled: 0
|
||||
m_horizontalMapping: 0
|
||||
m_verticalMapping: 0
|
||||
m_uvLineOffset: 0
|
||||
m_geometrySortingOrder: 0
|
||||
m_IsTextObjectScaleStatic: 0
|
||||
m_VertexBufferAutoSizeReduction: 0
|
||||
m_useMaxVisibleDescender: 1
|
||||
m_pageToDisplay: 1
|
||||
m_margin: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_isUsingLegacyAnimationComponent: 0
|
||||
m_isVolumetricText: 0
|
||||
m_hasFontAssetChanged: 0
|
||||
m_baseMaterial: {fileID: 0}
|
||||
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 548ae6ac061bc9648b093c9f9d203615
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,174 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &3478571236783653060
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 7551350917878394023}
|
||||
- component: {fileID: 1847471482995505170}
|
||||
- component: {fileID: 2586856635111866746}
|
||||
m_Layer: 0
|
||||
m_Name: item
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &7551350917878394023
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3478571236783653060}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 5438773728966160586}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 1}
|
||||
m_AnchorMax: {x: 0, y: 1}
|
||||
m_AnchoredPosition: {x: 42, y: -42}
|
||||
m_SizeDelta: {x: 84, y: 84}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &1847471482995505170
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3478571236783653060}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 14c449229b5a12a42892e4fa263d6cbd, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
itemNameText: {fileID: 0}
|
||||
itemPriceText: {fileID: 0}
|
||||
itemIconImage: {fileID: 7716303670266317094}
|
||||
--- !u!114 &2586856635111866746
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3478571236783653060}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Navigation:
|
||||
m_Mode: 3
|
||||
m_WrapAround: 0
|
||||
m_SelectOnUp: {fileID: 0}
|
||||
m_SelectOnDown: {fileID: 0}
|
||||
m_SelectOnLeft: {fileID: 0}
|
||||
m_SelectOnRight: {fileID: 0}
|
||||
m_Transition: 1
|
||||
m_Colors:
|
||||
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
|
||||
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
|
||||
m_ColorMultiplier: 1
|
||||
m_FadeDuration: 0.1
|
||||
m_SpriteState:
|
||||
m_HighlightedSprite: {fileID: 0}
|
||||
m_PressedSprite: {fileID: 0}
|
||||
m_SelectedSprite: {fileID: 0}
|
||||
m_DisabledSprite: {fileID: 0}
|
||||
m_AnimationTriggers:
|
||||
m_NormalTrigger: Normal
|
||||
m_HighlightedTrigger: Highlighted
|
||||
m_PressedTrigger: Pressed
|
||||
m_SelectedTrigger: Selected
|
||||
m_DisabledTrigger: Disabled
|
||||
m_Interactable: 1
|
||||
m_TargetGraphic: {fileID: 7716303670266317094}
|
||||
m_OnClick:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
--- !u!1 &5714852699199745934
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 5438773728966160586}
|
||||
- component: {fileID: 7941037763135429874}
|
||||
- component: {fileID: 7716303670266317094}
|
||||
m_Layer: 5
|
||||
m_Name: icon
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &5438773728966160586
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5714852699199745934}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 7551350917878394023}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 1}
|
||||
m_AnchorMax: {x: 0, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: -42}
|
||||
m_SizeDelta: {x: 84, y: 84}
|
||||
m_Pivot: {x: 0, y: 0.5}
|
||||
--- !u!222 &7941037763135429874
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5714852699199745934}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &7716303670266317094
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5714852699199745934}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 21300000, guid: 8b997e886fff00540b9c069a9dff12af, type: 3}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c44c4ffd9028b8439ed778ff0dd564b
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -35,20 +35,20 @@ public partial class CECGameRun
|
||||
// _playerPrefab = _gameRunConfig.PlayerPrefab;
|
||||
// _monsterPrefab = _gameRunConfig.MonsterPrefab;
|
||||
// _npcServerPrefab = _gameRunConfig.NpcServerPrefab;
|
||||
// _testVfxPrefab = _gameRunConfig.TestVfxPrefab;
|
||||
LoadPrefabs();
|
||||
|
||||
// LoadPrefabs();
|
||||
EC_ManMessage.RegisterHandler(this);
|
||||
|
||||
AddressableManager.Instance.OnDispose += Dispose;
|
||||
}
|
||||
|
||||
public static void Dispose()
|
||||
private static void Dispose()
|
||||
{
|
||||
instance = null;
|
||||
AddressableManager.Instance.UnloadAsset(AddressResourceConfig.PlayerPrefab);
|
||||
AddressableManager.Instance.UnloadAsset(AddressResourceConfig.MonsterPrefab);
|
||||
AddressableManager.Instance.UnloadAsset(AddressResourceConfig.NpcServerPrefab);
|
||||
AddressableManager.Instance.UnloadAsset(AddressResourceConfig.TestVfxPrefab);
|
||||
AddressableManager.Instance.ReleaseAsset(AddressResourceConfig.PlayerPrefab);
|
||||
AddressableManager.Instance.ReleaseAsset(AddressResourceConfig.MonsterPrefab);
|
||||
AddressableManager.Instance.ReleaseAsset(AddressResourceConfig.NpcServerPrefab);
|
||||
}
|
||||
|
||||
private async void LoadPrefabs()
|
||||
|
||||
@@ -54,35 +54,74 @@ namespace BrewMonster
|
||||
|
||||
protected bool LoadANSIStrings(string resourceName)
|
||||
{
|
||||
TextAsset textAsset = Resources.Load<TextAsset>(resourceName);
|
||||
if (textAsset == null)
|
||||
try
|
||||
{
|
||||
Debug.LogError($"[CECStringTab] Resource not found: {resourceName}");
|
||||
// If a real file path is provided (e.g. StreamingAssets), read directly from disk.
|
||||
// 如果提供的是实际文件路径(例如 StreamingAssets),则直接从磁盘读取。
|
||||
if (File.Exists(resourceName))
|
||||
{
|
||||
// ANSI tables are in CP936 in original PW; keep using CP936 decoder.
|
||||
// 原版完美世界的ANSI表是CP936编码,这里保持一致。
|
||||
byte[] bytes = File.ReadAllBytes(resourceName);
|
||||
string content = ByteToStringUtils.ByteArrayToCP936String(bytes);
|
||||
using var srFile = new StringReader(content);
|
||||
return ParseIntoDict(srFile, isWide: false);
|
||||
}
|
||||
|
||||
// Fallback to Resources (old behaviour).
|
||||
// 回退到 Resources 加载(旧行为)。
|
||||
TextAsset textAsset = Resources.Load<TextAsset>(resourceName);
|
||||
if (textAsset == null)
|
||||
{
|
||||
Debug.LogError($"[CECStringTab] Resource not found: {resourceName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
string resContent = ByteToStringUtils.ByteArrayToCP936String(textAsset.bytes);
|
||||
using var srRes = new StringReader(resContent);
|
||||
return ParseIntoDict(srRes, isWide: false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[CECStringTab] LoadANSIStrings failed for '{resourceName}': {e}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Giải mã bytes -> string (ANSI: dùng Encoding.Default)
|
||||
string content = ByteToStringUtils.ByteArrayToCP936String(textAsset.bytes);
|
||||
using var sr = new StringReader(content);
|
||||
return ParseIntoDict(sr, isWide: false);
|
||||
}
|
||||
|
||||
protected bool LoadWideStrings(string resourceName)
|
||||
{
|
||||
TextAsset textAsset = Resources.Load<TextAsset>(resourceName);
|
||||
if (textAsset == null)
|
||||
try
|
||||
{
|
||||
Debug.LogError($"[CECStringTab] Resource not found: {resourceName}");
|
||||
// Support absolute / relative filesystem paths (e.g. StreamingAssets/configs/*.txt)
|
||||
// 支持文件系统路径(例如 StreamingAssets/configs/*.txt)
|
||||
if (File.Exists(resourceName))
|
||||
{
|
||||
// String tables we ship in StreamingAssets are saved as UTF-8.
|
||||
// 我们放在 StreamingAssets 里的字符串表保存为 UTF-8。
|
||||
string content = File.ReadAllText(resourceName, Encoding.UTF8);
|
||||
using var srFile = new StringReader(content);
|
||||
return ParseIntoDict(srFile, isWide: true);
|
||||
}
|
||||
|
||||
// Fallback to Resources-based loading (old behaviour)
|
||||
// 回退到基于 Resources 的加载(旧行为)
|
||||
TextAsset textAsset = Resources.Load<TextAsset>(resourceName);
|
||||
if (textAsset == null)
|
||||
{
|
||||
Debug.LogError($"[CECStringTab] Resource not found: {resourceName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unity TextAsset.text is already UTF-8 decoded.
|
||||
string resContent = textAsset.text;
|
||||
using var srRes = new StringReader(resContent);
|
||||
return ParseIntoDict(srRes, isWide: true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[CECStringTab] LoadWideStrings failed for '{resourceName}': {e}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unity TextAsset mặc định đã decode text UTF8 -> textAsset.text
|
||||
// nhưng để chắc chắn BOM/Unicode thì đọc từ bytes
|
||||
string content;
|
||||
content = textAsset.text;
|
||||
|
||||
using var sr = new StringReader(content);
|
||||
return ParseIntoDict(sr, isWide: true);
|
||||
}
|
||||
|
||||
private static Encoding DetectEncoding(byte[] bom)
|
||||
@@ -183,6 +222,18 @@ namespace BrewMonster
|
||||
|
||||
private void PutString(int id, string value, bool isWide)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return;
|
||||
|
||||
// Many PW string tables wrap the payload in double quotes, e.g.:
|
||||
// 12345 "^ffcb4aSome text\rMore text"
|
||||
// Strip a single leading/trailing quote pair to avoid showing raw quotes in UI.
|
||||
// 许多字符串表会用双引号包裹内容,这里去掉首尾各一个引号以避免在UI中显示多余的引号。
|
||||
if (value.Length >= 2 && value[0] == '"' && value[value.Length - 1] == '"')
|
||||
{
|
||||
value = value.Substring(1, value.Length - 2);
|
||||
}
|
||||
|
||||
if (isWide) m_WStrTab[id] = value;
|
||||
else m_AStrTab[id] = value;
|
||||
}
|
||||
|
||||
@@ -130,4 +130,12 @@ public class CECUIManager : MonoSingleton<CECUIManager>
|
||||
}
|
||||
return gameUI;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current target NPC ID (same as stored from NPCINFO event)
|
||||
/// </summary>
|
||||
public int GetCurrentTargetNPCID()
|
||||
{
|
||||
return currentTargetNPCID;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user