加入很多新内容,这波一块交了
This commit is contained in:
@@ -0,0 +1,296 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &2099462153568767799
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 665396819362690529}
|
||||
- component: {fileID: 3192217589302937993}
|
||||
- component: {fileID: 5556623676502886536}
|
||||
m_Layer: 5
|
||||
m_Name: levelID
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &665396819362690529
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2099462153568767799}
|
||||
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: 9152270112823009895}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: -107.33, y: 0}
|
||||
m_SizeDelta: {x: 25.792, y: 30}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &3192217589302937993
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2099462153568767799}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &5556623676502886536
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2099462153568767799}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, 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_FontData:
|
||||
m_Font: {fileID: 12800000, guid: 8b8373b0af11dca46b89be60dbe469f8, type: 3}
|
||||
m_FontSize: 20
|
||||
m_FontStyle: 0
|
||||
m_BestFit: 0
|
||||
m_MinSize: 2
|
||||
m_MaxSize: 40
|
||||
m_Alignment: 4
|
||||
m_AlignByGeometry: 0
|
||||
m_RichText: 1
|
||||
m_HorizontalOverflow: 0
|
||||
m_VerticalOverflow: 0
|
||||
m_LineSpacing: 1
|
||||
m_Text: 10
|
||||
--- !u!1 &2343926165364488871
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 9152270112823009895}
|
||||
- component: {fileID: 4801688296739999581}
|
||||
- component: {fileID: 4501378674247754304}
|
||||
m_Layer: 5
|
||||
m_Name: btm
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &9152270112823009895
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2343926165364488871}
|
||||
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: 2290428063649654242}
|
||||
- {fileID: 665396819362690529}
|
||||
m_Father: {fileID: 2460305581810160109}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 250, y: 30}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &4801688296739999581
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2343926165364488871}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &4501378674247754304
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2343926165364488871}
|
||||
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: -52179173452712981, guid: be120bf969ae2c84fa997ecca6dd28e6, type: 3}
|
||||
m_Type: 1
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 2
|
||||
--- !u!1 &3339470438795980614
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2290428063649654242}
|
||||
- component: {fileID: 4129409205783608839}
|
||||
- component: {fileID: 534678815217464394}
|
||||
m_Layer: 5
|
||||
m_Name: skillName
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &2290428063649654242
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3339470438795980614}
|
||||
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: 9152270112823009895}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 1.4, y: 0}
|
||||
m_SizeDelta: {x: 187.733, y: 30}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &4129409205783608839
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3339470438795980614}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &534678815217464394
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3339470438795980614}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, 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_FontData:
|
||||
m_Font: {fileID: 12800000, guid: dfa10de3e2846524089b5073175cd151, type: 3}
|
||||
m_FontSize: 20
|
||||
m_FontStyle: 0
|
||||
m_BestFit: 0
|
||||
m_MinSize: 2
|
||||
m_MaxSize: 40
|
||||
m_Alignment: 3
|
||||
m_AlignByGeometry: 0
|
||||
m_RichText: 1
|
||||
m_HorizontalOverflow: 0
|
||||
m_VerticalOverflow: 0
|
||||
m_LineSpacing: 1
|
||||
m_Text: "\u5076\u50CF\u5927\u5E08\uFF1A\u8D5B\u540E\u611F\u609F"
|
||||
--- !u!1 &4419549278339915466
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2460305581810160109}
|
||||
- component: {fileID: 7517620086478948793}
|
||||
m_Layer: 5
|
||||
m_Name: levelRewardPrefab
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &2460305581810160109
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4419549278339915466}
|
||||
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: 9152270112823009895}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 450, y: 30}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &7517620086478948793
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4419549278339915466}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 427d68583c7d9ab4588888b68dc7a8d2, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
enableSprite: {fileID: -52179173452712981, guid: be120bf969ae2c84fa997ecca6dd28e6, type: 3}
|
||||
availableSprite: {fileID: 2561534620446350566, guid: 23a48ca1e983a4c47971fcf7eab26b89, type: 3}
|
||||
lockedSprite: {fileID: -1936355827857895663, guid: fa360ec6245a3dd43bfbae1ac1093a6e, type: 3}
|
||||
unknownSprite: {fileID: 21300000, guid: 931f6455943555649a61ed7f1c76cbf6, type: 3}
|
||||
preSelectSprite: {fileID: 7919194353434859991, guid: b35fcaa46fa9f3e49b71c103f69df698, type: 3}
|
||||
levelName: {fileID: 5556623676502886536}
|
||||
statusImg: {fileID: 4501378674247754304}
|
||||
thisSkillName: {fileID: 534678815217464394}
|
||||
descriptionPrefab: {fileID: 2219571326304505587, guid: bde76837f913f3943836d4f83292618b, type: 3}
|
||||
descriptionOffsetX: 150
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 26d681beab8631442ac57714a80f1f37
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,184 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.EventSystems;
|
||||
using System.Collections;
|
||||
|
||||
public class lrPrefab : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler
|
||||
{
|
||||
[Header("sprite")]
|
||||
public Sprite enableSprite;
|
||||
public Sprite availableSprite;
|
||||
public Sprite lockedSprite;
|
||||
public Sprite unknownSprite;
|
||||
public Sprite preSelectSprite;
|
||||
|
||||
[Header("uis")]
|
||||
public Text levelName;
|
||||
public Image statusImg;
|
||||
public Text thisSkillName;
|
||||
|
||||
[Header("desOBJ")]
|
||||
public GameObject descriptionPrefab;
|
||||
public float descriptionOffsetX = 80f;
|
||||
|
||||
private uLevel_skills owner;
|
||||
private userLevel_skills_SO ownerSo;
|
||||
private userLevel_skills_SO.UserLevelSkillEntry boundEntry;
|
||||
private int boundIndex = -1;
|
||||
private bool isUnlocked = false;
|
||||
private Coroutine hoverCoroutine;
|
||||
private GameObject spawnedDescription;
|
||||
|
||||
public void Bind(uLevel_skills ownerController, userLevel_skills_SO sourceSo, int index, userLevel_skills_SO.UserLevelSkillEntry entry, bool unlocked)
|
||||
{
|
||||
owner = ownerController;
|
||||
ownerSo = sourceSo;
|
||||
boundIndex = index;
|
||||
boundEntry = entry;
|
||||
isUnlocked = unlocked;
|
||||
|
||||
if (levelName != null)
|
||||
levelName.text = entry != null ? entry.userLevel.ToString() : string.Empty;
|
||||
|
||||
RefreshVisualState();
|
||||
}
|
||||
|
||||
public void RefreshVisualState()
|
||||
{
|
||||
if (owner != null && boundEntry != null)
|
||||
isUnlocked = owner.IsSkillUnlocked(boundEntry);
|
||||
|
||||
if (boundEntry == null)
|
||||
return;
|
||||
|
||||
bool revealLockedSkills = PlayerSkillService.IsSkillEnabled(7);
|
||||
bool shouldHideLockedSkillInfo = !isUnlocked && !revealLockedSkills;
|
||||
|
||||
if (thisSkillName != null)
|
||||
{
|
||||
thisSkillName.text = shouldHideLockedSkillInfo
|
||||
? "?????"
|
||||
: (boundEntry.skillName ?? string.Empty);
|
||||
}
|
||||
|
||||
if (statusImg == null)
|
||||
return;
|
||||
|
||||
if (owner != null && owner.IsSkillPreselected(boundIndex))
|
||||
statusImg.sprite = preSelectSprite != null ? preSelectSprite : availableSprite;
|
||||
else if (boundEntry.isEnabled)
|
||||
statusImg.sprite = enableSprite;
|
||||
else if (isUnlocked)
|
||||
statusImg.sprite = availableSprite;
|
||||
else if (shouldHideLockedSkillInfo)
|
||||
statusImg.sprite = unknownSprite != null ? unknownSprite : lockedSprite;
|
||||
else
|
||||
statusImg.sprite = lockedSprite;
|
||||
}
|
||||
|
||||
public void OnPointerEnter(PointerEventData eventData)
|
||||
{
|
||||
if (boundEntry == null || descriptionPrefab == null)
|
||||
return;
|
||||
|
||||
if (!isUnlocked && !PlayerSkillService.IsSkillEnabled(7))
|
||||
return;
|
||||
|
||||
if (hoverCoroutine != null)
|
||||
StopCoroutine(hoverCoroutine);
|
||||
|
||||
hoverCoroutine = StartCoroutine(ShowDescriptionAfterDelay());
|
||||
}
|
||||
|
||||
public void OnPointerExit(PointerEventData eventData)
|
||||
{
|
||||
if (hoverCoroutine != null)
|
||||
{
|
||||
StopCoroutine(hoverCoroutine);
|
||||
hoverCoroutine = null;
|
||||
}
|
||||
|
||||
HideDescription();
|
||||
}
|
||||
|
||||
public void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
if (owner == null || boundIndex < 0)
|
||||
return;
|
||||
|
||||
owner.ToggleSkill(boundIndex);
|
||||
}
|
||||
|
||||
private IEnumerator ShowDescriptionAfterDelay()
|
||||
{
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
hoverCoroutine = null;
|
||||
ShowDescription();
|
||||
}
|
||||
|
||||
private void ShowDescription()
|
||||
{
|
||||
if (spawnedDescription != null || descriptionPrefab == null || boundEntry == null)
|
||||
return;
|
||||
|
||||
RectTransform root = ResolveDescriptionRoot();
|
||||
if (root == null)
|
||||
return;
|
||||
|
||||
spawnedDescription = Instantiate(descriptionPrefab, root);
|
||||
spawnedDescription.SetActive(true);
|
||||
spawnedDescription.transform.SetAsLastSibling();
|
||||
|
||||
esDesPrefab detail = spawnedDescription.GetComponent<esDesPrefab>();
|
||||
if (detail != null)
|
||||
detail.Bind(boundEntry.skillName, boundEntry.skillIcon, boundEntry.skillDescription, boundEntry.skillSummary);
|
||||
|
||||
RectTransform spawnedRect = spawnedDescription.GetComponent<RectTransform>();
|
||||
if (spawnedRect == null)
|
||||
return;
|
||||
|
||||
Canvas canvas = root.GetComponentInParent<Canvas>();
|
||||
Camera eventCamera = null;
|
||||
if (canvas != null && canvas.renderMode != RenderMode.ScreenSpaceOverlay)
|
||||
eventCamera = canvas.worldCamera;
|
||||
|
||||
Vector2 localPoint;
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(root, Input.mousePosition, eventCamera, out localPoint);
|
||||
localPoint.x += descriptionOffsetX;
|
||||
spawnedRect.anchoredPosition = localPoint;
|
||||
}
|
||||
|
||||
private RectTransform ResolveDescriptionRoot()
|
||||
{
|
||||
Canvas canvas = GetComponentInParent<Canvas>();
|
||||
if (canvas == null)
|
||||
return null;
|
||||
|
||||
Canvas rootCanvas = canvas.rootCanvas != null ? canvas.rootCanvas : canvas;
|
||||
return rootCanvas.transform as RectTransform;
|
||||
}
|
||||
|
||||
private void HideDescription()
|
||||
{
|
||||
if (spawnedDescription == null)
|
||||
return;
|
||||
|
||||
if (Application.isPlaying)
|
||||
Destroy(spawnedDescription);
|
||||
else
|
||||
DestroyImmediate(spawnedDescription);
|
||||
|
||||
spawnedDescription = null;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (hoverCoroutine != null)
|
||||
{
|
||||
StopCoroutine(hoverCoroutine);
|
||||
hoverCoroutine = null;
|
||||
}
|
||||
|
||||
HideDescription();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 427d68583c7d9ab4588888b68dc7a8d2
|
||||
@@ -0,0 +1,415 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class uLevel_skills : MonoBehaviour
|
||||
{
|
||||
public userLevel_skills_SO ulsSO;
|
||||
public Player_SO pso;
|
||||
|
||||
[Header("objs")]
|
||||
public GameObject uSkillBarPrefab;
|
||||
public Transform uSkillBarParent;
|
||||
|
||||
[Header("current selected skill details")]
|
||||
public Image btm_skillImage;
|
||||
public Image skillIcon_Profile;
|
||||
public Text skill_thisSkillName;
|
||||
public Text skill_thisSkillSummary;
|
||||
|
||||
[Header("change")]
|
||||
public Slider need_progress;
|
||||
public Text need_text;
|
||||
public Text need_howmuchCoins;
|
||||
public Button yesChangeButton;
|
||||
public Button noCancelButton;
|
||||
|
||||
private readonly System.Collections.Generic.List<lrPrefab> spawnedBars = new System.Collections.Generic.List<lrPrefab>();
|
||||
private bool hasStarted = false;
|
||||
private int pendingSkillIndex = -1;
|
||||
private Color defaultNeedCoinsColor = Color.white;
|
||||
private const int SkillSwitchCooldownMatches = 3;
|
||||
private const int SkillSwitchMinimumCost = 200;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
hasStarted = true;
|
||||
CacheUiDefaults();
|
||||
RegisterButtons();
|
||||
PlayerSkillService.RegisterSkillAsset(ulsSO);
|
||||
RefreshSkillBars();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
CacheUiDefaults();
|
||||
RegisterButtons();
|
||||
PlayerSkillService.RegisterSkillAsset(ulsSO);
|
||||
if (hasStarted)
|
||||
{
|
||||
RefreshSkillBars();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
UnregisterButtons();
|
||||
}
|
||||
|
||||
public void SetPlayerSo(Player_SO playerSo)
|
||||
{
|
||||
pso = playerSo;
|
||||
}
|
||||
|
||||
public void RefreshSkillBars()
|
||||
{
|
||||
EnsurePlayerSo();
|
||||
|
||||
if (uSkillBarParent == null || uSkillBarPrefab == null || ulsSO == null)
|
||||
return;
|
||||
|
||||
NormalizeSelectionState();
|
||||
ClearExistingBars();
|
||||
spawnedBars.Clear();
|
||||
|
||||
for (int i = 0; i < ulsSO.skills.Count; i++)
|
||||
{
|
||||
var entry = ulsSO.skills[i];
|
||||
if (entry == null)
|
||||
continue;
|
||||
|
||||
GameObject instance = Instantiate(uSkillBarPrefab, uSkillBarParent);
|
||||
instance.SetActive(true);
|
||||
|
||||
lrPrefab bar = instance.GetComponent<lrPrefab>();
|
||||
if (bar == null)
|
||||
continue;
|
||||
|
||||
bar.Bind(this, ulsSO, i, entry, IsSkillUnlocked(entry));
|
||||
spawnedBars.Add(bar);
|
||||
}
|
||||
|
||||
RefreshDetailPanel();
|
||||
RefreshChangeUi();
|
||||
}
|
||||
|
||||
public void ToggleSkill(int index)
|
||||
{
|
||||
if (ulsSO == null || index < 0 || index >= ulsSO.skills.Count)
|
||||
return;
|
||||
|
||||
var entry = ulsSO.skills[index];
|
||||
if (entry == null || !IsSkillUnlocked(entry))
|
||||
return;
|
||||
|
||||
int currentEnabledIndex = GetEnabledSkillIndex();
|
||||
if (index == currentEnabledIndex)
|
||||
{
|
||||
pendingSkillIndex = -1;
|
||||
RefreshDetailPanel();
|
||||
RefreshChangeUi();
|
||||
RefreshStatusesOnly();
|
||||
return;
|
||||
}
|
||||
|
||||
pendingSkillIndex = pendingSkillIndex == index ? -1 : index;
|
||||
RefreshDetailPanel();
|
||||
RefreshChangeUi();
|
||||
RefreshStatusesOnly();
|
||||
}
|
||||
|
||||
public bool IsSkillPreselected(int index)
|
||||
{
|
||||
return pendingSkillIndex >= 0 && pendingSkillIndex == index;
|
||||
}
|
||||
|
||||
public bool IsSkillUnlocked(userLevel_skills_SO.UserLevelSkillEntry entry)
|
||||
{
|
||||
EnsurePlayerSo();
|
||||
|
||||
if (entry == null)
|
||||
return false;
|
||||
|
||||
int currentExp = pso != null ? Mathf.Max(0, pso.player_currentEXP) : 0;
|
||||
return currentExp >= Mathf.Max(0, entry.thisLevelNeedExps);
|
||||
}
|
||||
|
||||
private void RefreshStatusesOnly()
|
||||
{
|
||||
for (int i = 0; i < spawnedBars.Count; i++)
|
||||
{
|
||||
if (spawnedBars[i] == null)
|
||||
continue;
|
||||
|
||||
spawnedBars[i].RefreshVisualState();
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshDetailPanel()
|
||||
{
|
||||
if (ulsSO == null || ulsSO.skills == null || ulsSO.skills.Count == 0)
|
||||
{
|
||||
SetDetailPanel(null);
|
||||
return;
|
||||
}
|
||||
|
||||
int detailIndex = pendingSkillIndex >= 0 ? pendingSkillIndex : GetEnabledSkillIndex();
|
||||
if (detailIndex < 0 || detailIndex >= ulsSO.skills.Count)
|
||||
{
|
||||
SetDetailPanel(null);
|
||||
return;
|
||||
}
|
||||
|
||||
SetDetailPanel(ulsSO.skills[detailIndex]);
|
||||
}
|
||||
|
||||
private void SetDetailPanel(userLevel_skills_SO.UserLevelSkillEntry entry)
|
||||
{
|
||||
if (btm_skillImage != null)
|
||||
{
|
||||
btm_skillImage.sprite = entry != null ? entry.skillIcon : null;
|
||||
}
|
||||
|
||||
if (skillIcon_Profile != null)
|
||||
{
|
||||
skillIcon_Profile.sprite = entry != null ? entry.skillIcon : null;
|
||||
}
|
||||
|
||||
if (skill_thisSkillName != null)
|
||||
{
|
||||
skill_thisSkillName.text = entry != null ? (entry.skillName ?? string.Empty) : string.Empty;
|
||||
}
|
||||
|
||||
if (skill_thisSkillSummary != null)
|
||||
{
|
||||
skill_thisSkillSummary.text = entry != null ? (entry.skillDescription ?? string.Empty) : string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshChangeUi()
|
||||
{
|
||||
int cooldownRemaining = PlayerSkillService.GetSkillSwitchCooldownRemainingMatches();
|
||||
int currentEnabledIndex = GetEnabledSkillIndex();
|
||||
bool hasPendingSelection = pendingSkillIndex >= 0 && pendingSkillIndex != currentEnabledIndex;
|
||||
|
||||
if (need_text != null)
|
||||
{
|
||||
need_text.text = cooldownRemaining > 0
|
||||
? $"还需要{cooldownRemaining}场比赛"
|
||||
: "可以切换技能";
|
||||
}
|
||||
|
||||
if (need_progress != null)
|
||||
{
|
||||
need_progress.minValue = 0f;
|
||||
need_progress.maxValue = 1f;
|
||||
need_progress.value = cooldownRemaining > 0
|
||||
? Mathf.Clamp01((float)(SkillSwitchCooldownMatches - cooldownRemaining) / SkillSwitchCooldownMatches)
|
||||
: 1f;
|
||||
}
|
||||
|
||||
int switchCost = GetSkillSwitchCost();
|
||||
bool hasEnoughCoins = HasEnoughCoinsForSwitch(switchCost);
|
||||
|
||||
if (need_howmuchCoins != null)
|
||||
{
|
||||
need_howmuchCoins.text = switchCost.ToString();
|
||||
need_howmuchCoins.color = hasEnoughCoins ? defaultNeedCoinsColor : Color.red;
|
||||
}
|
||||
|
||||
if (yesChangeButton != null)
|
||||
{
|
||||
yesChangeButton.interactable = hasPendingSelection && cooldownRemaining <= 0 && hasEnoughCoins;
|
||||
}
|
||||
|
||||
if (noCancelButton != null)
|
||||
{
|
||||
noCancelButton.interactable = pendingSkillIndex >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
private int GetSkillSwitchCost()
|
||||
{
|
||||
int currentCoins = Application.isPlaying
|
||||
? PlayerEconomyLedger.EnsureInstance().GetCoins()
|
||||
: (pso != null ? Mathf.Max(0, pso.Coins) : 0);
|
||||
int percentCost = Mathf.CeilToInt(currentCoins * 0.01f);
|
||||
return Mathf.Max(SkillSwitchMinimumCost, percentCost);
|
||||
}
|
||||
|
||||
private bool HasEnoughCoinsForSwitch(int amount)
|
||||
{
|
||||
if (amount <= 0)
|
||||
return true;
|
||||
|
||||
return Application.isPlaying
|
||||
? PlayerEconomyLedger.EnsureInstance().HasEnoughCoins(amount)
|
||||
: (pso != null && pso.Coins >= amount);
|
||||
}
|
||||
|
||||
private void HandleConfirmSwitchClicked()
|
||||
{
|
||||
int currentEnabledIndex = GetEnabledSkillIndex();
|
||||
if (pendingSkillIndex < 0 || pendingSkillIndex == currentEnabledIndex || ulsSO == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (PlayerSkillService.GetSkillSwitchCooldownRemainingMatches() > 0)
|
||||
{
|
||||
RefreshChangeUi();
|
||||
return;
|
||||
}
|
||||
|
||||
int switchCost = GetSkillSwitchCost();
|
||||
bool spent = Application.isPlaying
|
||||
? PlayerEconomyLedger.EnsureInstance().TrySpendCoins(switchCost)
|
||||
: (pso != null && pso.TrySpendCoins(switchCost));
|
||||
if (!spent)
|
||||
{
|
||||
RefreshChangeUi();
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerSkillService.ConfirmSkillSelection(ulsSO, pendingSkillIndex);
|
||||
pendingSkillIndex = -1;
|
||||
MarkSkillsDirty();
|
||||
RefreshStatusesOnly();
|
||||
RefreshDetailPanel();
|
||||
RefreshChangeUi();
|
||||
}
|
||||
|
||||
private void HandleCancelPreselectClicked()
|
||||
{
|
||||
pendingSkillIndex = -1;
|
||||
RefreshStatusesOnly();
|
||||
RefreshDetailPanel();
|
||||
RefreshChangeUi();
|
||||
}
|
||||
|
||||
private void ClearExistingBars()
|
||||
{
|
||||
for (int i = uSkillBarParent.childCount - 1; i >= 0; i--)
|
||||
{
|
||||
Transform child = uSkillBarParent.GetChild(i);
|
||||
if (Application.isPlaying)
|
||||
Destroy(child.gameObject);
|
||||
else
|
||||
DestroyImmediate(child.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
private void MarkSkillsDirty()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying && ulsSO != null)
|
||||
UnityEditor.EditorUtility.SetDirty(ulsSO);
|
||||
#endif
|
||||
}
|
||||
|
||||
private void EnsurePlayerSo()
|
||||
{
|
||||
if (pso == null)
|
||||
pso = Resources.Load<Player_SO>("playerdata/playerData_uniqueData");
|
||||
}
|
||||
|
||||
private void CacheUiDefaults()
|
||||
{
|
||||
if (need_howmuchCoins != null)
|
||||
{
|
||||
defaultNeedCoinsColor = need_howmuchCoins.color;
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterButtons()
|
||||
{
|
||||
if (yesChangeButton != null)
|
||||
{
|
||||
yesChangeButton.onClick.RemoveListener(HandleConfirmSwitchClicked);
|
||||
yesChangeButton.onClick.AddListener(HandleConfirmSwitchClicked);
|
||||
}
|
||||
|
||||
if (noCancelButton != null)
|
||||
{
|
||||
noCancelButton.onClick.RemoveListener(HandleCancelPreselectClicked);
|
||||
noCancelButton.onClick.AddListener(HandleCancelPreselectClicked);
|
||||
}
|
||||
}
|
||||
|
||||
private void UnregisterButtons()
|
||||
{
|
||||
if (yesChangeButton != null)
|
||||
{
|
||||
yesChangeButton.onClick.RemoveListener(HandleConfirmSwitchClicked);
|
||||
}
|
||||
|
||||
if (noCancelButton != null)
|
||||
{
|
||||
noCancelButton.onClick.RemoveListener(HandleCancelPreselectClicked);
|
||||
}
|
||||
}
|
||||
|
||||
private int GetEnabledSkillIndex()
|
||||
{
|
||||
if (ulsSO == null || ulsSO.skills == null)
|
||||
return -1;
|
||||
|
||||
for (int i = 0; i < ulsSO.skills.Count; i++)
|
||||
{
|
||||
var entry = ulsSO.skills[i];
|
||||
if (entry != null && entry.isEnabled)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void NormalizeSelectionState()
|
||||
{
|
||||
if (ulsSO == null || ulsSO.skills == null || ulsSO.skills.Count == 0)
|
||||
return;
|
||||
|
||||
int selectedIndex = -1;
|
||||
for (int i = 0; i < ulsSO.skills.Count; i++)
|
||||
{
|
||||
var entry = ulsSO.skills[i];
|
||||
if (entry == null || !entry.isEnabled)
|
||||
continue;
|
||||
|
||||
if (selectedIndex < 0)
|
||||
{
|
||||
selectedIndex = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.isEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedIndex < 0)
|
||||
{
|
||||
for (int i = 0; i < ulsSO.skills.Count; i++)
|
||||
{
|
||||
var entry = ulsSO.skills[i];
|
||||
if (entry == null)
|
||||
continue;
|
||||
|
||||
selectedIndex = i;
|
||||
entry.isEnabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < ulsSO.skills.Count; i++)
|
||||
{
|
||||
var entry = ulsSO.skills[i];
|
||||
if (entry == null)
|
||||
continue;
|
||||
|
||||
entry.isEnabled = (i == selectedIndex);
|
||||
}
|
||||
|
||||
MarkSkillsDirty();
|
||||
PlayerSkillService.RegisterSkillAsset(ulsSO);
|
||||
pendingSkillIndex = -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 522d48b038e44bd4289b57ea155ae0b9
|
||||
@@ -0,0 +1,92 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 19a25568f1e1a8b4093dce8a6753be0e, type: 3}
|
||||
m_Name: ulsSO
|
||||
m_EditorClassIdentifier:
|
||||
skills:
|
||||
- userLevel: 0
|
||||
skillName: "\u8D5B\u524D\u51C6\u5907"
|
||||
skillDescription: "\u4F60\u5DF2\u6765\u5230\u672C\u7D22\u5C3C\u514B\u4E16\u754C\u3002\u8D5B\u524D\u51C6\u5907\u65F6\uFF0C\u4F60\u53EF\u4EE5\u65E0\u9650\u5236\u5730\u8FDB\u5165\u65B0\u624B\u6559\u7A0B\u3002"
|
||||
skillSummary: "\u5378\u4E0B\u5B83\u65F6\u4F60\u5DF2\u4E0D\u518D\u662F\u65B0\u624B"
|
||||
isEnabled: 1
|
||||
skillIcon: {fileID: 0}
|
||||
thisLevelNeedExps: 0
|
||||
- userLevel: 1
|
||||
skillName: "\u8BB0\u5FC6\u8D2D\u4E70\u8BA1\u5212"
|
||||
skillDescription: "\u5141\u8BB8\u4F60\u4EE510\u91D1\u5E01\u8D2D\u4E701\u4E2A\u8BB0\u5FC6\u788E\u7247\u3002\u88C5\u5907\u6B64\u6280\u80FD\u65F6\u53EF\u4E8E\u5546\u5E97\u89E3\u9501\u4E00\u4E2A\u8D2D\u4E70\u9009\u9879\u3002"
|
||||
skillSummary: "\u4ECE\u8001\u5E08\u90A3\u91CC\u65B0\u5B66\u4E86\u4EC0\u4E48\uFF1F"
|
||||
isEnabled: 0
|
||||
skillIcon: {fileID: 0}
|
||||
thisLevelNeedExps: 200
|
||||
- userLevel: 2
|
||||
skillName: "\u8BB0\u5FC6\u5F52\u8FD8\u8BA1\u5212"
|
||||
skillDescription: "\u5141\u8BB8\u4F60\u5C061\u4E2A\u8BB0\u5FC6\u788E\u7247\u4EE54\u91D1\u5E01\u552E\u51FA\u3002\u88C5\u5907\u6B64\u6280\u80FD\u65F6\u53EF\u4E8E\u5546\u5E97\u89E3\u9501\u4E00\u4E2A\u8D2D\u4E70\u9009\u9879\u3002"
|
||||
skillSummary: "\u8001\u5E08\u6559\u7ED9\u4F60\u7684\u53C8\u8FD8\u56DE\u53BB\u4E86\u5417\uFF1F"
|
||||
isEnabled: 0
|
||||
skillIcon: {fileID: 0}
|
||||
thisLevelNeedExps: 300
|
||||
- userLevel: 3
|
||||
skillName: "\u6311\u6218\u8005\u6A21\u578B"
|
||||
skillDescription: "\u5141\u8BB8\u8BBE\u7F6E\u4E00\u4E2A\u81EA\u5B9A\u7684\u767E\u5206\u6BD4\u9ED1\u5E55\u6765\u906E\u4F4F\u8F68\u9053\u3002\u6700\u5C11\u906E\u632120%\uFF0C\u6700\u591A\u906E\u632190%\u3002\u6BCF\u906E\u63211%\uFF0C\u63D0\u4F9B1.5%\u7ED3\u7B97\u5956\u52B1\u52A0\u6210\u3002"
|
||||
skillSummary: "\u9AD8\u98CE\u9669\u9AD8\u56DE\u62A5\uFF0C\u597D\u5947\u5FC3\u5BB3\u6B7B\u732B\u3002"
|
||||
isEnabled: 0
|
||||
skillIcon: {fileID: 0}
|
||||
thisLevelNeedExps: 400
|
||||
- userLevel: 4
|
||||
skillName: "\u8D5B\u540E\u611F\u609F"
|
||||
skillDescription: "\u6BCF\u8FDB\u884C10\u573A\u6BD4\u8D5B\u53EF\u83B7\u5F97\u4E00\u4EF6\u968F\u673A\u7C7B\u578B\u968F\u673A\u5C5E\u6027\u7684\u8BB0\u5FC6\u3002\u5728\u66F4\u6362\u6280\u80FD\u65F6\u6E05\u96F6\u8BA1\u6570\u5668\u3002"
|
||||
skillSummary:
|
||||
isEnabled: 0
|
||||
skillIcon: {fileID: 0}
|
||||
thisLevelNeedExps: 500
|
||||
- userLevel: 5
|
||||
skillName: "\u6211\u7231\u53D1\u660E"
|
||||
skillDescription: "\u4F60\u7684\u8BB0\u5FC6\u878D\u5408\u548C\u52A0\u901F\u6240\u9700\u7684\u8D27\u5E01\u51CF\u534A\u3002"
|
||||
skillSummary:
|
||||
isEnabled: 0
|
||||
skillIcon: {fileID: 0}
|
||||
thisLevelNeedExps: 600
|
||||
- userLevel: 6
|
||||
skillName: "\u8FFD\u68A6\u4E4B\u8DEF"
|
||||
skillDescription: "\u7ED3\u7B97\u65F6\uFF0C\u83B7\u5F97\u5076\u50CF\u5206\u65701\u2030\u7684\u8BB0\u5FC6\u788E\u7247\u3002"
|
||||
skillSummary:
|
||||
isEnabled: 0
|
||||
skillIcon: {fileID: 0}
|
||||
thisLevelNeedExps: 700
|
||||
- userLevel: 7
|
||||
skillName: "\u6D1E\u5BDF"
|
||||
skillDescription: "\u4F60\u53EF\u4EE5\u770B\u5230\u89D2\u8272\u672A\u89E3\u9501\u7684\u6280\u80FD\u53CA\u5176\u4ECB\u7ECD\u3002"
|
||||
skillSummary: "\u4F60\u7684\u76EE\u5149\u6781\u5177\u6218\u7565\u524D\u77BB\u6027\u3002"
|
||||
isEnabled: 0
|
||||
skillIcon: {fileID: 0}
|
||||
thisLevelNeedExps: 800
|
||||
- userLevel: 8
|
||||
skillName: "95\u6298"
|
||||
skillDescription: "\u6D88\u8D39\u8BB0\u5FC6\u788E\u7247\u548Ccoins\u65F6\u767E\u5206\u6BD4\u8FD4\u8FD8\u3002\u4F60\u6BCF\u62E5\u6709\u4E00\u4E2A\u5076\u50CF\uFF0C\u589E\u52A0\u6D88\u8D39\u8FD4\u8FD8\u7684\u6BD4\u4F8B0.5%\u3002\u6700\u591A\u5408\u8BA1\u8FD4\u8FD85%\u3002"
|
||||
skillSummary:
|
||||
isEnabled: 0
|
||||
skillIcon: {fileID: 0}
|
||||
thisLevelNeedExps: 900
|
||||
- userLevel: 9
|
||||
skillName: "\u5229\u6DA6\u63D0\u53D6\u5668"
|
||||
skillDescription: "\u7ED3\u7B97\u65F6\uFF0C\u83B7\u5F97\u5076\u50CF\u5206\u65702\u2030\u7684\u91D1\u5E01\u3002"
|
||||
skillSummary: "\u6B7B\u4E0E\u7A0E"
|
||||
isEnabled: 0
|
||||
skillIcon: {fileID: 0}
|
||||
thisLevelNeedExps: 1100
|
||||
- userLevel: 10
|
||||
skillName: "\u9876\u5CF0\u76F8\u89C1"
|
||||
skillDescription: "\u6E38\u620F\u4E2D\u4F60\u7684\u89C6\u89D2\u9AD8\u5EA6\u63D0\u9AD810%\u3002"
|
||||
skillSummary: "\u611F\u6069\u6559\u7EC3\u3002"
|
||||
isEnabled: 0
|
||||
skillIcon: {fileID: 0}
|
||||
thisLevelNeedExps: 1500
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: acf1a85d46975694aba2cdc12b9b3dfb
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,20 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[CreateAssetMenu(fileName = "userLevel_skills", menuName = "Player/UserLevel Skills SO")]
|
||||
public class userLevel_skills_SO : ScriptableObject
|
||||
{
|
||||
[System.Serializable]
|
||||
public class UserLevelSkillEntry
|
||||
{
|
||||
public int userLevel;
|
||||
public string skillName;
|
||||
[TextArea(3, 10)] public string skillDescription;
|
||||
public string skillSummary;
|
||||
public bool isEnabled;
|
||||
public Sprite skillIcon;
|
||||
public int thisLevelNeedExps;
|
||||
}
|
||||
|
||||
public List<UserLevelSkillEntry> skills = new List<UserLevelSkillEntry>();
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 19a25568f1e1a8b4093dce8a6753be0e
|
||||
Reference in New Issue
Block a user