diff --git a/Assets/PerfectWorld/Prefab/UI/MessageBox.prefab b/Assets/PerfectWorld/Prefab/UI/MessageBox.prefab
index c68a796b79..4c624559eb 100644
--- a/Assets/PerfectWorld/Prefab/UI/MessageBox.prefab
+++ b/Assets/PerfectWorld/Prefab/UI/MessageBox.prefab
@@ -1,5 +1,126 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &99051929539797809
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 4915293664251870536}
+ - component: {fileID: 7416811990837147698}
+ - component: {fileID: 6809282891346298764}
+ - component: {fileID: 7010901635634620631}
+ m_Layer: 5
+ m_Name: ButtonNo
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &4915293664251870536
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 99051929539797809}
+ 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: 2385775756193422540}
+ m_Father: {fileID: 2782318616460798463}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 0, y: 0}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 205.9258, y: 56.2433}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &7416811990837147698
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 99051929539797809}
+ m_CullTransparentMesh: 1
+--- !u!114 &6809282891346298764
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 99051929539797809}
+ 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: 0.14578143, g: 0.7924528, b: 0.1864423, 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: 0}
+ 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 &7010901635634620631
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 99051929539797809}
+ 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: 6809282891346298764}
+ m_OnClick:
+ m_PersistentCalls:
+ m_Calls: []
--- !u!1 &1448650841350251410
GameObject:
m_ObjectHideFlags: 0
@@ -168,10 +289,10 @@ RectTransform:
m_Children: []
m_Father: {fileID: 7906706137011413807}
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: -81}
- m_SizeDelta: {x: 200, y: 50}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &5808684862484431307
CanvasRenderer:
@@ -201,7 +322,7 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
- m_text: OK
+ m_text: "\u0110\u1ED3ng \xDD(Y)"
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
@@ -228,10 +349,10 @@ MonoBehaviour:
m_faceColor:
serializedVersion: 2
rgba: 4294967295
- m_fontSize: 36
+ m_fontSize: 44.25
m_fontSizeBase: 36
m_fontWeight: 400
- m_enableAutoSizing: 0
+ m_enableAutoSizing: 1
m_fontSizeMin: 18
m_fontSizeMax: 72
m_fontStyle: 0
@@ -272,6 +393,70 @@ MonoBehaviour:
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
+--- !u!1 &4140524140714306011
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 2782318616460798463}
+ - component: {fileID: 6567280976245013390}
+ m_Layer: 5
+ m_Name: Buttons
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &2782318616460798463
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4140524140714306011}
+ 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: 4915293664251870536}
+ - {fileID: 7906706137011413807}
+ m_Father: {fileID: 8578995796031649400}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 0}
+ m_AnchoredPosition: {x: 0, y: 41.609}
+ m_SizeDelta: {x: 0, y: 83.217}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &6567280976245013390
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4140524140714306011}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 30649d3a9faa99c48a7b1166b86bf2a0, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Padding:
+ m_Left: 0
+ m_Right: 0
+ m_Top: 0
+ m_Bottom: 0
+ m_ChildAlignment: 4
+ m_Spacing: 11.43
+ m_ChildForceExpandWidth: 0
+ m_ChildForceExpandHeight: 1
+ m_ChildControlWidth: 0
+ m_ChildControlHeight: 0
+ m_ChildScaleWidth: 0
+ m_ChildScaleHeight: 0
+ m_ReverseArrangement: 0
--- !u!1 &4839074738306786208
GameObject:
m_ObjectHideFlags: 0
@@ -285,7 +470,7 @@ GameObject:
- component: {fileID: 8250962023850685786}
- component: {fileID: 7766051278568089760}
m_Layer: 5
- m_Name: ButtonExit
+ m_Name: ButtonOk
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
@@ -298,18 +483,18 @@ RectTransform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4839074738306786208}
- m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ 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: 7677644201189461152}
- m_Father: {fileID: 8578995796031649400}
+ m_Father: {fileID: 2782318616460798463}
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_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
- m_SizeDelta: {x: 160, y: 30}
+ m_SizeDelta: {x: 205.9258, y: 56.2433}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &8280971203118505009
CanvasRenderer:
@@ -332,7 +517,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
- m_Color: {r: 1, g: 1, b: 1, a: 1}
+ m_Color: {r: 0.14578143, g: 0.7924528, b: 0.1864423, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
@@ -425,10 +610,10 @@ RectTransform:
m_Children: []
m_Father: {fileID: 8578995796031649400}
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: 3}
- m_SizeDelta: {x: 200, y: 50}
+ m_AnchorMin: {x: 0, y: 0.5}
+ m_AnchorMax: {x: 1, y: 0.5}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 50}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &655909173274991632
CanvasRenderer:
@@ -487,10 +672,10 @@ MonoBehaviour:
m_faceColor:
serializedVersion: 2
rgba: 4294967295
- m_fontSize: 36
+ m_fontSize: 44.75
m_fontSizeBase: 36
m_fontWeight: 400
- m_enableAutoSizing: 0
+ m_enableAutoSizing: 1
m_fontSizeMin: 18
m_fontSizeMax: 72
m_fontStyle: 0
@@ -562,15 +747,15 @@ RectTransform:
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- - {fileID: 7906706137011413807}
- {fileID: 1931146730219979515}
- {fileID: 6428994832978992641}
+ - {fileID: 2782318616460798463}
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: 30.6318}
- m_SizeDelta: {x: 434.6896, y: 248.9211}
+ m_SizeDelta: {x: 1067.9689, y: 248.9211}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &2243330050876855902
CanvasRenderer:
@@ -625,3 +810,140 @@ MonoBehaviour:
titleText: {fileID: 5031655611580643013}
messageText: {fileID: 7448521238108099750}
okButton: {fileID: 7766051278568089760}
+ _noButton: {fileID: 7010901635634620631}
+--- !u!1 &5664175764923475105
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 2385775756193422540}
+ - component: {fileID: 8838082653881325633}
+ - component: {fileID: 438315464709753381}
+ m_Layer: 5
+ m_Name: Text (TMP)
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &2385775756193422540
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5664175764923475105}
+ 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: 4915293664251870536}
+ 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: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &8838082653881325633
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5664175764923475105}
+ m_CullTransparentMesh: 1
+--- !u!114 &438315464709753381
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5664175764923475105}
+ 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: "H\u1EE7y(N)"
+ m_isRightToLeft: 0
+ m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
+ m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
+ m_fontSharedMaterials: []
+ m_fontMaterial: {fileID: 0}
+ m_fontMaterials: []
+ m_fontColor32:
+ serializedVersion: 2
+ rgba: 4278190080
+ m_fontColor: {r: 0, g: 0, b: 0, 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: 50.3
+ m_fontSizeBase: 36
+ m_fontWeight: 400
+ m_enableAutoSizing: 1
+ 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: 0
+ 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}
diff --git a/Assets/PerfectWorld/Prefab/UIManager.prefab b/Assets/PerfectWorld/Prefab/UIManager.prefab
index d54d64101c..ab2d2b4c7e 100644
--- a/Assets/PerfectWorld/Prefab/UIManager.prefab
+++ b/Assets/PerfectWorld/Prefab/UIManager.prefab
@@ -999,6 +999,7 @@ RectTransform:
- {fileID: 3483809415181351540}
- {fileID: 7451658084820611230}
- {fileID: 807958754388851913}
+ - {fileID: 7749074831901819156}
m_Father: {fileID: 2780428059708698453}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
@@ -2282,6 +2283,151 @@ MonoBehaviour:
- {fileID: 2554621538146193444}
- {fileID: 5079395126799912635}
- {fileID: 2024142370080989935}
+--- !u!1 &5525441299837637062
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 7749074831901819156}
+ - component: {fileID: 7710645045682326551}
+ - component: {fileID: 4846176155215941588}
+ - component: {fileID: 2039217357137269515}
+ - component: {fileID: 354882936729747584}
+ m_Layer: 0
+ m_Name: BackToSelect_btn
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &7749074831901819156
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5525441299837637062}
+ 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: 3233441867675090637}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 1}
+ m_AnchorMax: {x: 0, y: 1}
+ m_AnchoredPosition: {x: 49, y: -229.8}
+ m_SizeDelta: {x: 85, y: 92}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &7710645045682326551
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5525441299837637062}
+ m_CullTransparentMesh: 1
+--- !u!114 &4846176155215941588
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5525441299837637062}
+ 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: 052d4b5b3b147d04cbd00f737f5c7c53, 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 &2039217357137269515
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5525441299837637062}
+ 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: 4846176155215941588}
+ m_OnClick:
+ m_PersistentCalls:
+ m_Calls:
+ - m_Target: {fileID: 354882936729747584}
+ m_TargetAssemblyTypeName: BrewMonster.UI.BtnBackToSelectRole, Assembly-CSharp
+ m_MethodName: OnClick
+ m_Mode: 1
+ m_Arguments:
+ m_ObjectArgument: {fileID: 0}
+ m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
+ m_IntArgument: 0
+ m_FloatArgument: 0
+ m_StringArgument:
+ m_BoolArgument: 0
+ m_CallState: 2
+--- !u!114 &354882936729747584
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5525441299837637062}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: f9aed6caec10d2a44a39e3b9c458c5c5, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
--- !u!1 &5539920506348156658
GameObject:
m_ObjectHideFlags: 0
diff --git a/Assets/PerfectWorld/Resources/UI/DialogScriptTableObject.asset b/Assets/PerfectWorld/Resources/UI/DialogScriptTableObject.asset
index f1405a4f75..393710000b 100644
--- a/Assets/PerfectWorld/Resources/UI/DialogScriptTableObject.asset
+++ b/Assets/PerfectWorld/Resources/UI/DialogScriptTableObject.asset
@@ -39,5 +39,5 @@ MonoBehaviour:
prefab: {fileID: 6310702841431484757, guid: 6620f766cee7c8f4cb00dd457ac77675, type: 3}
- id: Win_ArrangeTeam
prefab: {fileID: 2452003196178065293, guid: 5a3e828efd5c542469d1f17565205ded, type: 3}
- - id: CDlgInfoTooltip
- prefab: {fileID: 6830833846243993097, guid: 97dd1de3aba08a04980849e40d5c1ea4, type: 3}
+ - id: DlgMessageBox
+ prefab: {fileID: 5492547392745930423, guid: 54cccb2c6a758a24183474cd385ccb2c, type: 3}
diff --git a/Assets/PerfectWorld/Scripts/Common/Singleton.cs b/Assets/PerfectWorld/Scripts/Common/Singleton.cs
index 5b16cdb2e1..460a57c271 100644
--- a/Assets/PerfectWorld/Scripts/Common/Singleton.cs
+++ b/Assets/PerfectWorld/Scripts/Common/Singleton.cs
@@ -16,6 +16,13 @@ namespace BrewMonster
{
if (_instance.IsValueCreated)
throw new InvalidOperationException($"Singleton<{typeof(T).Name}> already created!");
+
+ Initialize();
+ }
+
+ protected virtual void Initialize()
+ {
+
}
}
}
diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrAutoMp.cs b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrAutoMp.cs
index ed77e2bee8..e51e89d92e 100644
--- a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrAutoMp.cs
+++ b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrAutoMp.cs
@@ -165,8 +165,9 @@ namespace PerfectWorld.Scripts.Managers
}
// Get item cool time
- public int GetCoolTime(ref int piMax)
+ public override int GetCoolTime(out int piMax)
{
+ piMax = 0;
CECHostPlayer pHost = EC_Game.GetGameRun().GetHostPlayer();
return pHost != null ? pHost.GetCoolTime((int)CoolTimeIndex.GP_CT_AUTOMP, out piMax) : 0;
}
diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrGeneralCard.cs b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrGeneralCard.cs
index bbc1cd4fee..53e3c432cd 100644
--- a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrGeneralCard.cs
+++ b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrGeneralCard.cs
@@ -3,6 +3,8 @@ namespace PerfectWorld.Scripts.Managers
{
public class EC_IvtrGeneralCard : EC_IvtrItem
{
+ private IVTR_ESSENCE_GENERALCARD m_Essence;
+
///
/// Not create logic yet (add summary later)
///
@@ -15,6 +17,11 @@ namespace PerfectWorld.Scripts.Managers
public EC_IvtrGeneralCard(EC_IvtrGeneralCard other) : base(other)
{
}
+
+ public IVTR_ESSENCE_GENERALCARD GetEssence()
+ {
+ return m_Essence;
+ }
}
}
diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrTaskDice.cs b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrTaskDice.cs
index 934066e194..77db9842cb 100644
--- a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrTaskDice.cs
+++ b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrTaskDice.cs
@@ -80,6 +80,11 @@ namespace PerfectWorld.Scripts.Managers
{
return m_pDBEssence.FileMatter;
}
+
+ public TASKDICE_ESSENCE GetDBEssence()
+ {
+ return m_pDBEssence;
+ }
}
}
diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrType.cs b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrType.cs
index df359a2ed2..3ecaf3f726 100644
--- a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrType.cs
+++ b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrType.cs
@@ -348,7 +348,7 @@ namespace BrewMonster.Scripts.Managers
public struct IVTR_ESSENCE_GENERALCARD
{
// TODO : implement data later
- // int type;
+ public int type;
// int rank;
// int require_level;
// int require_leadership;
diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs
index 106516a128..abe6fe50b2 100644
--- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs
+++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs
@@ -1600,6 +1600,12 @@ namespace CSNetwork.S2CCommand
public int strength;
public int agility;
}
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct SevClearEmbeddedChipContent
+ {
+ public int iEquipIdx;
+ public int tidEquip;
+ }
}
// Player and NPC state
\ No newline at end of file
diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs
index 7ef1385a41..6a0fecedbe 100644
--- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs
+++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs
@@ -269,6 +269,15 @@ namespace CSNetwork.C2SCommand
};
return SerializeCommand(CommandID.STOP_MOVE, cmd);
}
+
+ public static Octets CreatePlayerLogoutCmd(int outType)
+ {
+ var cmd = new CMD_PlayerLogout
+ {
+ outType = outType
+ };
+ return SerializeCommand(CommandID.LOGOUT, cmd);
+ }
public static Octets CreatePlayerCastSkill(int idSkill, byte byPVPMask, int iNumTarget, int[] aTargets)
{
var cmd = new CMD_CastSkill
@@ -993,5 +1002,19 @@ namespace CSNetwork.C2SCommand
};
return SerializeCommand(CommandID.SET_STATUS_POINT, pCmd);
}
+ public static Octets CreateNPCSevClearEmbeddedChipCmd(int iEquipIdx, int tidEquip)
+ {
+ var cmd = new cmd_sevnpc_serve
+ {
+ service_type = NPC_service_type.GP_NPCSEV_CLEAR_TESSERA,
+ len = (uint)Marshal.SizeOf()
+ };
+ SevClearEmbeddedChipContent content = new SevClearEmbeddedChipContent()
+ {
+ iEquipIdx = iEquipIdx,
+ tidEquip = tidEquip,
+ };
+ return SerializeCommand(CommandID.SEVNPC_SERVE, cmd, content);
+ }
}
}
diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs
index b3fda706b1..19e2261b35 100644
--- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs
+++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs
@@ -47,6 +47,12 @@ namespace CSNetwork
public static SynchronizationContext Context;
private CECC2SCmdCache m_CmdCache; // C2S command cache
+ /// Raised when server sends PROTOCOL_PLAYERLOGOUT(69).
+ public event Action PlayerLogoutReceived;
+
+ /// Raised when the underlying network disconnects.
+ public event Action Disconnected;
+
private static void PostToUnityContext(Action action)
{
if (action == null) return;
@@ -564,13 +570,16 @@ namespace CSNetwork
OnPrtcPlayerBaseInfoRe(protocol);
break;
case ProtocolType.PROTOCOL_GETUICONFIG_RE: OnPrtcGetConfigRe(protocol); break;
+ case ProtocolType.PROTOCOL_PLAYERLOGOUT:
+ HandlePlayerLogout((playerlogout)protocol);
+ break;
case ProtocolType.PROTOCOL_AUTOTEAMSETGOAL_RE:
- {
- // CECAutoTeam pAutoTeam = CECGameRun.Instance.GetHostPlayer().GetAutoTeam();
- // if( pAutoTeam !=null)
- // pAutoTeam.OnPrtcAutoTeamSetGoalRe((AutoTeamSetGoal_Re)protocol);
- }
+ {
+ // CECAutoTeam pAutoTeam = CECGameRun.Instance.GetHostPlayer().GetAutoTeam();
+ // if( pAutoTeam !=null)
+ // pAutoTeam.OnPrtcAutoTeamSetGoalRe((AutoTeamSetGoal_Re)protocol);
+ }
break;
default:
@@ -579,6 +588,13 @@ namespace CSNetwork
}
}
+ private void HandlePlayerLogout(playerlogout protocol)
+ {
+ // Original client receives this before EVENT_DISCONNECT.
+ // We just publish it to allow higher-level flow (UnityGameSession/UI) to react.
+ PostToUnityContext(() => PlayerLogoutReceived?.Invoke(protocol));
+ }
+
private void HandleServerDataSend(gamedatasend protocol)
{
int lenghtHeader = Marshal.SizeOf();
@@ -811,7 +827,7 @@ namespace CSNetwork
#if UNITY_EDITOR
BMLogger.LogError($"### GameDataSend: ERROR_MESSAGE: {errRaw}");
#endif
- cmd_error_msg pCmd = GPDataTypeHelper.FromBytes(pDataBuf);
+ cmd_error_msg pCmd = GPDataTypeHelper.FromBytes(pDataBuf);
#if UNITY_EDITOR
BMLogger.LogError($"### GameDataSend: ERROR_MESSAGE parsed iMessage={pCmd.iMessage}");
#endif
@@ -829,50 +845,50 @@ namespace CSNetwork
// g_pGame.GetGameRun().AddChatMessage(szMsg, GP_CHAT_MISC);
}
- if (pCmd.iMessage == 2)
- {
- // Attack target is too far
- EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_TARGETISFAR, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
- }
- else if (pCmd.iMessage == 20)
- {
- // Failed to cast skill
- //pGameRun.PostMessage(MSG_PM_CASTSKILL, MAN_PLAYER, 0, (DWORD)pDataBuf, pCmdHeader.cmd);
- }
- else if (pCmd.iMessage == 133 || pCmd.iMessage == 134)
- {
- // deal failed
- //pGameRun.PostMessage(MSG_HST_BUY_SELL_FAIL, MAN_PLAYER, 0, (DWORD)pDataBuf, pCmdHeader.cmd);
- }
- else if (pCmd.iMessage == 158)
- {
- // µ±Ç°»ãÂʲ»¶Ô£¬ÖØÐÂÈ¡»ãÂÊ
- //c2s_CmdGetCashMoneyRate();
- }
- else if (pCmd.iMessage == 108 /*&& pGameRun.GetHostPlayer().IsInKingService()*/)
- {
- /* CECGameUIMan* pGameUI = pGameRun.GetUIManager().GetInGameUIMan();
- if (pGameUI)
- pGameUI.EndNPCService();*/
- }
- else if
- (pCmd.iMessage == 108 /*&& pGameRun.GetHostPlayer().GetOfflineShopCtrl().GetNPCSevFlag() != COfflineShopCtrl::NPCSEV_NULL*/
- )
- {
- /* CECGameUIMan* pGameUI = pGameRun.GetUIManager().GetInGameUIMan();
- if (pGameUI)
- pGameUI.EndNPCService();*/
- }
- else if (pCmd.iMessage == 175)
- {
- //c2s_CmdQueryParallelWorld();
- }
- else if (pCmd.iMessage == 6)
- {
- //AP_ActionEvent(AP_EVENT_CANNOTPICKUP);
- }
+ if (pCmd.iMessage == 2)
+ {
+ // Attack target is too far
+ EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_TARGETISFAR, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
+ }
+ else if (pCmd.iMessage == 20)
+ {
+ // Failed to cast skill
+ //pGameRun.PostMessage(MSG_PM_CASTSKILL, MAN_PLAYER, 0, (DWORD)pDataBuf, pCmdHeader.cmd);
+ }
+ else if (pCmd.iMessage == 133 || pCmd.iMessage == 134)
+ {
+ // deal failed
+ //pGameRun.PostMessage(MSG_HST_BUY_SELL_FAIL, MAN_PLAYER, 0, (DWORD)pDataBuf, pCmdHeader.cmd);
+ }
+ else if (pCmd.iMessage == 158)
+ {
+ // µ±Ç°»ãÂʲ»¶Ô£¬ÖØÐÂÈ¡»ãÂÊ
+ //c2s_CmdGetCashMoneyRate();
+ }
+ else if (pCmd.iMessage == 108 /*&& pGameRun.GetHostPlayer().IsInKingService()*/)
+ {
+ /* CECGameUIMan* pGameUI = pGameRun.GetUIManager().GetInGameUIMan();
+ if (pGameUI)
+ pGameUI.EndNPCService();*/
+ }
+ else if
+ (pCmd.iMessage == 108 /*&& pGameRun.GetHostPlayer().GetOfflineShopCtrl().GetNPCSevFlag() != COfflineShopCtrl::NPCSEV_NULL*/
+ )
+ {
+ /* CECGameUIMan* pGameUI = pGameRun.GetUIManager().GetInGameUIMan();
+ if (pGameUI)
+ pGameUI.EndNPCService();*/
+ }
+ else if (pCmd.iMessage == 175)
+ {
+ //c2s_CmdQueryParallelWorld();
+ }
+ else if (pCmd.iMessage == 6)
+ {
+ //AP_ActionEvent(AP_EVENT_CANNOTPICKUP);
+ }
- break;
+ break;
}
case CommandID.SELECT_TARGET:
case CommandID.UNSELECT:
@@ -941,7 +957,7 @@ namespace CSNetwork
case CommandID.OBJECT_CAST_INSTANT_SKILL:
case CommandID.OBJECT_CAST_POS_SKILL:
{
- cmd_object_cast_skill pCmd2 = GPDataTypeHelper.FromBytes(pDataBuf, true);
+ cmd_object_cast_skill pCmd2 = GPDataTypeHelper.FromBytes(pDataBuf,true);
if (ISPLAYERID(pCmd2.caster))
EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_CASTSKILL, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
else if (ISNPCID(pCmd2.caster))
@@ -1141,6 +1157,7 @@ namespace CSNetwork
FailRoleListInProgress("Disconnected");
// Clear command cache
m_CmdCache.RemoveAllCmds();
+ PostToUnityContext(() => Disconnected?.Invoke());
}
// --- Protocol Handling Logic ---
@@ -1200,7 +1217,7 @@ namespace CSNetwork
var callback = _loginCallback;
_loginCallback = null;
- callback?.Invoke(true);
+ PostToUnityContext(() => callback?.Invoke(true));
}
private void RequestRoleListInternal(int lastHandle = -1)
@@ -1396,6 +1413,18 @@ namespace CSNetwork
gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(C2SCommand.CommandID.CONTINUE_ACTION);
SendProtocol(gamedatasend);
}
+
+ ///
+ /// Client logout request. Mirrors original client behavior:
+ /// - outType=1: back to select role
+ /// - outType=0: logout account
+ ///
+ public void SendPlayerLogout(int outType, Action complete = null)
+ {
+ var g = new gamedatasend();
+ g.Data = C2SCommandFactory.CreatePlayerLogoutCmd(outType);
+ SendProtocol(g, complete);
+ }
public void c2s_SendCmdStopMove(in Vector3 vDest, float fSpeed, int iMoveMode,
byte byDir, ushort wStamp, int iTime)
{
@@ -1479,24 +1508,24 @@ namespace CSNetwork
// Get referral name for adding friend or other display
//TODO: a Hung lam phan select role info di
- /* RoleInfo info = EC_Game.GetGameRun().GetSelectedRoleInfo();
- if (info.referrer_role > 0)
- GetPlayerBriefInfo(1, info.referrer_role, 2);*/
+ /* RoleInfo info = EC_Game.GetGameRun().GetSelectedRoleInfo();
+ if (info.referrer_role > 0)
+ GetPlayerBriefInfo(1, info.referrer_role, 2);*/
}
CECHostPlayer pHost = EC_Game.GetGameRun().GetHostPlayer();
pHost.OnAllInitDataReady();
- /* if (pHost.IsGM())
- {
- CDlgCountryMap pDlgCountryMap = (CDlgCountryMap)pGameUI.GetDialog("Win_CountryMap");
- pDlgCountryMap.GetConfig();
- }
+ /* if (pHost.IsGM())
+ {
+ CDlgCountryMap pDlgCountryMap = (CDlgCountryMap)pGameUI.GetDialog("Win_CountryMap");
+ pDlgCountryMap.GetConfig();
+ }
- g_pGame.GetConfigs().ApplyOptimizeSetting();
+ g_pGame.GetConfigs().ApplyOptimizeSetting();
- if (g_pGame.GetConfigs().IsMiniClient())
- CECMCDownload::GetInstance().SendGetDownloadOK();*/
+ if (g_pGame.GetConfigs().IsMiniClient())
+ CECMCDownload::GetInstance().SendGetDownloadOK();*/
}
}
private void OnPrtcPlayerBaseInfoRe(Protocol pProtocol)
@@ -1703,7 +1732,8 @@ namespace CSNetwork
public void c2s_SendCmdTaskNotify(byte[] pData, uint dwDataSize)
{
gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateTaskNotifyCmd(pData, dwDataSize);
+ gamedatasend.Data = C2SCommandFactory.CreateTaskNotifyCmd( pData, dwDataSize);
+ BMLogger.Log($"[MH Task] c2s_SendCmdTaskNotify Command ID : {pData[0]} Size: {dwDataSize}");
SendProtocol(gamedatasend);
}
@@ -1713,11 +1743,11 @@ namespace CSNetwork
gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(C2SCommand.CommandID.STAND_UP);
SendProtocol(gamedatasend);
}
-
+
public void c2s_SendCmdAutoTeamSetGoal(int type, int goal_id, int op)
{
gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateAutoTeamSetGoalCommand(type, goal_id, op);
+ gamedatasend.Data = C2SCommandFactory.CreateAutoTeamSetGoalCommand(type,goal_id, op);
SendProtocol(gamedatasend);
}
@@ -1773,14 +1803,14 @@ namespace CSNetwork
public void c2s_CmdGoto(float x, float y, float z)
{
- c2s_SendCmdGoto(x, y, z);
+ c2s_SendCmdGoto(x, y, z);
}
-
+
// Send C2S::GOTO command data
void c2s_SendCmdGoto(float x, float y, float z)
{
gamedatasend gamedatasend = new gamedatasend();
- gamedatasend.Data = C2SCommandFactory.CreateGoToCommed(x, y, z);
+ gamedatasend.Data = C2SCommandFactory.CreateGoToCommed( x, y, z);
SendProtocol(gamedatasend);
}
@@ -1858,5 +1888,11 @@ namespace CSNetwork
gamedatasend.Data = C2SCommandFactory.CreateSetStatusPtCmd(vitality, energy, strength, agility);
SendProtocol(gamedatasend);
}
+ public void c2s_CmdNPCSevClearEmbeddedChip(int iEquipIdx, int tidEquip)
+ {
+ gamedatasend gamedatasend = new gamedatasend();
+ gamedatasend.Data = C2SCommandFactory.CreateNPCSevClearEmbeddedChipCmd(iEquipIdx, tidEquip);
+ SendProtocol(gamedatasend);
+ }
}
}
\ No newline at end of file
diff --git a/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs b/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs
index 0162ca2a57..be910eaca7 100644
--- a/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs
+++ b/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs
@@ -14,8 +14,10 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BrewMonster.Scripts.Task;
+using BrewMonster.UI;
using UnityEngine;
using UnityEngine.SceneManagement;
+using BrewMonster.Scripts;
namespace BrewMonster.Network
{
@@ -24,6 +26,8 @@ namespace BrewMonster.Network
// 2. Login
public class UnityGameSession : MonoSingleton
{
+ private const string WorldSceneName = "a61";
+ private const string LoginSceneName = "LoginScene";
private GameSession _gameSession;
private bool _isInitialized = false;
@@ -82,6 +86,28 @@ namespace BrewMonster.Network
Instance._ip = ip;
Instance._port = port;
}
+
+ ///
+ /// Origin-like: In-game can only return to Select Role (LOGOUT(1)).
+ /// This will load LoginScene and auto-login (using saved creds) to show the Select Role UI.
+ ///
+ public static void ReturnToSelectRole()
+ {
+ if (Instance == null) return;
+ // Origin-like: in-game only returns to Select Role (auto-login using saved creds).
+ // Keep world scene loaded (a61) but cleaned.
+ _ = Instance.LogoutAndReturnAsync(outType: 1, entryTarget: LogoutFlowState.LoginEntryTarget.SelectRole, clearSavedCreds: false);
+ }
+
+ ///
+ /// Origin-like: Account logout from Select Role (LOGOUT(0)).
+ /// This will clear saved creds and load LoginScene showing username/password UI.
+ ///
+ public static void LogoutAccount()
+ {
+ if (Instance == null) return;
+ _ = Instance.LogoutAndReturnAsync(outType: 0, entryTarget: LogoutFlowState.LoginEntryTarget.LoginUI, clearSavedCreds: true);
+ }
public static void c2s_CmdCastSkill(int idSkill, byte byPVPMask, int iNumTarget, int[] aTargets)
{
Instance._gameSession.CmdCache.SendCmdCastSkill(idSkill, byPVPMask, iNumTarget, aTargets);
@@ -146,6 +172,169 @@ namespace BrewMonster.Network
DontDestroyOnLoad(gameObject);
}
+
+ private async Task LogoutAndReturnAsync(int outType, LogoutFlowState.LoginEntryTarget entryTarget, bool clearSavedCreds)
+ {
+ // Tell LoginScene what to show next.
+ LogoutFlowState.NextLoginEntry = entryTarget;
+
+ if (clearSavedCreds)
+ {
+ PlayerPrefs.DeleteKey("username");
+ PlayerPrefs.DeleteKey("password");
+ PlayerPrefs.Save();
+ }
+
+ try
+ {
+ if (_gameSession != null && _gameSession.IsConnected)
+ {
+ // Send LOGOUT(outType) like the original client.
+ _gameSession.SendPlayerLogout(outType);
+
+ // Wait briefly for server-driven disconnect.
+ await WaitForDisconnectAsync(timeoutMs: 4000);
+ }
+ }
+ catch (Exception ex)
+ {
+ BMLogger.LogError($"LogoutAndReturnAsync exception: {ex.Message}");
+ }
+ finally
+ {
+ // Fallback: if server didn't close, close locally.
+ if (_gameSession != null && _gameSession.IsConnected)
+ {
+ _gameSession.Disconnect();
+ }
+ }
+
+ // Return to LoginScene.
+ // IMPORTANT: for outType=1 we must keep the world scene (a61) loaded; only "clean" runtime objects.
+ await Task.Yield();
+ // Requirement: even on account logout, keep a61 loaded (do not delete the world scene),
+ // just clean runtime objects and show LoginScene UI.
+ CleanRuntimeObjectsKeepWorld();
+ await EnsureSceneLoadedAdditiveAsync(WorldSceneName);
+ await EnsureLoginSceneAdditiveAndActivateAsync(keepSceneName: WorldSceneName);
+
+ // When LoginScene is already loaded additively, Start() won't re-run.
+ // Force the login UI to refresh to the correct entry state immediately.
+ ApplyLoginEntryToUI(entryTarget);
+
+ // now we have to logout from Tech3C SDK
+ Tech3CSDKWrapper.Instance.Logout();
+ }
+
+ private static void ApplyLoginEntryToUI(LogoutFlowState.LoginEntryTarget entryTarget)
+ {
+ try
+ {
+ // Find even if inactive (Resources.FindObjectsOfTypeAll returns inactive objects too).
+ var all = Resources.FindObjectsOfTypeAll();
+ for (int i = 0; i < all.Length; i++)
+ {
+ var ui = all[i];
+ if (ui == null) continue;
+ if (!ui.gameObject.scene.IsValid() || ui.gameObject.scene.name != LoginSceneName) continue;
+ // Avoid hard dependency on method existence (merges may edit LoginScreenUI).
+ ui.SendMessage("ApplyLoginEntry", entryTarget, SendMessageOptions.DontRequireReceiver);
+ return;
+ }
+ }
+ catch (Exception ex)
+ {
+ BMLogger.LogError($"ApplyLoginEntryToUI error: {ex.Message}");
+ }
+ }
+
+ private static void CleanRuntimeObjectsKeepWorld()
+ {
+ // Spawned runtime objects (player/monsters/vfx) are parented under ObjectSpawner.
+ // Destroying them "cleans" the world without unloading the scene (a61 remains loaded).
+ try
+ {
+ var spawner = BrewMonster.ObjectSpawner.Instance;
+ if (spawner == null) return;
+
+ var root = spawner.transform;
+ for (int i = root.childCount - 1; i >= 0; i--)
+ {
+ var child = root.GetChild(i);
+ if (child != null)
+ UnityEngine.Object.Destroy(child.gameObject);
+ }
+ }
+ catch (Exception ex)
+ {
+ BMLogger.LogError($"CleanRuntimeObjectsKeepWorld error: {ex.Message}");
+ }
+ }
+
+ private static async Task EnsureSceneLoadedAdditiveAsync(string sceneName)
+ {
+ if (string.IsNullOrEmpty(sceneName)) return;
+ var s = SceneManager.GetSceneByName(sceneName);
+ if (s.IsValid() && s.isLoaded) return;
+
+ var op = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
+ while (op != null && !op.isDone)
+ await Task.Yield();
+ }
+
+ private static async Task EnsureLoginSceneAdditiveAndActivateAsync(string keepSceneName)
+ {
+ // Load LoginScene additively if needed (do not unload keepSceneName, e.g., a61).
+ var loginScene = SceneManager.GetSceneByName(LoginSceneName);
+ if (!loginScene.IsValid() || !loginScene.isLoaded)
+ {
+ var op = SceneManager.LoadSceneAsync(LoginSceneName, LoadSceneMode.Additive);
+ while (op != null && !op.isDone)
+ await Task.Yield();
+ loginScene = SceneManager.GetSceneByName(LoginSceneName);
+ }
+
+ if (loginScene.IsValid() && loginScene.isLoaded)
+ {
+ SceneManager.SetActiveScene(loginScene);
+ }
+
+ // Optionally unload other non-world scenes (keep a61 + LoginScene).
+ // This prevents extra scenes like NPCRender staying around.
+ for (int i = 0; i < SceneManager.sceneCount; i++)
+ {
+ var s = SceneManager.GetSceneAt(i);
+ if (!s.IsValid() || !s.isLoaded) continue;
+ if (s.name == LoginSceneName) continue;
+ if (!string.IsNullOrEmpty(keepSceneName) && s.name == keepSceneName) continue;
+
+ // Only unload if it's not the active scene (we already switched active to LoginScene above).
+ if (SceneManager.GetActiveScene() == s) continue;
+ _ = SceneManager.UnloadSceneAsync(s);
+ }
+ }
+
+ private Task WaitForDisconnectAsync(int timeoutMs)
+ {
+ if (_gameSession == null || !_gameSession.IsConnected)
+ return Task.CompletedTask;
+
+ var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+ void OnDisc() => tcs.TrySetResult(true);
+ _gameSession.Disconnected += OnDisc;
+
+ return Task.Run(async () =>
+ {
+ try
+ {
+ await Task.WhenAny(tcs.Task, Task.Delay(timeoutMs));
+ }
+ finally
+ {
+ _gameSession.Disconnected -= OnDisc;
+ }
+ });
+ }
public RoleInfo GetRoleInfo()
{
return _gameSession.GetRoleInfo();
@@ -499,5 +688,38 @@ namespace BrewMonster.Network
{
Instance._gameSession.c2s_SendCmdGetItemInfo(byPackage, bySlot);
}
+ public static void c2s_CmdNPCSevClearEmbeddedChip(int iEquipIdx, int tidEquip)
+ {
+ Instance._gameSession.c2s_CmdNPCSevClearEmbeddedChip(iEquipIdx, tidEquip);
+ }
+ }
+
+ ///
+ /// Small cross-scene state to tell LoginScene what to show after a logout flow.
+ /// This mirrors the original client: in-game can only return to Select Role; Account Logout happens from Select Role.
+ ///
+ public static class LogoutFlowState
+ {
+ public enum LoginEntryTarget
+ {
+ LoginUI = 0,
+ SelectRole = 1,
+ }
+
+ private static LoginEntryTarget _nextEntry = LoginEntryTarget.LoginUI;
+
+ public static LoginEntryTarget NextLoginEntry
+ {
+ get => _nextEntry;
+ set => _nextEntry = value;
+ }
+
+ /// Consume and reset to default (LoginUI).
+ public static LoginEntryTarget ConsumeNextLoginEntry()
+ {
+ var v = _nextEntry;
+ _nextEntry = LoginEntryTarget.LoginUI;
+ return v;
+ }
}
}
\ No newline at end of file
diff --git a/Assets/PerfectWorld/Scripts/Tech3CSDK.meta b/Assets/PerfectWorld/Scripts/Tech3CSDK.meta
new file mode 100644
index 0000000000..06775562c2
--- /dev/null
+++ b/Assets/PerfectWorld/Scripts/Tech3CSDK.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 9cfabbaaddd233a4a9d4f07a3577d3a2
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/PerfectWorld/Scripts/Tech3CSDK/Tech3CSDKWrapper.cs b/Assets/PerfectWorld/Scripts/Tech3CSDK/Tech3CSDKWrapper.cs
new file mode 100644
index 0000000000..348554316c
--- /dev/null
+++ b/Assets/PerfectWorld/Scripts/Tech3CSDK/Tech3CSDKWrapper.cs
@@ -0,0 +1,208 @@
+using Tech3C;
+using BrewMonster;
+using System;
+using UnityEngine;
+
+namespace BrewMonster.Scripts
+{
+ public enum LOGIN_ERROR_CODE
+ {
+ SUCCESS = 0,
+ CANCELLED = -1
+ }
+
+ public class Tech3CSDKWrapper : Singleton
+ {
+ public string clientId = "tghh";
+ public string clientSecret = "1nBnWnUJadlqzlcd7x7uibXZwW9Bxx9h";
+
+ private AuthCallback authCallback;
+ private LogoutCallback logoutCallback;
+
+ ///
+ /// After Tech3C SDK returns result, this callback will be called.
+ /// (errorCode, userId, password)
+ ///
+ public Action onLoginCallback;
+ public Action onLogOutCallback;
+
+ public bool isInitialized = false;
+ public bool isProcessing = false; // true if the SDK is processing a request (login or logout)
+
+ #region private functions
+ protected override void Initialize()
+ {
+ base.Initialize();
+
+ SetupCallbacks();
+
+ // Auto-initialize SDK
+ if (!string.IsNullOrEmpty(clientId) && !string.IsNullOrEmpty(clientSecret))
+ {
+ BMLogger.Log($"Initializing Tech3C SDK...\n Client ID: {clientId}");
+ Tech3CSDK.Instance.Initialize(clientId, clientSecret);
+
+ if (Tech3CSDK.Instance.IsInitialized)
+ {
+ BMLogger.Log("[SDK] SDK Initialized Successfully!");
+ }
+ else
+ {
+ BMLogger.Log("[SDK] Failed to initialize SDK");
+ }
+ }
+ else
+ {
+ BMLogger.Log("[SDK] Client ID and/or Client Secret not set. Please configure in Inspector.");
+ }
+
+ BMLogger.Log("[SDK] Tech3C SDK Started");
+
+ isInitialized = true;
+ }
+
+ private void SetupCallbacks()
+ {
+ // Auth callback
+ authCallback = new AuthCallback();
+ authCallback.OnAuthSuccessEvent += OnAuthSuccessEvent;
+ authCallback.OnAuthCancelledEvent += OnAuthCancelledEvent;
+ authCallback.OnAuthErrorEvent += OnAuthErrorEvent;
+
+ // Logout callback
+ logoutCallback = new LogoutCallback();
+ logoutCallback.OnLogoutSuccessEvent += OnLogoutSuccessEvent;
+ logoutCallback.OnLogoutErrorEvent += OnLogoutErrorEvent;
+ }
+ #endregion
+
+ #region SDK Callbacks
+ private void OnAuthSuccessEvent(string userId, string password, string accessToken, string refreshToken, LoginType loginType, long expiryTime)
+ {
+ isProcessing = false;
+ BMLogger.Log($"[SDK] Auth Success!\n User ID: {userId}\n Password: {password}\n Login Type: {loginType}\n Token: {accessToken?.Substring(0, Mathf.Min(20, accessToken?.Length ?? 0))}...");
+ onLoginCallback?.Invoke((byte)LOGIN_ERROR_CODE.SUCCESS, userId, password);
+ }
+
+ private void OnAuthCancelledEvent()
+ {
+ isProcessing = false;
+ BMLogger.Log("[SDK] Auth Cancelled by user");
+ onLoginCallback?.Invoke((int)LOGIN_ERROR_CODE.CANCELLED, "", "");
+ }
+
+ private void OnAuthErrorEvent(int errorCode, string errorMessage)
+ {
+ isProcessing = false;
+ BMLogger.Log($"[SDK] Auth Error [{errorCode}]: {errorMessage}");
+ onLoginCallback?.Invoke(errorCode, errorMessage, string.Empty);
+ }
+
+ private void OnLogoutSuccessEvent()
+ {
+ isProcessing = false;
+ BMLogger.Log("[SDK] Logout Successful");
+ onLogOutCallback?.Invoke((int)LOGIN_ERROR_CODE.SUCCESS, "");
+ }
+
+ private void OnLogoutErrorEvent(int errorCode, string errorMessage)
+ {
+ isProcessing = false;
+ BMLogger.Log($"[SDK] Logout Error [{errorCode}]: {errorMessage}");
+ onLogOutCallback?.Invoke(errorCode, errorMessage);
+ }
+ #endregion
+
+
+ #region public functions
+
+ public void SetLoginCallback(Action callback)
+ {
+ if (callback != null)
+ {
+ BMLogger.LogWarning("[SDK] Login callback is already set, it will be overridden");
+ }
+ onLoginCallback = callback;
+ }
+
+ public void RemoveLoginCallback()
+ {
+ onLoginCallback = null;
+ }
+
+ public void SetLogoutCallback(Action callback)
+ {
+ if (callback != null)
+ {
+ BMLogger.LogWarning("[SDK] Logout callback is already set, it will be overridden");
+ }
+ onLogOutCallback = callback;
+ }
+
+ public void RemoveLogoutCallback()
+ {
+ onLogOutCallback = null;
+ }
+
+ ///
+ /// Show Tech3C SDK login screen.
+ /// Callback will be called after login success or failed.
+ ///
+ public bool Login()
+ {
+ if (!isInitialized)
+ {
+ BMLogger.LogError("[SDK] [Login] SDK is not initialized, please call Initialize() first");
+ return false;
+ }
+
+ if (isProcessing)
+ {
+ BMLogger.LogError("[SDK] [Login] SDK is already processing a request, please wait for the current request to complete");
+ return false;
+ }
+
+ if (onLoginCallback == null)
+ {
+ BMLogger.LogError("[SDK] [Login] Login callback is not set, please set it using SetLoginCallback");
+ return false;
+ }
+
+ isProcessing = true;
+
+ Tech3CSDK.Instance.ShowAuth(authCallback);
+ return true;
+ }
+
+ public bool Logout()
+ {
+ if (!isInitialized)
+ {
+ BMLogger.LogError("[SDK] [Logout] SDK is not initialized, please call Initialize() first");
+ return false;
+ }
+
+ if (isProcessing)
+ {
+ BMLogger.LogError("[SDK] [Logout] SDK is already processing a request, please wait for the current request to complete");
+ return false;
+ }
+
+
+ if (onLogOutCallback == null)
+ {
+ BMLogger.LogError("[SDK] [Logout] Logout callback is not set, please set it using SetLogoutCallback");
+ return false;
+ }
+
+ //TODO: Check again after 3C resolve the callback issue.
+ // isProcessing = true;
+
+ Tech3CSDK.Instance.Logout(logoutCallback);
+ return true;
+ }
+
+ #endregion
+
+ }
+}
\ No newline at end of file
diff --git a/Assets/PerfectWorld/Scripts/Tech3CSDK/Tech3CSDKWrapper.cs.meta b/Assets/PerfectWorld/Scripts/Tech3CSDK/Tech3CSDKWrapper.cs.meta
new file mode 100644
index 0000000000..07834551d0
--- /dev/null
+++ b/Assets/PerfectWorld/Scripts/Tech3CSDK/Tech3CSDKWrapper.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 61795692226b05849aec56bf168c905a
\ No newline at end of file
diff --git a/Assets/PerfectWorld/Scripts/UI/Action/CDlgSkillSubAction.cs b/Assets/PerfectWorld/Scripts/UI/Action/CDlgSkillSubAction.cs
index 7553bf9e76..1d518858fa 100644
--- a/Assets/PerfectWorld/Scripts/UI/Action/CDlgSkillSubAction.cs
+++ b/Assets/PerfectWorld/Scripts/UI/Action/CDlgSkillSubAction.cs
@@ -4,8 +4,6 @@ using System;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
-using UnityEngine.UI;
-using static UnityEditor.AddressableAssets.Build.Layout.BuildLayout;
namespace BrewMonster
{
diff --git a/Assets/PerfectWorld/Scripts/UI/CDlgMessageBox.cs b/Assets/PerfectWorld/Scripts/UI/CDlgMessageBox.cs
index 488f82b625..b75c1bb055 100644
--- a/Assets/PerfectWorld/Scripts/UI/CDlgMessageBox.cs
+++ b/Assets/PerfectWorld/Scripts/UI/CDlgMessageBox.cs
@@ -1,28 +1,80 @@
-using BrewMonster.UI;
+using System;
+using BrewMonster.UI;
using UnityEngine;
using static CECUIManager;
namespace BrewMonster
{
+ public enum MessageBoxType
+ {
+ YesButton,
+ NoButton,
+ BothYesNoButton,
+ }
+ public struct MessageBoxData
+ {
+ public string Title;
+ public string Message;
+ public AUIDialog Dlg;
+ public MessageBoxType MessageBoxType;
+ public Action OnClickedYes;
+ public Action OnClickedNo;
+ }
public class CDlgMessageBox : AUIDialog
{
[SerializeField] private TMPro.TextMeshProUGUI titleText;
[SerializeField] private TMPro.TextMeshProUGUI messageText;
[SerializeField] private UnityEngine.UI.Button okButton;
- private void Awake()
+ [SerializeField] private UnityEngine.UI.Button _noButton;
+
+ private MessageBoxData _messageData;
+
+ public override void OnEnable()
{
- okButton.onClick.RemoveAllListeners();
+ base.OnEnable();
okButton.onClick.AddListener(OnOkClicked);
+ _noButton.onClick.AddListener(OnNoClicked);
}
+
+ public override void OnDisable()
+ {
+ okButton.onClick.RemoveListener(OnOkClicked);
+ _noButton.onClick.RemoveListener(OnNoClicked);
+ }
+
private void OnOkClicked()
{
- EventBus.Publish(new MessageBoxEvent(1,this));
+ EventBus.Publish(new MessageBoxEvent(1,_messageData.Dlg));
+ _messageData.OnClickedYes?.Invoke();
Show(false);
}
- public void ShowMessageBox(string title, string message)
+ private void OnNoClicked()
{
- SetName(title);
- messageText.text = message;
+ EventBus.Publish(new MessageBoxEvent(0,_messageData.Dlg));
+ _messageData.OnClickedNo?.Invoke();
+ Show(false);
+ }
+ public void ShowMessageBox(MessageBoxData messageBoxData)
+ {
+ _messageData = messageBoxData;
+ SetName(messageBoxData.Title);
+ messageText.text = messageBoxData.Message;
+
+ okButton.gameObject.SetActive(false);
+ _noButton.gameObject.SetActive(false);
+ switch (_messageData.MessageBoxType)
+ {
+ case MessageBoxType.YesButton:
+ okButton.gameObject.SetActive(true);
+ break;
+ case MessageBoxType.NoButton:
+ _noButton.gameObject.SetActive(true);
+ break;
+ case MessageBoxType.BothYesNoButton:
+ okButton.gameObject.SetActive(true);
+ _noButton.gameObject.SetActive(true);
+ break;
+ }
Show(true);
}
}
diff --git a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgInstall.cs b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgInstall.cs
index b42dd8c65f..d76b053eb1 100644
--- a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgInstall.cs
+++ b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgInstall.cs
@@ -33,6 +33,7 @@ namespace BrewMonster
[SerializeField] private Button m_BtnCancel;
[SerializeField] private Sprite khung_item;
+ [SerializeField] private Transform itemInventoryRoot;
private EC_IvtrItem m_SelectedEquip;
private EC_IvtrItem m_SelectedMaterial;
@@ -50,12 +51,28 @@ namespace BrewMonster
RegisterClick(m_SlotSecondlParent, OnClickMaterialSlot);
}
+ public override void Update()
+ {
+#if UNITY_EDITOR || UNITY_STANDALONE
+ if (Input.GetMouseButtonDown(0))
+ {
+ CheckHidePanel(Input.mousePosition);
+ }
+#else
+ if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
+ {
+ CheckHidePanel(Input.GetTouch(0).position);
+ }
+#endif
+ }
+
public override void OnEnable()
{
base.OnEnable();
//todo need to set from other class
// SetName("Win_Enchase");
m_BtnMerge.onClick.AddListener(OnClickedMerge);
+ m_BtnCancel.onClick.AddListener(OnClickedCancel);
m_install_price = -1;
}
@@ -63,11 +80,7 @@ namespace BrewMonster
{
base.OnDisable();
m_BtnMerge.onClick.RemoveListener(OnClickedMerge);
- }
-
- public void OpenInstall(uint npcId)
- {
-
+ m_BtnCancel.onClick.RemoveListener(OnClickedCancel);
}
public void CloseInstall()
@@ -217,6 +230,12 @@ namespace BrewMonster
else
detailedItem.GetDetailDataFromLocal();
+ if (m_FirstInvSlot >= 0)
+ {
+ var previosBtn = FindInventoryButtonBySlot(m_FirstInvSlot);
+ SetInventorySlotGray(previosBtn, false);
+ }
+
m_SelectedEquip?.Freeze(false);
m_SelectedEquip = detailedItem;
m_FirstInvSlot = slotIndex;
@@ -227,8 +246,6 @@ namespace BrewMonster
SetInventorySlotGray(btn, true);
detailedItem.Freeze(true);
-
- Debug.Log($"[Install] Equipment: {detailedItem.m_tid} from slot {slotIndex}");
}
private void OnDropMaterial(PointerEventData eventData)
@@ -252,6 +269,12 @@ namespace BrewMonster
else
detailedItem.GetDetailDataFromLocal();
+ if (m_SecondInvSlot >= 0)
+ {
+ var previosBtn = FindInventoryButtonBySlot(m_SecondInvSlot);
+ SetInventorySlotGray(previosBtn, false);
+ }
+
m_SelectedMaterial?.Freeze(false);
m_SelectedMaterial = detailedItem;
m_SelectedMaterial?.Freeze(true);
@@ -297,8 +320,6 @@ namespace BrewMonster
// a_sprintf(szText, _AL("%d"), nAmount);
// m_pTxtGold->SetText(szText);
// }
-
- Debug.Log($"[Install] Material: {detailedItem.m_tid} from slot {slotIndex}");
}
private void SetInventorySlotGray(Button btn, bool gray)
@@ -343,23 +364,19 @@ namespace BrewMonster
private void ClearEquipSlot()
{
m_SelectedEquip?.Freeze(false);
- m_SelectedMaterial?.Freeze(false);
m_SelectedEquip = null;
m_FirstInvSlot = -1;
m_TxtFirstName.text = "___";
ClearSlotIcon(m_SlotFirstParent);
-
- Debug.Log("[Install] Equipment slot cleared");
}
private void ClearMaterialSlot()
{
+ m_SelectedMaterial?.Freeze(false);
m_SelectedMaterial = null;
m_SecondInvSlot = -1;
m_TxtSecondName.text = "___";
ClearSlotIcon(m_SlotSecondlParent);
-
- Debug.Log("[Install] Material slot cleared");
}
private void ClearSlotIcon(Transform slot)
@@ -385,8 +402,8 @@ namespace BrewMonster
if( nMoney > pHost.GetMoneyAmount() )
{
message = GetGameUIMan().GetStringFromTable(226);
- Debug.LogError(message);
- // GetGameUIMan()->MessageBox("", GetGameUIMan().GetStringFromTable(226), MB_OK,
+ CECUIManager.Instance.ShowMessageBox("", message);
+ // GetGameUIMan().GetDialog("")
// A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox);
// pMsgBox->SetLife(3);
return;
@@ -396,7 +413,7 @@ namespace BrewMonster
if( !pIvtrA.IsEquipment() )
{
message = GetGameUIMan().GetStringFromTable(223);
- Debug.LogError(message);
+ CECUIManager.Instance.ShowMessageBox("", message);
// GetGameUIMan().MessageBox("", GetGameUIMan().GetStringFromTable(223), MB_OK,
// A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox);
// pMsgBox.SetLife(3);
@@ -410,7 +427,7 @@ namespace BrewMonster
if( pEquipA.GetEmptyHoleNum() <= 0 )
{
message = GetGameUIMan().GetStringFromTable(224);
- Debug.LogError(message);
+ CECUIManager.Instance.ShowMessageBox("", message);
// GetGameUIMan().MessageBox("", GetGameUIMan()->GetStringFromTable(224), MB_OK,
// A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox);
// pMsgBox.SetLife(3);
@@ -421,7 +438,7 @@ namespace BrewMonster
if(pIvtrB == null || !pIvtrB.IsEmbeddable() )
{
message = GetGameUIMan().GetStringFromTable(225);
- Debug.LogError(message);
+ CECUIManager.Instance.ShowMessageBox("", message);
// GetGameUIMan().MessageBox("", GetGameUIMan().GetStringFromTable(225), MB_OK,
// A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox);
// pMsgBox.SetLife(3);
@@ -449,7 +466,7 @@ namespace BrewMonster
if( nStoneLevel > nEquipLevel )
{
message = GetGameUIMan().GetStringFromTable(300);
- Debug.LogError(message);
+ CECUIManager.Instance.ShowMessageBox("", message);
// GetGameUIMan().MessageBox("", GetGameUIMan().GetStringFromTable(300), MB_OK,
// A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox);
// pMsgBox.SetLife(3);
@@ -464,7 +481,7 @@ namespace BrewMonster
pHost.GetPack(InventoryConst.IVTRTYPE_PACK).UnfreezeAllItems();
message = GetGameUIMan().GetStringFromTable(228);
- Debug.LogError(message);
+ CECUIManager.Instance.ShowMessageBox("", message);
// GetGameUIMan().MessageBox("", GetGameUIMan().GetStringFromTable(228),
// MB_OK, A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox);
// pMsgBox.SetLife(3);
@@ -484,5 +501,41 @@ namespace BrewMonster
// MB_OKCANCEL, A3DCOLORRGBA(255, 255, 255, 160));
// }
}
+ private void OnClickedCancel()
+ {
+ Show(false);
+ }
+
+ public void ResetInstallUI()
+ {
+ RestoreInventoryColors();
+
+ m_SelectedEquip = null;
+ m_SelectedMaterial = null;
+
+ m_FirstInvSlot = -1;
+ m_SecondInvSlot = -1;
+
+ m_TxtFirstName.text = "___";
+ m_TxtSecondName.text = "___";
+ m_TxtMoney.text = "0";
+ m_install_price = -1;
+
+ ClearSlotIcon(m_SlotFirstParent);
+ ClearSlotIcon(m_SlotSecondlParent);
+
+ gameObject.SetActive(false);
+ }
+
+ private void CheckHidePanel(Vector2 screenPos)
+ {
+ if (!RectTransformUtility.RectangleContainsScreenPoint(
+ itemInventoryRoot as RectTransform, screenPos,
+ Camera.main))
+ {
+ if(itemInventoryRoot!=null)
+ itemInventoryRoot.gameObject.SetActive(false);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs
index a546e83502..ab8369e45a 100644
--- a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs
+++ b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs
@@ -3406,27 +3406,9 @@ namespace BrewMonster.UI
if (pCurNPCEssence.HasValue)
{
uint npcID = pCurNPCEssence.Value.id;
- DlgInstall dlgInstall = FindFirstObjectByType();
- if (dlgInstall == null)
- {
- CECGameUIMan gameUIMan = GetGameUIMan();
- DialogScriptTableObject dialogResource = gameUIMan.GetDialogResource();
- Canvas canvas = gameUIMan.GetCanvas();
-
- if(dialogResource != null && canvas != null)
- {
- GameObject ob = dialogResource.GetPrefabDialog("Win_Enchase");
- if (ob != null)
- {
- dlgInstall = GameObject.Instantiate(ob, canvas.transform).GetComponent();
- }
- }
- }
-
- if(dlgInstall != null)
- {
- dlgInstall.OpenInstall(npcID);
- }
+
+ var dlgInstall =GetGameUIMan().GetDialog("Win_Enchase");
+ dlgInstall.Show(true);
}
//pShow1 = m_pAUIManager.GetDialog("Win_Enchase");
//pShow2 = m_pAUIManager.GetDialog("Win_Inventory");
diff --git a/Assets/PerfectWorld/Scripts/UI/Inventory/CECSCItem.cs b/Assets/PerfectWorld/Scripts/UI/Inventory/CECSCItem.cs
index 8ab2bba4c8..8101130b87 100644
--- a/Assets/PerfectWorld/Scripts/UI/Inventory/CECSCItem.cs
+++ b/Assets/PerfectWorld/Scripts/UI/Inventory/CECSCItem.cs
@@ -1,15 +1,5 @@
using BrewMonster.Network;
-using BrewMonster.Scripts.Skills;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Unity.VisualScripting;
-using static BrewMonster.SkillArrayWrapper;
-using UnityEngine;
using BrewMonster.Scripts.Managers;
-using UnityEditorInternal.Profiling.Memory.Experimental;
using BrewMonster.Scripts;
namespace BrewMonster
diff --git a/Assets/PerfectWorld/Scripts/UI/Login/BtnBackToSelectRole.cs b/Assets/PerfectWorld/Scripts/UI/Login/BtnBackToSelectRole.cs
new file mode 100644
index 0000000000..59d081a721
--- /dev/null
+++ b/Assets/PerfectWorld/Scripts/UI/Login/BtnBackToSelectRole.cs
@@ -0,0 +1,17 @@
+using UnityEngine;
+using BrewMonster.Network;
+
+namespace BrewMonster.UI
+{
+ ///
+ /// Attach to the in-game button: sends LOGOUT(1) and returns to Select Role.
+ ///
+ public class BtnBackToSelectRole : MonoBehaviour
+ {
+ public void OnClick()
+ {
+ UnityGameSession.ReturnToSelectRole();
+ }
+ }
+}
+
diff --git a/Assets/PerfectWorld/Scripts/UI/Login/BtnBackToSelectRole.cs.meta b/Assets/PerfectWorld/Scripts/UI/Login/BtnBackToSelectRole.cs.meta
new file mode 100644
index 0000000000..b500247166
--- /dev/null
+++ b/Assets/PerfectWorld/Scripts/UI/Login/BtnBackToSelectRole.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: f9aed6caec10d2a44a39e3b9c458c5c5
\ No newline at end of file
diff --git a/Assets/PerfectWorld/Scripts/UI/Login/BtnLogoutAccount.cs b/Assets/PerfectWorld/Scripts/UI/Login/BtnLogoutAccount.cs
new file mode 100644
index 0000000000..53dcf51254
--- /dev/null
+++ b/Assets/PerfectWorld/Scripts/UI/Login/BtnLogoutAccount.cs
@@ -0,0 +1,17 @@
+using UnityEngine;
+using BrewMonster.Network;
+
+namespace BrewMonster.UI
+{
+ ///
+ /// Attach to the Select Role screen button: sends LOGOUT(0) and returns to Login UI.
+ ///
+ public class BtnLogoutAccount : MonoBehaviour
+ {
+ public void OnClick()
+ {
+ UnityGameSession.LogoutAccount();
+ }
+ }
+}
+
diff --git a/Assets/PerfectWorld/Scripts/UI/Login/BtnLogoutAccount.cs.meta b/Assets/PerfectWorld/Scripts/UI/Login/BtnLogoutAccount.cs.meta
new file mode 100644
index 0000000000..fec91d3246
--- /dev/null
+++ b/Assets/PerfectWorld/Scripts/UI/Login/BtnLogoutAccount.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 725d0d91266148944894d6c831bf2650
\ No newline at end of file
diff --git a/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs b/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs
index 559b51cac9..ee6024729e 100644
--- a/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs
+++ b/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using BrewMonster.Network;
+using BrewMonster.Scripts;
using CSNetwork.Protocols;
using CSNetwork.Protocols.RPCData;
using TMPro;
@@ -29,19 +30,51 @@ namespace BrewMonster.UI
private List _roleInfos;
private List _currentRoles;
private RoleInfo _pendingCreatedRole;
+ private bool _loginInProgress;
bool isDoneWorldRender = false;
bool isDoneNPCRender = false;
private SynchronizationContext context;
public AudioClip loginBGM;
+
+ void Awake()
+ {
+ // Ensure wrapper created early (Tech3C SDK).
+ _ = Tech3CSDKWrapper.Instance;
+ }
+
+ void OnEnable()
+ {
+ Tech3CSDKWrapper.Instance.SetLoginCallback(OnLoginCallback);
+ Tech3CSDKWrapper.Instance.SetLogoutCallback(OnLogoutCallback);
+ }
+
+ private void OnDisable()
+ {
+ Tech3CSDKWrapper.Instance.RemoveLoginCallback();
+ Tech3CSDKWrapper.Instance.RemoveLogoutCallback();
+ }
+
void Start()
{
AudioManager.Instance.PlayBGM(loginBGM, 1.5f);
_loginButton.onClick.AddListener(OnLoginButtonClicked);
context = SynchronizationContext.Current;
+ // Requirement: Login UI should also have a61 loaded.
+ var world = SceneManager.GetSceneByName("a61");
+ if (!world.IsValid() || !world.isLoaded)
+ {
+ SceneManager.LoadSceneAsync("a61", LoadSceneMode.Additive);
+ }
+
_usernameInputField.text = PlayerPrefs.GetString("username", "");
_passwordInputField.text = PlayerPrefs.GetString("password", "");
-
+
+ // Default: login UI first, select-role hidden until login succeeds.
+ if (_selectCharacterScreen != null)
+ _selectCharacterScreen.gameObject.SetActive(false);
+
+ ApplyLoginEntry(LogoutFlowState.ConsumeNextLoginEntry());
}
// Update is called once per frame
@@ -69,18 +102,86 @@ namespace BrewMonster.UI
#endif
}
+
public async void OnLoginButtonClicked()
{
+ if (_loginInProgress)
+ {
+ BMLogger.LogWarning("[LoginScreenUI] Login already in progress (ignored click).");
+ return;
+ }
+
+ _loginInProgress = true;
+ if (_loginButton != null) _loginButton.interactable = false;
+
+ // If username or password is empty, use Tech3C SDK login UI.
+ if (string.IsNullOrEmpty(_usernameInputField.text) || string.IsNullOrEmpty(_passwordInputField.text))
+ {
+ // Use Tech3C SDK login UI.
+ bool started = Tech3CSDKWrapper.Instance.Login();
+ if (!started)
+ {
+ // Fallback: manual username/password login (useful in dev if SDK not configured).
+ BMLogger.LogWarning("[LoginScreenUI] Tech3CSDKWrapper.Login() failed, fallback to manual login.");
+ await BeginGameLoginAsync(_usernameInputField.text, _passwordInputField.text);
+ }
+ }
+ else
+ {
+ // otherwise use manual username/password login.
+ BMLogger.LogError("[LoginScreenUI] Username/password empty.");
+ await BeginGameLoginAsync(_usernameInputField.text, _passwordInputField.text);
+ }
+
+ }
+
+ private async Task BeginGameLoginAsync(string username, string password)
+ {
+ if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
+ {
+ BMLogger.LogError("[LoginScreenUI] Username/password empty.");
+ _loginInProgress = false;
+ if (_loginButton != null) _loginButton.interactable = true;
+ return;
+ }
+
BMLogger.Log("OnLoginButtonClicked");
- string username = _usernameInputField.text;
- string password = _passwordInputField.text;
- // UnityGameSession.SetConnectionInfo("103.182.22.52", 29000);
UnityGameSession.SetConnectionInfo("103.51.120.195", 29000);
PlayerPrefs.SetString("username", username);
PlayerPrefs.SetString("password", password);
PlayerPrefs.Save();
+
+ BMLogger.Log($"[LoginScreenUI] Connecting+login start user='{username}'");
await UnityGameSession.Login(username, password, OnLoginComplete);
- _selectCharacterScreen.gameObject.SetActive(true);
+ }
+
+ ///
+ /// Apply how LoginScene should look after a logout flow.
+ /// Call this when LoginScene is already loaded (additive) and you need to switch UI without reloading the scene.
+ ///
+ public void ApplyLoginEntry(BrewMonster.Network.LogoutFlowState.LoginEntryTarget entry)
+ {
+ _loginInProgress = false;
+ if (_loginButton != null) _loginButton.interactable = true;
+
+ // Always refresh fields from PlayerPrefs (LogoutAccount clears them).
+ if (_usernameInputField != null) _usernameInputField.text = PlayerPrefs.GetString("username", "");
+ if (_passwordInputField != null) _passwordInputField.text = PlayerPrefs.GetString("password", "");
+
+ if (_selectCharacterScreen != null)
+ _selectCharacterScreen.gameObject.SetActive(false);
+
+ if (entry == BrewMonster.Network.LogoutFlowState.LoginEntryTarget.SelectRole)
+ {
+ // Auto-login to reach Select Role like the original client, without showing Tech3C auth UI again.
+ if (!string.IsNullOrEmpty(_usernameInputField.text) && !string.IsNullOrEmpty(_passwordInputField.text))
+ {
+ BMLogger.Log("[LoginScreenUI] Auto-login triggered (return-to-select-role).");
+ _loginInProgress = true;
+ if (_loginButton != null) _loginButton.interactable = false;
+ _ = BeginGameLoginAsync(_usernameInputField.text, _passwordInputField.text);
+ }
+ }
}
///
@@ -89,12 +190,19 @@ namespace BrewMonster.UI
///
private void OnLoginComplete(bool result)
{
+ BMLogger.Log($"[LoginScreenUI] OnLoginComplete result={result}");
if (!result)
{
BMLogger.LogError("Login failed");
+ if (_selectCharacterScreen != null)
+ _selectCharacterScreen.gameObject.SetActive(false);
+ _loginInProgress = false;
+ if (_loginButton != null) _loginButton.interactable = true;
return;
}
+ if (_selectCharacterScreen != null)
+ _selectCharacterScreen.gameObject.SetActive(true);
UnityGameSession.GetRoleListAsync(OnGetRoleListComplete);
}
@@ -108,6 +216,8 @@ namespace BrewMonster.UI
{
BMLogger.LogError("OnGetRoleListComplete: roleInfos is null");
// Keep whatever is currently shown; don't overwrite UI state with null.
+ _loginInProgress = false;
+ if (_loginButton != null) _loginButton.interactable = true;
return;
}
@@ -142,6 +252,9 @@ namespace BrewMonster.UI
BMLogger.Log($"OnGetRoleListComplete: roles={roleInfos.Count}");
_roleInfos = roleInfos;
_currentRoles = roleInfos;
+
+ // Login flow finished; keep login button disabled (origin-like) once you're at select role.
+ _loginInProgress = false;
}
private void OnClickSelectCharacter(RoleInfo roleInfo)
@@ -278,5 +391,38 @@ namespace BrewMonster.UI
}
}
#endif
+
+ private async void OnLoginCallback(int errorCode, string userId, string password)
+ {
+ if (errorCode == 0)
+ {
+ BMLogger.Log($"Login success -- userId: {userId} - {password}");
+ // UnityGameSession.SetConnectionInfo("103.182.22.52", 29000);
+ UnityGameSession.SetConnectionInfo("103.51.120.195", 29000);
+ PlayerPrefs.SetString("username", userId);
+ PlayerPrefs.SetString("password", password);
+ PlayerPrefs.Save();
+ await BeginGameLoginAsync(userId, password);
+ }
+ else
+ {
+ // if it failed, the userId will be the error message
+ BMLogger.LogError($"Login failed -- errorCode: {errorCode}: {userId}");
+ _loginInProgress = false;
+ if (_loginButton != null) _loginButton.interactable = true;
+ }
+ }
+
+ private void OnLogoutCallback(int errorCode, string errorMessage)
+ {
+ if (errorCode == 0)
+ {
+ BMLogger.Log("Logout success");
+ }
+ else
+ {
+ BMLogger.LogError($"Logout failed -- errorCode: {errorCode}: {errorMessage}");
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Assets/Plugins/Android.meta b/Assets/Plugins/Android.meta
new file mode 100644
index 0000000000..ba484d202e
--- /dev/null
+++ b/Assets/Plugins/Android.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f45408c4c89289d498475d0e1e1e5b7e
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Android/AndroidManifest.xml b/Assets/Plugins/Android/AndroidManifest.xml
new file mode 100644
index 0000000000..783069970e
--- /dev/null
+++ b/Assets/Plugins/Android/AndroidManifest.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Assets/Plugins/Android/AndroidManifest.xml.meta b/Assets/Plugins/Android/AndroidManifest.xml.meta
new file mode 100644
index 0000000000..bfa9b92c0a
--- /dev/null
+++ b/Assets/Plugins/Android/AndroidManifest.xml.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 11f5d7426047d6048a47d330b02103d9
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Android/baseProjectTemplate.gradle.DISABLED b/Assets/Plugins/Android/baseProjectTemplate.gradle.DISABLED
new file mode 100644
index 0000000000..fd43460978
--- /dev/null
+++ b/Assets/Plugins/Android/baseProjectTemplate.gradle.DISABLED
@@ -0,0 +1,13 @@
+plugins {
+ // If you are changing the Android Gradle Plugin version, make sure it is compatible with the Gradle version preinstalled with Unity
+ // See which Gradle version is preinstalled with Unity here https://docs.unity3d.com/Manual/android-gradle-overview.html
+ // See official Gradle and Android Gradle Plugin compatibility table here https://developer.android.com/studio/releases/gradle-plugin#updating-gradle
+ // To specify a custom Gradle version in Unity, go do "Preferences > External Tools", uncheck "Gradle Installed with Unity (recommended)" and specify a path to a custom Gradle version
+ id 'com.android.application' version '7.4.2' apply false
+ id 'com.android.library' version '7.4.2' apply false
+ **BUILD_SCRIPT_DEPS**
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Assets/Plugins/Android/baseProjectTemplate.gradle.DISABLED.meta b/Assets/Plugins/Android/baseProjectTemplate.gradle.DISABLED.meta
new file mode 100644
index 0000000000..f58f05f816
--- /dev/null
+++ b/Assets/Plugins/Android/baseProjectTemplate.gradle.DISABLED.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 7736ab544ec5cab4a89af84ac45b5a75
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Android/mainTemplate.gradle b/Assets/Plugins/Android/mainTemplate.gradle
new file mode 100644
index 0000000000..323c9d1038
--- /dev/null
+++ b/Assets/Plugins/Android/mainTemplate.gradle
@@ -0,0 +1,51 @@
+apply plugin: 'com.android.library'
+apply from: '../shared/keepUnitySymbols.gradle'
+**APPLY_PLUGINS**
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "androidx.appcompat:appcompat:1.6.1"
+ implementation "com.google.android.material:material:1.11.0"
+**DEPS**}
+
+android {
+ ndkVersion "**NDKVERSION**"
+ namespace "com.unity3d.player"
+ ndkPath "**NDKPATH**"
+ compileSdk **APIVERSION**
+ buildToolsVersion '**BUILDTOOLS**'
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_11
+ targetCompatibility JavaVersion.VERSION_11
+ }
+
+ buildFeatures {
+ viewBinding true
+ }
+
+ defaultConfig {
+**DEFAULT_CONFIG_SETUP**
+ minSdk **MINSDK**
+ targetSdk **TARGETSDK**
+ ndk {
+ debugSymbolLevel **DEBUGSYMBOLLEVEL**
+ abiFilters **ABIFILTERS**
+ }
+ versionCode **VERSIONCODE**
+ versionName '**VERSIONNAME**'
+ consumerProguardFiles 'proguard-unity.txt'**USER_PROGUARD**
+ }
+
+ lint {
+ abortOnError false
+ }
+
+ androidResources {
+ noCompress = **BUILTIN_NOCOMPRESS** + unityStreamingAssets.tokenize(', ')
+ ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~"
+ }**PACKAGING**
+}
+**IL_CPP_BUILD_SETUP**
+**SOURCE_BUILD_SETUP**
+**EXTERNAL_SOURCES**
diff --git a/Assets/Plugins/Android/mainTemplate.gradle.backup b/Assets/Plugins/Android/mainTemplate.gradle.backup
new file mode 100644
index 0000000000..c5203ee0d7
--- /dev/null
+++ b/Assets/Plugins/Android/mainTemplate.gradle.backup
@@ -0,0 +1,47 @@
+apply plugin: 'com.android.library'
+**APPLY_PLUGINS**
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "androidx.appcompat:appcompat:1.6.1"
+ implementation "com.google.android.material:material:1.11.0"
+**DEPS**}
+
+android {
+ namespace "com.unity3d.player"
+ ndkPath "**NDKPATH**"
+ compileSdkVersion **APIVERSION**
+ buildToolsVersion '**BUILDTOOLS**'
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_11
+ targetCompatibility JavaVersion.VERSION_11
+ }
+
+ buildFeatures {
+ viewBinding true
+ }
+
+ defaultConfig {
+ minSdkVersion **MINSDKVERSION**
+ targetSdkVersion **TARGETSDKVERSION**
+ ndk {
+ abiFilters **ABIFILTERS**
+ }
+ versionCode **VERSIONCODE**
+ versionName '**VERSIONNAME**'
+ consumerProguardFiles 'proguard-unity.txt'**USER_PROGUARD**
+ }
+
+ lintOptions {
+ abortOnError false
+ }
+
+ aaptOptions {
+ noCompress = **BUILTIN_NOCOMPRESS** + unityStreamingAssets.tokenize(', ')
+ ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~"
+ }**PACKAGING_OPTIONS**
+}
+**IL_CPP_BUILD_SETUP**
+**SOURCE_BUILD_SETUP**
+**EXTERNAL_SOURCES**
diff --git a/Assets/Plugins/Android/mainTemplate.gradle.backup.meta b/Assets/Plugins/Android/mainTemplate.gradle.backup.meta
new file mode 100644
index 0000000000..db9977c019
--- /dev/null
+++ b/Assets/Plugins/Android/mainTemplate.gradle.backup.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: f3bedb9070cd041a2bb8a27d9751aada
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Android/mainTemplate.gradle.meta b/Assets/Plugins/Android/mainTemplate.gradle.meta
new file mode 100644
index 0000000000..ff480e90c1
--- /dev/null
+++ b/Assets/Plugins/Android/mainTemplate.gradle.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 950fdc575e5b3b74ea1d3f1c60322d4f
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Prefabs/UI/DlgInstall.prefab b/Assets/Prefabs/UI/DlgInstall.prefab
index bed021b314..04a23593d8 100644
--- a/Assets/Prefabs/UI/DlgInstall.prefab
+++ b/Assets/Prefabs/UI/DlgInstall.prefab
@@ -1660,7 +1660,7 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
- m_text: 10000000000000
+ m_text: 0
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2}
m_sharedMaterial: {fileID: 9092487103257209053, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2}
@@ -10577,6 +10577,7 @@ MonoBehaviour:
m_BtnMerge: {fileID: 8208092408021918524}
m_BtnCancel: {fileID: 4503836757578509720}
khung_item: {fileID: 21300000, guid: a5366f3bce011c046902e39b6bd3a077, type: 3}
+ itemInventoryRoot: {fileID: 7750009739432212686}
--- !u!1 &5641506892578507279
GameObject:
m_ObjectHideFlags: 0
@@ -14989,7 +14990,7 @@ MonoBehaviour:
m_Calls:
- m_Target: {fileID: 2206910701173095729}
m_TargetAssemblyTypeName: BrewMonster.DlgInstall, Assembly-CSharp
- m_MethodName: CloseInstall
+ m_MethodName: ResetInstallUI
m_Mode: 1
m_Arguments:
m_ObjectArgument: {fileID: 0}
diff --git a/Assets/Prefabs/UI/SelectCharacterUI.prefab b/Assets/Prefabs/UI/SelectCharacterUI.prefab
index dc077c82ca..6309ad986a 100644
--- a/Assets/Prefabs/UI/SelectCharacterUI.prefab
+++ b/Assets/Prefabs/UI/SelectCharacterUI.prefab
@@ -142,7 +142,7 @@ RectTransform:
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0.0009808608, y: -0.00012207031}
- m_SizeDelta: {x: 0, y: 0}
+ m_SizeDelta: {x: -474.1602, y: -682.9546}
m_Pivot: {x: 0, y: 1}
--- !u!114 &8239567059120679520
MonoBehaviour:
@@ -504,7 +504,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
- m_IsActive: 1
+ m_IsActive: 0
--- !u!224 &5968911800527563993
RectTransform:
m_ObjectHideFlags: 0
@@ -580,7 +580,7 @@ MonoBehaviour:
characterItemPrefab: {fileID: 5263746738484752443, guid: 726ee9eade6587245ac1b55d2335e9b9, type: 3}
addCharacterItemPrefab: {fileID: 5263746738484752443, guid: a0b31ed0940ec9942b243a0b8cec8ad3, type: 3}
parentItems: {fileID: 2643174602035272289}
- createCharacterButton: {fileID: 0}
+ createCharacterButton: {fileID: 2685968509838782728}
createCharacterScreen: {fileID: 0}
--- !u!1 &7510180475820570348
GameObject:
@@ -594,6 +594,7 @@ GameObject:
- component: {fileID: 8869589995078313987}
- component: {fileID: 1038588709929429120}
- component: {fileID: 1166159884785039946}
+ - component: {fileID: 1481527591792018053}
m_Layer: 5
m_Name: back_btn
m_TagString: Untagged
@@ -701,7 +702,31 @@ MonoBehaviour:
m_TargetGraphic: {fileID: 1038588709929429120}
m_OnClick:
m_PersistentCalls:
- m_Calls: []
+ m_Calls:
+ - m_Target: {fileID: 1481527591792018053}
+ m_TargetAssemblyTypeName: BrewMonster.UI.BtnLogoutAccount, Assembly-CSharp
+ m_MethodName: OnClick
+ m_Mode: 1
+ m_Arguments:
+ m_ObjectArgument: {fileID: 0}
+ m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
+ m_IntArgument: 0
+ m_FloatArgument: 0
+ m_StringArgument:
+ m_BoolArgument: 0
+ m_CallState: 2
+--- !u!114 &1481527591792018053
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7510180475820570348}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 725d0d91266148944894d6c831bf2650, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
--- !u!1 &7885375190050319286
GameObject:
m_ObjectHideFlags: 0
diff --git a/Assets/Scripts/CECHostPlayer.cs b/Assets/Scripts/CECHostPlayer.cs
index 0921735cee..14f00ce172 100644
--- a/Assets/Scripts/CECHostPlayer.cs
+++ b/Assets/Scripts/CECHostPlayer.cs
@@ -802,19 +802,26 @@ namespace BrewMonster
if (pCmd.cooldown_index == (int)CoolTimeIndex.GP_CT_CAST_ELF_SKILL)
{
int i;
- // other goblin skills should be set public cool down, 1 second
- /* for (i = 0; i < m_aGoblinSkills.GetSize(); i++)
- {
- if (m_aGoblinSkills[i] && m_aGoblinSkills[i]->GetCoolingCnt() == 0)
- m_aGoblinSkills[i]->StartCooling(GetCoolTime(GP_CT_CAST_ELF_SKILL), GetCoolTime(GP_CT_CAST_ELF_SKILL));
- }*/
+ for (i = 0; i < m_aGoblinSkills.Count; i++)
+ {
+ if (m_aGoblinSkills[i] != null && m_aGoblinSkills[i].GetCoolingCnt() == 0)
+ {
+ int fakeRef = 0;
+ int coolTime = GetCoolTime((int)CoolTimeIndex.GP_CT_CAST_ELF_SKILL, out fakeRef);
+ m_aGoblinSkills[i].StartCooling(coolTime, coolTime);
+ }
+ }
- /* for (i = 0; i < m_aPsSkills.Count; i++)
- {
- CECSkill pSkill = GetPassiveSkillByIndex(i);
- if (pSkill && (pSkill->GetCommonCoolDown() & (1 << (pCmd->cooldown_index - GP_CT_SKILLCOMMONCOOLDOWN0))))
- pSkill->StartCooling(GetCoolTime(pCmd->cooldown_index), GetCoolTime(pCmd->cooldown_index));
- }*/
+ for (i = 0; i < m_aPsSkills.Count; i++)
+ {
+ CECSkill pSkill = GetPassiveSkillByIndex(i);
+ if (pSkill != null && (pSkill.GetCommonCoolDown() & (1 << (pCmd.cooldown_index - (int)CoolTimeIndex.GP_CT_SKILLCOMMONCOOLDOWN0))) != 0)
+ {
+ int fakeRef = 0;
+ int coolTime = GetCoolTime(pCmd.cooldown_time, out fakeRef);
+ pSkill.StartCooling(coolTime, coolTime);
+ }
+ }
}
if (pCmd.cooldown_index >= (int)CoolTimeIndex.GP_CT_SKILLCOMMONCOOLDOWN0 && pCmd.cooldown_index <= (int)CoolTimeIndex.GP_CT_SKILLCOMMONCOOLDOWN4)
@@ -826,7 +833,11 @@ namespace BrewMonster
CECSkill pSkill = GetPositiveSkillByIndex(i);
int fakeRef = 0;
if (pSkill != null && (pSkill.GetCommonCoolDown() & mask) != 0)
- pSkill.StartCooling(GetCoolTime(pCmd.cooldown_index, out fakeRef), GetCoolTime(pCmd.cooldown_index, out fakeRef));
+ {
+ int coolTime = GetCoolTime(pCmd.cooldown_index, out fakeRef);
+ pSkill.StartCooling(coolTime, coolTime);
+ //pSkill.StartCooling(GetCoolTime(pCmd.cooldown_index, out fakeRef), GetCoolTime(pCmd.cooldown_index, out fakeRef));
+ }
}
/*const std::map&inherentSkillMap = CECComboSkillState::Instance().GetInherentSkillMap();
std::map < unsigned int, CECSkill*>::const_iterator it;
@@ -849,7 +860,9 @@ namespace BrewMonster
}
ct.iCurTime = pCmd.cooldown_time;
ct.iMaxTime = pCmd.cooldown_time;
- Math.Clamp(ct.iCurTime, 0, ct.iMaxTime);
+ ct.iCurTime = Math.Clamp(ct.iCurTime, 0, ct.iMaxTime);
+ m_skillCoolTime[idSkill] = ct;
+ //Math.Clamp(ct.iCurTime, 0, ct.iMaxTime);
CECSkill pSkill = GetNormalSkill(idSkill);
if (pSkill != null)
@@ -864,6 +877,18 @@ namespace BrewMonster
{
BMLogger.LogError("HoangDev: pSkill " + pSkill);
}
+ else
+ {
+ pSkill = CECComboSkillState.Instance.GetInherentSkillByID((uint)idSkill);
+ if (pSkill != null)
+ {
+ pSkill.StartCooling(pCmd.cooldown_time, pCmd.cooldown_time);
+ }
+ else if (GetEquipSkillByID(idSkill) == null)
+ {
+ Debug.LogWarning($"OnMsgHstSetCoolTime: Skill {idSkill} not found in nomal/equip skills");
+ }
+ }
}
else
{
@@ -874,6 +899,11 @@ namespace BrewMonster
UpdateEquipSkillCoolDown(pCmd.cooldown_index);
}
+ private CECSkill GetPassiveSkillByIndex(int n)
+ {
+ return m_aPsSkills[n];
+ }
+
private void OnMsgHstCoolTimeData(ECMSG Msg)
{
cmd_cooltime_data pCmd = default;
@@ -7493,16 +7523,97 @@ namespace BrewMonster
CECGameRun pGameRun = EC_Game.GetGameRun();
//CECGameSession pSession = g_pGame.GetGameSession();
+ if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_FIREWORK)
+ {
+ if (GetProfession() == (int)PROFESSION.PROF_GHOST && IsInvisible())
+ {
+ if (showMsg)
+ pGameRun.AddFixedMessage((int)FixedMsg.FIXMSG_CANNOT_USE_WHEN_INVISIBLE);
+ return false;
+ }
+ }
+
+ if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_INCSKILLABILITY)
+ {
+ EC_IvtrIncSkillAbility pIncSkill = pItem as EC_IvtrIncSkillAbility;
+ //if (pIncSkill != null)
+ //{
+ // var pDBEssence = pIncSkill.GetDBEssence();
+ // CECSkill pSkill = GetNormalSkill(pDBEssence.id_skill);
+ // if (pSkill != null)
+ // {
+ // if (pSkill.GetSkillLevel() != pDBEssence.level_required)
+ // {
+ // if (showMsg)
+ // pGameRun.AddFixedMessage((int)FixedMsg.FIXMSG_PRODUCE_LEVEL_INVALID);
+ // return false;
+ // }
+ // if (GetSkillAbilityPercent(pDBEssence.id_skill) >= 100)
+ // {
+ // if (showMsg)
+ // pGameRun.AddFixedMessage((int)FixedMsg.FIXMSG_PRODUCE_ABILITY_FULL);
+ // return false;
+ // }
+ // }
+ //}
+ }
+
if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_TRANSMITSCROLL)
{
CECGameUIMan pGameUI = pGameRun.GetUIManager().GetInGameUIMan();
if (pGameUI != null && !IsFighting())
{
// TODO: Implement travel map dialog
+ //CDlgWorldMap* pMap = (CDlgWorldMap*)pGameUI->GetDialog("Win_WorldMapTravel");
+ //pMap->BuildTravelMap(DT_TRANSMITSCROLL_ESSENCE, (void*)iSlot);
+ //pMap->Show(true);
}
return true;
}
+ if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_SHOPTOKEN)
+ {
+ CECGameUIMan pGameUI = pGameRun.GetUIManager().GetInGameUIMan();
+ if (pGameUI != null && !IsFighting())
+ {
+ //CDlgTokenShop* pDlg = dynamic_cast(pGameUI->GetDialog("Win_TokenShop"));
+ //if (pDlg)
+ //{
+ // pDlg->InitTokenShopItem(pItem->GetTemplateID());
+ // pDlg->Show(!pDlg->IsShow());
+ //}
+ }
+ return true;
+ }
+
+ if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_UNIVERSAL_TOKEN)
+ {
+ // TODO: Implement universal token when available
+ EC_IvtrUniversalToken pUniversalToken = pItem as EC_IvtrUniversalToken;
+ //if (pUniversalToken != null && pUniversalToken.HasAnyUsage())
+ //{
+ // CECUseUniversalTokenCommandManager.Instance.Use(pUniversalToken, pUniversalToken.UsageIndexAt(0));
+ // return true;
+ //}
+ }
+
+ if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_TASKDICE)
+ {
+ EC_IvtrTaskDice pTaskDice = pItem as EC_IvtrTaskDice;
+ if (pTaskDice != null)
+ {
+ if (pTaskDice != null)
+ {
+ if (IsFlying() && pTaskDice.GetDBEssence().no_use_in_combat == 1)
+ {
+ if(showMsg)
+ pGameRun.AddFixedMessage((int)FixedMsg.FIXMSG_CANNOT_USE_IN_BATTLE);
+ return false;
+ }
+ }
+ }
+ }
+
if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_TARGETITEM)
{
EC_IvtrTargetItem pTargetItem = pItem as EC_IvtrTargetItem;
@@ -7521,7 +7632,7 @@ namespace BrewMonster
return false;
}
- if (pTargetItem.GetDBEssence().use_in_sanctuary_only != 0 && !IsInSanctuary())
+ if (essence.use_in_sanctuary_only != 0 && !IsInSanctuary())
{
if (showMsg)
pGameRun.AddFixedMessage((int)FixedMsg.FIXMSG_USE_IN_SANCTUARY_ONLY);
@@ -7662,6 +7773,24 @@ namespace BrewMonster
}
}
}
+ else if(pSkill.GetType() == (int)CECSkill.SkillType.TYPE_BLESS)
+ {
+ // TODO: Implement pet blessing when petsystem is available
+ //CECSCPet pPet = EC_Game.GetGameRun().GetWorld().GetPetByID(m_idSelTarget);
+ //if (pPet == null || pPet.GetMasterID() == GetCharacterID())
+ //{
+ // CECPetData pPetData = m_pPetCorral.GetActivePet();
+ // if (pPetData == null ||
+ // pPetData.GetClass() != GP_PET_TYPE.GP_PET_CLASS_COMBAT &&
+ // pPetData.GetClass() != GP_PET_TYPE.GP_PET_CLASS_SUMMON &&
+ // pPetData.GetClass() != GP_PET_TYPE.GP_PET_CLASS_EVOLUTION)
+ // return false;
+
+ // idCastTarget = m_pPetCorral.GetActivePetNPCID();
+ //}
+ //if(iTargetType != 0 && idCastTarget == 0)
+ // return false;
+ }
if (iTargetType != 0)
{
@@ -7684,9 +7813,8 @@ namespace BrewMonster
if (!pSkill.IsInstant() && pSkill.GetType() != (int)CECSkill.SkillType.TYPE_FLASHMOVE)
{
- // TODO: Implement NaturallyStopMoving
- //if (!NaturallyStopMoving())
- // return false;
+ if (!NaturallyStopMoving())
+ return false;
}
else if (pSkill.GetType() == (int)CECSkill.SkillType.TYPE_FLASHMOVE)
{
@@ -7749,6 +7877,16 @@ namespace BrewMonster
return false;
}
+ if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_DYNSKILLEQUIP)
+ {
+ int iSameIDPos = m_pEquipPack.FindItem(pItem.GetTemplateID());
+ if (iSameIDPos >= 0)
+ {
+ iDst = iSameIDPos;
+ }
+
+ }
+
if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_ARROW ||
pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_DYNSKILLEQUIP)
{
@@ -7757,6 +7895,7 @@ namespace BrewMonster
UnityGameSession.RequestEquipItemAsync((byte)iSlot, (byte)iDst, null);
else
{
+ // TODO: Implement c2s_CmdMoveItemToEquip when available
//UnityGameSession.c2s_CmdMoveItemToEquip((byte)iSlot, (byte)iDst);
}
}
@@ -7765,6 +7904,11 @@ namespace BrewMonster
if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_GENERALCARD)
{
//TODO: Add general card equip request
+ EC_IvtrGeneralCard pCard = pItem as EC_IvtrGeneralCard;
+ if (pCard != null)
+ {
+ iDst = InventoryConst.EQUIPIVTR_GENERALCARD1 + pCard.GetEssence().type;
+ }
}
UnityGameSession.RequestEquipItemAsync((byte)iSlot, (byte)iDst, null);
}
@@ -7826,19 +7970,21 @@ namespace BrewMonster
float fDist = 0, fTargetRag = 0;
CECObject pObject = null;
- //if (CalcDist(m_idSelTarget, ref fDist, ref pObject))
- //{
- // return false;
- //}
+ if (CalcDist(m_idSelTarget, out fDist, out pObject))
+ {
+ return false;
+ }
if (GPDataTypeHelper.ISNPCID(m_idSelTarget))
{
+ pObject = EC_ManMessageMono.Instance.CECNPCMan.GetNPC(m_idSelTarget);
CECNPC pNPC = pObject as CECNPC;
if (pNPC != null)
fTargetRag = pNPC.GetTouchRadius();
}
else if (GPDataTypeHelper.ISPLAYERID(m_idSelTarget))
{
+ pObject = EC_ManMessageMono.Instance.GetECManPlayer.GetElsePlayer(m_idSelTarget);
EC_ElsePlayer pPlayer = pObject as EC_ElsePlayer;
if (pPlayer != null)
fTargetRag = pPlayer.GetTouchRadius();
@@ -7869,6 +8015,7 @@ namespace BrewMonster
if (showMsg)
{
CECGameUIMan pGameUI = pGameRun.GetUIManager().GetInGameUIMan();
+ //pGameUI.MessageBox("", pGameUI.GetStringFromTable(828), MB_OK, new Color32(1, 1, 1, 0.6));
}
return false;
}
@@ -7891,14 +8038,12 @@ namespace BrewMonster
EC_Inventory pPack = GetPack(pCmd.byPackage);
if (pPack == null)
{
- Debug.LogError("[OnMsgHstUseItem] Pack not found");
return;
}
EC_IvtrItem pItem = pPack.GetItem(pCmd.bySlot, false);
if (pItem == null || pItem.GetTemplateID() != pCmd.item_id)
{
- Debug.LogError($"[OnMsgHstUseItem] Item mismatch at slot {pCmd.bySlot}");
return;
}
@@ -7911,12 +8056,12 @@ namespace BrewMonster
{
if (pPack.GetItem(pCmd.bySlot, false) == null)
{
- Debug.Log($"[OnMsgHstUseItem] Item {pCmd.item_id} removed from slot {pCmd.bySlot}");
+ //Debug.Log($"[OnMsgHstUseItem] Item {pCmd.item_id} removed from slot {pCmd.bySlot}");
}
}
else
{
- Debug.LogError($"[OnMsgHstUseItem] Failed to remove item {pCmd.item_id} from slot {pCmd.bySlot}");
+ //Debug.LogError($"[OnMsgHstUseItem] Failed to remove item {pCmd.item_id} from slot {pCmd.bySlot}");
}
var ui = GameObject.FindFirstObjectByType();
@@ -7927,7 +8072,7 @@ namespace BrewMonster
}
else
{
- Debug.LogError("[OnMsgHstUseItem] EC_InventoryUI not found, UI may not update");
+ //Debug.LogError("[OnMsgHstUseItem] EC_InventoryUI not found, UI may not update");
}
}
@@ -7944,58 +8089,64 @@ namespace BrewMonster
}
}
- ///
- /// Calculate distance to an object and optionally retrieve the object reference
- /// 计算到对象的距离,并可选地获取对象引用
- ///
- /// Target object ID / 目标对象ID
- /// Output distance / 输出距离
- /// Output object reference (optional) / 输出对象引用(可选)
- /// True if calculation succeeded / 计算成功返回true
- //public bool CalcDist(int idObject, ref float pfDist, out CECObject ppObject)
- //{
- // ppObject = null;
+ //
+ // Calculate distance to an object and optionally retrieve the object reference
+ // 计算到对象的距离,并可选地获取对象引用
+ //
+ // Target object ID / 目标对象ID
+ // Output distance / 输出距离
+ // Output object reference (optional) / 输出对象引用(可选)
+ // True if calculation succeeded / 计算成功返回true
+ public bool CalcDist(int idObject, out float pfDist, out CECObject ppObject)
+ {
+ pfDist = 0.0f;
+ ppObject = null;
- // if (idObject == 0 || idObject == m_PlayerInfo.cid)
- // return false;
+ if (idObject == 0 || idObject == m_PlayerInfo.cid)
+ return false;
- // CECObject pObject = CECGameRun.Instance.GetWorld()?.GetObject(idObject, 1);
- // if (pObject == null)
- // return false;
+ CECWorld pWorld = CECGameRun.Instance.GetWorld();
+ if(pWorld == null)
+ return false;
- // ppObject = pObject;
- // float fDist = 0.0f;
+ CECObject pObject = pWorld.GetObject(idObject, 1);
+ if(ppObject == null)
+ return false;
- // if (ISNPCID(idObject))
- // {
- // CECNPC pNPC = (CECNPC)pObject;
- // fDist = pNPC.CalcDist(GetPos(), true);
- // }
- // else if (ISPLAYERID(idObject))
- // {
- // Debug.Assert(pObject.GetClassID() == CECObject.OCID_ELSEPLAYER);
- // EC_ElsePlayer pPlayer = (EC_ElsePlayer)pObject;
- // fDist = pPlayer.CalcDist(GetPos(), true);
- // }
- // else if (ISMATTERID(idObject))
- // {
- // Debug.Assert(pObject.GetClassID() == CECObject.OCID_MATTER);
- // CECMatter pMatter = (CECMatter)pObject;
- // fDist = (pMatter.GetPos() - GetPos()).magnitude;
- // }
- // else
- // return false;
+ ppObject = pObject;
+ float fDist = 0.0f;
- // pfDist = fDist;
- // return true;
- //}
+ if (GPDataTypeHelper.ISNPCID(idObject))
+ {
+ CECNPC pNPC = pObject as CECNPC;
+ if (pNPC == null)
+ return false;
+ fDist = pNPC.CalcDist(GetPos(), true);
+ }
+ else if (GPDataTypeHelper.ISMATTERID(idObject))
+ {
+ Debug.Assert(pObject.GetClassID() == Class_ID.OCID_MATTER);
+ CECMatter pMatter = pObject as CECMatter;
+ if (pMatter == null)
+ return false;
+ A3DVECTOR3 vDelta = pMatter.GetPos() - GetPos();
+ fDist = A3d_Magnitude(vDelta);
+ }
+ else
+ {
+ return false;
+ }
- //// Helper method overload without object output
- //public bool CalcDist(int idObject, ref float pfDist)
- //{
- // CECObject pObject;
- // return CalcDist(idObject, ref pfDist, out pObject);
- //}
+ pfDist = fDist;
+ return true;
+ }
+
+ // Helper method overload without object output
+ public bool CalcDist(int idObject, out float pfDist)
+ {
+ CECObject pObject;
+ return CalcDist(idObject, out pfDist, out pObject);
+ }
//// ID checking helper methods
//private bool ISNPCID(int id) => ((id & 0x80000000) != 0) && ((id & 0x40000000) == 0);
diff --git a/Assets/Scripts/CECUIManager.cs b/Assets/Scripts/CECUIManager.cs
index 131d4d63a6..d511547f9a 100644
--- a/Assets/Scripts/CECUIManager.cs
+++ b/Assets/Scripts/CECUIManager.cs
@@ -4,6 +4,7 @@ using BrewMonster.UI;
using CSNetwork;
using System;
using System.Collections.Generic;
+using BrewMonster.Scripts.Managers;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.UIElements;
@@ -109,7 +110,12 @@ public class CECUIManager : MonoSingleton
var msgBox = GetInGameUIMan().GetDialog("DlgMessageBox") as CDlgMessageBox;
if (msgBox != null)
{
- msgBox.ShowMessageBox(title, message);
+ msgBox.ShowMessageBox(new MessageBoxData()
+ {
+ Title = title,
+ Message = message,
+ MessageBoxType = MessageBoxType.YesButton
+ });
return msgBox;
}
else
@@ -230,6 +236,25 @@ public class CECUIManager : MonoSingleton
}
}
+ else if(string.CompareOrdinal("Game_Disenchase",pDlg.GetName())==0 && DialogBoxCommandIDs.IDOK == iRetVal)
+ {
+ // PAUIDIALOG pMsgBox;
+ var clearEmbeddedChipDlg = GetInGameUIMan().GetDialog("Game_Disenchase");
+ // EC_IvtrItem pIvtr = (EC_IvtrItem)m_pDlgUninstall->m_pItema->GetDataPtr("ptr_CECIvtrItem");
+ //
+ // pSession.c2s_CmdNPCSevClearEmbeddedChip(
+ // (WORD)m_pDlgUninstall->m_pItema->GetData(), pIvtr->GetTemplateID());
+ //
+ // m_pDlgUninstall->Show(false);
+ // pHost->EndNPCService();
+ // m_pCurNPCEssence = NULL;
+ // m_pDlgInventory->Show(false);
+ // pHost->GetPack(IVTRTYPE_PACK)->UnfreezeAllItems();
+ //
+ // MessageBox("", GetStringFromTable(228), MB_OK,
+ // A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox);
+ // pMsgBox->SetLife(3);
+ }
}
private bool OnNewMessageBox(int iRetVal)
diff --git a/Assets/Tech3C.meta b/Assets/Tech3C.meta
new file mode 100644
index 0000000000..b197cdb0af
--- /dev/null
+++ b/Assets/Tech3C.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 86378052e9798924e8ee14f4fa780f0f
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Tech3C/Plugins.meta b/Assets/Tech3C/Plugins.meta
new file mode 100644
index 0000000000..9c87ae31f2
--- /dev/null
+++ b/Assets/Tech3C/Plugins.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 4c935d070fff35741adebc5424c4e02b
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Tech3C/Plugins/Android.meta b/Assets/Tech3C/Plugins/Android.meta
new file mode 100644
index 0000000000..f279ada1af
--- /dev/null
+++ b/Assets/Tech3C/Plugins/Android.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2111256da5c2b2a469d98ce0f103cdb7
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Tech3C/Plugins/Android/Tech3CUnityBridge.java b/Assets/Tech3C/Plugins/Android/Tech3CUnityBridge.java
new file mode 100644
index 0000000000..6dd99c4ee8
--- /dev/null
+++ b/Assets/Tech3C/Plugins/Android/Tech3CUnityBridge.java
@@ -0,0 +1,184 @@
+package vn.tech3c.sdk.unity;
+
+import android.util.Log;
+
+import vn.tech3c.sdk.auth.callback.OnAuthCallback;
+import vn.tech3c.sdk.auth.controller.Tech3CIdController;
+import vn.tech3c.sdk.auth.entities.enums.LoginType;
+import vn.tech3c.sdk.auth.exceptions.Tech3CIdException;
+
+import com.unity3d.player.UnityPlayer;
+
+/**
+ * Unity Bridge for Tech3C SDK
+ * This class receives callbacks from Tech3C SDK and sends them to Unity via UnitySendMessage
+ */
+public class Tech3CUnityBridge implements OnAuthCallback {
+ private static final String TAG = "Tech3CUnityBridge";
+ private static final String UNITY_GAME_OBJECT = "Tech3CSDKBridge";
+
+ private static Tech3CUnityBridge instance;
+
+ private OnUnityAuthCallback unityAuthCallback;
+ private OnUnityLogoutCallback unityLogoutCallback;
+ private OnUnityUserInfoCallback unityUserInfoCallback;
+
+ // Singleton
+ public static synchronized Tech3CUnityBridge getInstance() {
+ if (instance == null) {
+ instance = new Tech3CUnityBridge();
+ }
+ return instance;
+ }
+
+ // Interfaces for Unity callbacks
+ public interface OnUnityAuthCallback {
+ void onAuthSuccess(String userId, String password, String accessToken, String refreshToken, LoginType loginType, long expiryTime);
+ void onAuthCancelled();
+ void onAuthError(int errorCode, String errorMessage);
+ }
+
+ public interface OnUnityLogoutCallback {
+ void onLogoutSuccess();
+ void onLogoutError(int errorCode, String errorMessage);
+ }
+
+ public interface OnUnityUserInfoCallback {
+ void onUserInfoReceived(String userId, String username, String email, String phone);
+ void onUserInfoCancelled();
+ void onUserInfoError(int errorCode, String errorMessage);
+ }
+
+ // Set callbacks
+ public void setUnityAuthCallback(OnUnityAuthCallback callback) {
+ this.unityAuthCallback = callback;
+ }
+
+ public void setUnityLogoutCallback(OnUnityLogoutCallback callback) {
+ this.unityLogoutCallback = callback;
+ }
+
+ public void setUnityUserInfoCallback(OnUnityUserInfoCallback callback) {
+ this.unityUserInfoCallback = callback;
+ }
+
+ // Send message to Unity
+ private void sendToUnity(String methodName, String message) {
+ try {
+ UnityPlayer.UnitySendMessage(UNITY_GAME_OBJECT, methodName, message);
+ Log.d(TAG, "Sent to Unity: " + methodName + " - " + message);
+ } catch (Exception e) {
+ Log.e(TAG, "Error sending to Unity: " + e.getMessage());
+ }
+ }
+
+ // ========== OnAuthCallback Implementation ==========
+
+ @Override
+ public void onLoginSuccess(String userId, String password, String accessToken, String refreshToken, LoginType loginType, long expiryTime) {
+ Log.d(TAG, "onLoginSuccess: " + userId);
+
+ // Send to Unity
+ String message = userId + "|" + password + "|" + accessToken + "|" + refreshToken + "|" + loginType.name() + "|" + expiryTime;
+ sendToUnity("OnAuthSuccess", message);
+
+ // Also call Unity callback if set
+ if (unityAuthCallback != null) {
+ unityAuthCallback.onAuthSuccess(userId, password, accessToken, refreshToken, loginType, expiryTime);
+ }
+ }
+
+ @Override
+ public void onRegisterSuccess(String accessToken, String refreshToken, String userId, long expiryTime) {
+ Log.d(TAG, "onRegisterSuccess: " + userId);
+
+ // Treat register success as auth success (password is empty for register)
+ String message = userId + "|" + "" + "|" + accessToken + "|" + refreshToken + "|REGISTER|" + expiryTime;
+ sendToUnity("OnAuthSuccess", message);
+
+ if (unityAuthCallback != null) {
+ unityAuthCallback.onAuthSuccess(userId, "", accessToken, refreshToken, LoginType.ACCOUNT, expiryTime);
+ }
+ }
+
+ @Override
+ public void onAuthCancelled() {
+ Log.d(TAG, "onAuthCancelled");
+ sendToUnity("OnAuthCancelled", "");
+
+ if (unityAuthCallback != null) {
+ unityAuthCallback.onAuthCancelled();
+ }
+ }
+
+ @Override
+ public void onAuthScreenOpened() {
+ Log.d(TAG, "onAuthScreenOpened");
+ // Optional: Send to Unity if needed
+ }
+
+ @Override
+ public void onError(Tech3CIdException exception) {
+ Log.d(TAG, "onError: " + exception.getMessage());
+
+ String message = exception.getErrorCode() + "|" + exception.getMessage();
+ sendToUnity("OnAuthError", message);
+
+ if (unityAuthCallback != null) {
+ unityAuthCallback.onAuthError(exception.getErrorCode(), exception.getMessage());
+ }
+ }
+
+ // ========== Additional Callback Methods ==========
+
+ public void onLogoutSuccess() {
+ Log.d(TAG, "onLogoutSuccess");
+ sendToUnity("OnLogoutSuccess", "");
+
+ if (unityLogoutCallback != null) {
+ unityLogoutCallback.onLogoutSuccess();
+ }
+ }
+
+ public void onLogoutError(int errorCode, String errorMessage) {
+ Log.d(TAG, "onLogoutError: " + errorMessage);
+
+ String message = errorCode + "|" + errorMessage;
+ sendToUnity("OnLogoutError", message);
+
+ if (unityLogoutCallback != null) {
+ unityLogoutCallback.onLogoutError(errorCode, errorMessage);
+ }
+ }
+
+ public void onUserInfoReceived(String userId, String username, String email, String phone) {
+ Log.d(TAG, "onUserInfoReceived: " + userId);
+
+ String message = userId + "|" + username + "|" + email + "|" + phone;
+ sendToUnity("OnUserInfoReceived", message);
+
+ if (unityUserInfoCallback != null) {
+ unityUserInfoCallback.onUserInfoReceived(userId, username, email, phone);
+ }
+ }
+
+ public void onUserInfoCancelled() {
+ Log.d(TAG, "onUserInfoCancelled");
+ sendToUnity("OnUserInfoCancelled", "");
+
+ if (unityUserInfoCallback != null) {
+ unityUserInfoCallback.onUserInfoCancelled();
+ }
+ }
+
+ public void onUserInfoError(int errorCode, String errorMessage) {
+ Log.d(TAG, "onUserInfoError: " + errorMessage);
+
+ String message = errorCode + "|" + errorMessage;
+ sendToUnity("OnUserInfoError", message);
+
+ if (unityUserInfoCallback != null) {
+ unityUserInfoCallback.onUserInfoError(errorCode, errorMessage);
+ }
+ }
+}
diff --git a/Assets/Tech3C/Plugins/Android/Tech3CUnityBridge.java.meta b/Assets/Tech3C/Plugins/Android/Tech3CUnityBridge.java.meta
new file mode 100644
index 0000000000..6f929e7398
--- /dev/null
+++ b/Assets/Tech3C/Plugins/Android/Tech3CUnityBridge.java.meta
@@ -0,0 +1,32 @@
+fileFormatVersion: 2
+guid: e32ed25f4a2155a4f8106c56c82bb7fc
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ defineConstraints: []
+ isPreloaded: 0
+ isOverridable: 0
+ isExplicitlyReferenced: 0
+ validateReferences: 1
+ platformData:
+ - first:
+ Android: Android
+ second:
+ enabled: 1
+ settings: {}
+ - first:
+ Any:
+ second:
+ enabled: 0
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 0
+ settings:
+ DefaultValueInitialized: true
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Tech3C/Plugins/Android/auth-1.2.1.aar b/Assets/Tech3C/Plugins/Android/auth-1.2.1.aar
new file mode 100644
index 0000000000..b972ff9123
Binary files /dev/null and b/Assets/Tech3C/Plugins/Android/auth-1.2.1.aar differ
diff --git a/Assets/Tech3C/Plugins/Android/auth-1.2.1.aar.meta b/Assets/Tech3C/Plugins/Android/auth-1.2.1.aar.meta
new file mode 100644
index 0000000000..26ca9658d7
--- /dev/null
+++ b/Assets/Tech3C/Plugins/Android/auth-1.2.1.aar.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 3385cda9ff7dcd74ebc47f50d2e8196c
\ No newline at end of file
diff --git a/Assets/Tech3C/Plugins/Android/gradle.properties b/Assets/Tech3C/Plugins/Android/gradle.properties
new file mode 100644
index 0000000000..646c51b977
--- /dev/null
+++ b/Assets/Tech3C/Plugins/Android/gradle.properties
@@ -0,0 +1,2 @@
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/Assets/Tech3C/Plugins/Android/gradle.properties.meta b/Assets/Tech3C/Plugins/Android/gradle.properties.meta
new file mode 100644
index 0000000000..235ab6ba4b
--- /dev/null
+++ b/Assets/Tech3C/Plugins/Android/gradle.properties.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 5b51400b9b30374459987374d5fb2a0a
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Tech3C/Plugins/Android/mainTemplate.gradle b/Assets/Tech3C/Plugins/Android/mainTemplate.gradle
new file mode 100644
index 0000000000..d5267dce20
--- /dev/null
+++ b/Assets/Tech3C/Plugins/Android/mainTemplate.gradle
@@ -0,0 +1,43 @@
+apply plugin: 'com.android.library'
+**APPLY_PLUGINS**
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "androidx.appcompat:appcompat:1.6.1"
+ implementation "com.google.android.material:material:1.11.0"
+**DEPS**}
+
+android {
+ namespace "com.unity3d.player"
+ ndkPath "**NDKPATH**"
+ compileSdkVersion **APIVERSION**
+ buildToolsVersion '**BUILDTOOLS**'
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_11
+ targetCompatibility JavaVersion.VERSION_11
+ }
+
+ defaultConfig {
+ minSdkVersion **MINSDKVERSION**
+ targetSdkVersion **TARGETSDKVERSION**
+ ndk {
+ abiFilters **ABIFILTERS**
+ }
+ versionCode **VERSIONCODE**
+ versionName '**VERSIONNAME**'
+ consumerProguardFiles 'proguard-unity.txt'**USER_PROGUARD**
+ }
+
+ lintOptions {
+ abortOnError false
+ }
+
+ aaptOptions {
+ noCompress = **BUILTIN_NOCOMPRESS** + unityStreamingAssets.tokenize(', ')
+ ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~"
+ }**PACKAGING_OPTIONS**
+}
+**IL_CPP_BUILD_SETUP**
+**SOURCE_BUILD_SETUP**
+**EXTERNAL_SOURCES**
diff --git a/Assets/Tech3C/Plugins/Android/mainTemplate.gradle.meta b/Assets/Tech3C/Plugins/Android/mainTemplate.gradle.meta
new file mode 100644
index 0000000000..8ce6936aca
--- /dev/null
+++ b/Assets/Tech3C/Plugins/Android/mainTemplate.gradle.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 68ef4c077c6bfee47ad1aa16645a896d
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Tech3C/Plugins/Android/proguard-user.txt b/Assets/Tech3C/Plugins/Android/proguard-user.txt
new file mode 100644
index 0000000000..6b42f31f02
--- /dev/null
+++ b/Assets/Tech3C/Plugins/Android/proguard-user.txt
@@ -0,0 +1,30 @@
+#### Proguard class tech3c
+-keep public class vn.tech3c.sdk.auth.entities.* {
+ public *;
+}
+-keep public class vn.tech3c.sdk.auth.entities.enums.* {
+ public *;
+}
+-keep public class vn.tech3c.sdk.auth.entities.response.* {
+ public *;
+}
+-keep public class vn.tech3c.sdk.auth.entities.request.* {
+ public *;
+}
+
+-keep public interface vn.tech3c.sdk.auth.** { *; }
+
+-keep public class vn.tech3c.sdk.auth.controller.Tech3CIdController { public *;}
+-keep public class vn.tech3c.sdk.auth.exceptions.Tech3CIdException { public *;}
+
+-keep public class vn.tech3c.sdk.auth.Tech3CIdAuthentication {
+ public *;
+}
+
+-keep public class vn.tech3c.sdk.auth.config.Tech3CIdConfig {
+ public *;
+}
+
+-keep public class vn.tech3c.sdk.example.Tech3CUnityBridge { public *;}
+-keep public class vn.tech3c.sdk.example.Tech3CUnityBridge$** { *; }
+
diff --git a/Assets/Tech3C/Plugins/Android/proguard-user.txt.meta b/Assets/Tech3C/Plugins/Android/proguard-user.txt.meta
new file mode 100644
index 0000000000..6b05d1673c
--- /dev/null
+++ b/Assets/Tech3C/Plugins/Android/proguard-user.txt.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 0a1d44356c1df074d8cd78bee53cbafa
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Tech3C/Runtime.meta b/Assets/Tech3C/Runtime.meta
new file mode 100644
index 0000000000..8c1c38fee8
--- /dev/null
+++ b/Assets/Tech3C/Runtime.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: c5469f93c69421146854ce74deba7931
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Tech3C/Runtime/Tech3CAndroidBridge.cs b/Assets/Tech3C/Runtime/Tech3CAndroidBridge.cs
new file mode 100644
index 0000000000..c781c13e3c
--- /dev/null
+++ b/Assets/Tech3C/Runtime/Tech3CAndroidBridge.cs
@@ -0,0 +1,452 @@
+using UnityEngine;
+using System;
+
+namespace Tech3C
+{
+ ///
+ /// Bridge class for Android platform to communicate with Tech3C native SDK
+ ///
+ public class Tech3CAndroidBridge : ITech3CNativeBridge
+ {
+ private const string CLASS_NAME = "vn.tech3c.sdk.auth.controller.Tech3CIdController";
+ private const string BRIDGE_CLASS = "vn.tech3c.sdk.unity.Tech3CUnityBridge";
+
+ private AndroidJavaObject controller;
+ private AndroidJavaObject unityBridge;
+
+ ///
+ /// Initialize the Tech3C SDK
+ ///
+ public void Initialize(string clientId, string clientSecret, Tech3CConfig config)
+ {
+ try
+ {
+ Debug.Log("[Tech3C] Bridge GameObject initialized");
+
+ // Initialize Unity Bridge for callbacks first
+ using (AndroidJavaClass bridgeClass = new AndroidJavaClass(BRIDGE_CLASS))
+ {
+ unityBridge = bridgeClass.CallStatic("getInstance");
+ }
+
+ // Initialize controller with method chaining to set callback
+ if (controller == null)
+ {
+ using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
+ using (AndroidJavaObject activity = unityPlayer.GetStatic("currentActivity"))
+ using (AndroidJavaClass controllerClass = new AndroidJavaClass(CLASS_NAME))
+ {
+ // Chain methods like MainActivity: initialize() -> setDebug() -> setOnAuthCallback()
+ controller = controllerClass.CallStatic(
+ "initialize",
+ activity,
+ clientId,
+ clientSecret
+ )
+ // .Call("setDebug", true)
+ // .Call("setDisableExitLogin", false)
+ // .Call("setEnableMaintenanceCheck", true)
+ .Call("setOnAuthCallback", unityBridge);
+
+ Debug.Log("[Tech3C] Unity Bridge registered with native SDK");
+ }
+ }
+
+ if (controller != null)
+ {
+ // Apply configuration
+ ApplyConfig(config);
+ Debug.Log("[Tech3C] Initialized successfully");
+ }
+ else
+ {
+ Debug.LogError("[Tech3C] Failed to initialize controller");
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"[Tech3C] Initialize error: {e.Message}");
+ }
+ }
+
+ ///
+ /// Apply configuration settings to the SDK
+ ///
+ private void ApplyConfig(Tech3CConfig config)
+ {
+ if (controller == null || config == null) return;
+
+ try
+ {
+ // Set debug mode (returns Tech3CIdController for chaining)
+ controller = controller.Call("setDebug", config.debugMode);
+
+ // Set language - pass Java Language enum
+ using (AndroidJavaClass languageEnumClass = new AndroidJavaClass("vn.tech3c.sdk.auth.entities.enums.Language"))
+ {
+ string javaEnumValue = config.language == Language.Vietnamese ? "VIETNAMESE" : "ENGLISH";
+ AndroidJavaObject javaLanguage = languageEnumClass.GetStatic(javaEnumValue);
+ controller = controller.Call("setLanguageDisplay", javaLanguage);
+ }
+
+ // Set UI mode - pass Java UiMode enum
+ using (AndroidJavaClass uiModeEnumClass = new AndroidJavaClass("vn.tech3c.sdk.auth.entities.enums.UiMode"))
+ {
+ string javaEnumValue = config.uiMode == UiMode.Fullscreen ? "FULLSCREEN" : "DIALOG";
+ AndroidJavaObject javaUiMode = uiModeEnumClass.GetStatic(javaEnumValue);
+ controller = controller.Call("setUiMode", javaUiMode);
+ }
+
+ // Set orientation - pass Java OrientationMode enum
+ using (AndroidJavaClass orientationEnumClass = new AndroidJavaClass("vn.tech3c.sdk.auth.entities.enums.OrientationMode"))
+ {
+ string javaEnumValue = config.screenOrientation.ToString().ToUpper();
+ AndroidJavaObject javaOrientation = orientationEnumClass.GetStatic(javaEnumValue);
+ controller = controller.Call("setOrientation", javaOrientation);
+ }
+
+ // Set guest login
+ controller = controller.Call("setEnableGuestLogin", config.enableGuestLogin);
+
+ // Set OTP requirement
+ controller = controller.Call("setRequireOtp", config.requireOtp);
+
+ // Set disable exit login
+ controller = controller.Call("setDisableExitLogin", config.disableExitLogin);
+
+ // Set maintenance check
+ controller = controller.Call("setEnableMaintenanceCheck", config.enableMaintenanceCheck);
+
+ if (!string.IsNullOrEmpty(config.maintenanceCheckUrl))
+ {
+ controller = controller.Call("setMaintenanceCheckUrl", config.maintenanceCheckUrl);
+ }
+
+ if (!string.IsNullOrEmpty(config.serverIp))
+ {
+ controller = controller.Call("setIpMaintenanceCheck", config.serverIp);
+ }
+
+ Debug.Log("[Tech3C] Configuration applied successfully");
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"[Tech3C] ApplyConfig error: {e.Message}");
+ }
+ }
+
+ ///
+ /// Show authentication screen
+ ///
+ public void ShowAuth(IAuthCallback callback)
+ {
+ try
+ {
+ if (controller == null)
+ {
+ Debug.LogError("[Tech3C] Controller not initialized");
+ callback?.OnAuthError(-1, "SDK not initialized");
+ return;
+ }
+
+ // Register callback with bridge
+ Tech3CSDKBridge.SetAuthCallback(callback);
+
+ using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
+ using (AndroidJavaObject activity = unityPlayer.GetStatic("currentActivity"))
+ {
+ controller.Call("showAuth", activity);
+ Debug.Log("[Tech3C] Auth screen shown");
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"[Tech3C] ShowAuth error: {e.Message}");
+ callback?.OnAuthError(-1, e.Message);
+ }
+ }
+
+ ///
+ /// Logout current user
+ ///
+ public void Logout(ILogoutCallback callback)
+ {
+ try
+ {
+ if (controller == null)
+ {
+ Debug.LogError("[Tech3C] Controller not initialized");
+ callback?.OnLogoutError(-1, "SDK not initialized");
+ return;
+ }
+
+ // Register callback with bridge
+ Tech3CSDKBridge.SetLogoutCallback(callback);
+
+ controller.Call("logout");
+ Debug.Log("[Tech3C] Logout initiated");
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"[Tech3C] Logout error: {e.Message}");
+ callback?.OnLogoutError(-1, e.Message);
+ }
+ }
+
+ ///
+ /// Get user information
+ ///
+ public void GetUserInfo(IUserInfoCallback callback)
+ {
+ try
+ {
+ if (controller == null)
+ {
+ Debug.LogError("[Tech3C] Controller not initialized");
+ callback?.OnUserInfoError(-1, "SDK not initialized");
+ return;
+ }
+
+ // Register callback with bridge
+ Tech3CSDKBridge.SetUserInfoCallback(callback);
+
+ controller.Call("getUserInfo");
+ Debug.Log("[Tech3C] GetUserInfo called");
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"[Tech3C] GetUserInfo error: {e.Message}");
+ callback?.OnUserInfoError(-1, e.Message);
+ }
+ }
+
+ ///
+ /// Check if user is logged in
+ ///
+ public bool IsLoggedIn()
+ {
+ try
+ {
+ if (controller == null) return false;
+
+ return controller.Call("isLogin");
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"[Tech3C] IsLoggedIn error: {e.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// Get access token
+ ///
+ public string GetAccessToken()
+ {
+ try
+ {
+ if (controller == null) return null;
+
+ return controller.Call("getAccessToken");
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"[Tech3C] GetAccessToken error: {e.Message}");
+ return null;
+ }
+ }
+
+ ///
+ /// Get refresh token
+ ///
+ public string GetRefreshToken()
+ {
+ try
+ {
+ if (controller == null) return null;
+
+ return controller.Call("getRefreshToken");
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"[Tech3C] GetRefreshToken error: {e.Message}");
+ return null;
+ }
+ }
+
+ ///
+ /// Get user ID
+ ///
+ public string GetUserId()
+ {
+ try
+ {
+ if (controller == null) return null;
+
+ return controller.Call("getUserId");
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"[Tech3C] GetUserId error: {e.Message}");
+ return null;
+ }
+ }
+
+ ///
+ /// Get device ID
+ ///
+ public string GetDeviceId()
+ {
+ try
+ {
+ if (controller == null) return null;
+
+ return controller.Call("getDeviceId");
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"[Tech3C] GetDeviceId error: {e.Message}");
+ return null;
+ }
+ }
+
+ ///
+ /// Get login time
+ ///
+ public long GetLoginTime()
+ {
+ try
+ {
+ if (controller == null) return 0;
+
+ return controller.Call("getLoginTime");
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"[Tech3C] GetLoginTime error: {e.Message}");
+ return 0;
+ }
+ }
+
+ ///
+ /// Get token expiry time
+ ///
+ public long GetTokenExpiry()
+ {
+ try
+ {
+ if (controller == null) return 0;
+
+ return controller.Call("getTokenExpiry");
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"[Tech3C] GetTokenExpiry error: {e.Message}");
+ return 0;
+ }
+ }
+
+ ///
+ /// Check if token is expired
+ ///
+ public bool IsTokenExpired()
+ {
+ try
+ {
+ if (controller == null) return false;
+
+ return controller.Call("isTokenExpired");
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"[Tech3C] IsTokenExpired error: {e.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// Set language
+ ///
+ public void SetLanguage(Language language)
+ {
+ try
+ {
+ if (controller == null) return;
+
+ // Set language - pass Java Language enum
+ using (AndroidJavaClass languageEnumClass = new AndroidJavaClass("vn.tech3c.sdk.auth.entities.enums.Language"))
+ {
+ string javaEnumValue = language == Language.Vietnamese ? "VIETNAMESE" : "ENGLISH";
+ AndroidJavaObject javaLanguage = languageEnumClass.GetStatic(javaEnumValue);
+ controller = controller.Call("setLanguageDisplay", javaLanguage);
+ }
+ Debug.Log($"[Tech3C] Language set to {language}");
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"[Tech3C] SetLanguage error: {e.Message}");
+ }
+ }
+
+ ///
+ /// Set debug mode
+ ///
+ public void SetDebug(bool debug)
+ {
+ try
+ {
+ if (controller == null) return;
+
+ controller = controller.Call("setDebug", debug);
+ Debug.Log($"[Tech3C] Debug mode set to {debug}");
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"[Tech3C] SetDebug error: {e.Message}");
+ }
+ }
+
+ ///
+ /// Cleanup resources
+ ///
+ public void Cleanup()
+ {
+ try
+ {
+ if (controller != null)
+ {
+ controller.Call("cleanup");
+ controller.Dispose();
+ controller = null;
+ Debug.Log("[Tech3C] Cleanup completed");
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"[Tech3C] Cleanup error: {e.Message}");
+ }
+ }
+ }
+
+ ///
+ /// Interface for native bridge implementations
+ ///
+ public interface ITech3CNativeBridge
+ {
+ void Initialize(string clientId, string clientSecret, Tech3CConfig config);
+ void ShowAuth(IAuthCallback callback);
+ void Logout(ILogoutCallback callback);
+ void GetUserInfo(IUserInfoCallback callback);
+ bool IsLoggedIn();
+ string GetAccessToken();
+ string GetRefreshToken();
+ string GetUserId();
+ string GetDeviceId();
+ long GetLoginTime();
+ long GetTokenExpiry();
+ bool IsTokenExpired();
+ void SetLanguage(Language language);
+ void SetDebug(bool debug);
+ void Cleanup();
+ }
+}
diff --git a/Assets/Tech3C/Runtime/Tech3CAndroidBridge.cs.meta b/Assets/Tech3C/Runtime/Tech3CAndroidBridge.cs.meta
new file mode 100644
index 0000000000..937d79ca0b
--- /dev/null
+++ b/Assets/Tech3C/Runtime/Tech3CAndroidBridge.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 50744a202f3e9e24a80e10aecf427dac
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Tech3C/Runtime/Tech3CCallbacks.cs b/Assets/Tech3C/Runtime/Tech3CCallbacks.cs
new file mode 100644
index 0000000000..7f40b826a3
--- /dev/null
+++ b/Assets/Tech3C/Runtime/Tech3CCallbacks.cs
@@ -0,0 +1,150 @@
+using System;
+
+namespace Tech3C
+{
+ ///
+ /// Callback for authentication operations (login, register, etc.)
+ ///
+ public interface IAuthCallback
+ {
+ ///
+ /// Called when authentication is successful
+ ///
+ /// The authenticated user ID
+ /// The user password
+ /// The access token
+ /// The refresh token
+ /// The type of login used
+ /// Token expiry timestamp
+ void OnAuthSuccess(string userId, string password, string accessToken, string refreshToken, LoginType loginType, long expiryTime);
+
+ ///
+ /// Called when authentication is cancelled by the user
+ ///
+ void OnAuthCancelled();
+
+ ///
+ /// Called when an error occurs during authentication
+ ///
+ /// The error code
+ /// The error message
+ void OnAuthError(int errorCode, string errorMessage);
+ }
+
+ ///
+ /// Callback for logout operations
+ ///
+ public interface ILogoutCallback
+ {
+ ///
+ /// Called when logout is successful
+ ///
+ void OnLogoutSuccess();
+
+ ///
+ /// Called when an error occurs during logout
+ ///
+ /// The error code
+ /// The error message
+ void OnLogoutError(int errorCode, string errorMessage);
+ }
+
+ ///
+ /// Callback for user info operations
+ ///
+ public interface IUserInfoCallback
+ {
+ ///
+ /// Called when user info is successfully retrieved
+ ///
+ /// The user ID
+ /// The username
+ /// The email address
+ /// The phone number
+ void OnUserInfoReceived(string userId, string username, string email, string phone);
+
+ ///
+ /// Called when user info retrieval is cancelled
+ ///
+ void OnUserInfoCancelled();
+
+ ///
+ /// Called when an error occurs while retrieving user info
+ ///
+ /// The error code
+ /// The error message
+ void OnUserInfoError(int errorCode, string errorMessage);
+ }
+
+ ///
+ /// Default implementation of IAuthCallback using Unity events
+ ///
+ [Serializable]
+ public class AuthCallback : IAuthCallback
+ {
+ public event Action OnAuthSuccessEvent;
+ public event Action OnAuthCancelledEvent;
+ public event Action OnAuthErrorEvent;
+
+ public void OnAuthSuccess(string userId, string password, string accessToken, string refreshToken, LoginType loginType, long expiryTime)
+ {
+ OnAuthSuccessEvent?.Invoke(userId, password, accessToken, refreshToken, loginType, expiryTime);
+ }
+
+ public void OnAuthCancelled()
+ {
+ OnAuthCancelledEvent?.Invoke();
+ }
+
+ public void OnAuthError(int errorCode, string errorMessage)
+ {
+ OnAuthErrorEvent?.Invoke(errorCode, errorMessage);
+ }
+ }
+
+ ///
+ /// Default implementation of ILogoutCallback using Unity events
+ ///
+ [Serializable]
+ public class LogoutCallback : ILogoutCallback
+ {
+ public event Action OnLogoutSuccessEvent;
+ public event Action OnLogoutErrorEvent;
+
+ public void OnLogoutSuccess()
+ {
+ OnLogoutSuccessEvent?.Invoke();
+ }
+
+ public void OnLogoutError(int errorCode, string errorMessage)
+ {
+ OnLogoutErrorEvent?.Invoke(errorCode, errorMessage);
+ }
+ }
+
+ ///
+ /// Default implementation of IUserInfoCallback using Unity events
+ ///
+ [Serializable]
+ public class UserInfoCallback : IUserInfoCallback
+ {
+ public event Action OnUserInfoReceivedEvent;
+ public event Action OnUserInfoCancelledEvent;
+ public event Action OnUserInfoErrorEvent;
+
+ public void OnUserInfoReceived(string userId, string username, string email, string phone)
+ {
+ OnUserInfoReceivedEvent?.Invoke(userId, username, email, phone);
+ }
+
+ public void OnUserInfoCancelled()
+ {
+ OnUserInfoCancelledEvent?.Invoke();
+ }
+
+ public void OnUserInfoError(int errorCode, string errorMessage)
+ {
+ OnUserInfoErrorEvent?.Invoke(errorCode, errorMessage);
+ }
+ }
+}
diff --git a/Assets/Tech3C/Runtime/Tech3CCallbacks.cs.meta b/Assets/Tech3C/Runtime/Tech3CCallbacks.cs.meta
new file mode 100644
index 0000000000..b7e5fd3738
--- /dev/null
+++ b/Assets/Tech3C/Runtime/Tech3CCallbacks.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ed5aa8a6da34849438530838098c8ca0
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Tech3C/Runtime/Tech3CConfig.cs b/Assets/Tech3C/Runtime/Tech3CConfig.cs
new file mode 100644
index 0000000000..6d31bb34b2
--- /dev/null
+++ b/Assets/Tech3C/Runtime/Tech3CConfig.cs
@@ -0,0 +1,114 @@
+using UnityEngine;
+
+namespace Tech3C
+{
+ ///
+ /// Configuration class for Tech3C SDK
+ ///
+ [CreateAssetMenu(fileName = "Tech3CConfig", menuName = "Tech3C SDK/Configuration", order = 1)]
+ public class Tech3CConfig : ScriptableObject
+ {
+ [Header("Client Credentials")]
+ [Tooltip("Client ID for Tech3C authentication")]
+ public string clientId;
+
+ [Tooltip("Client Secret for Tech3C authentication")]
+ public string clientSecret;
+
+ [Header("General Settings")]
+ [Tooltip("Enable debug mode for detailed logging")]
+ public bool debugMode = false;
+
+ [Tooltip("Display language for the SDK")]
+ public Language language = Language.English;
+
+ [Tooltip("Environment for the SDK")]
+ public Environment environment = Environment.Production;
+
+ [Header("UI Settings")]
+ [Tooltip("UI mode for authentication (Fullscreen or Dialog)")]
+ public UiMode uiMode = UiMode.Fullscreen;
+
+ [Tooltip("Dialog size when UI mode is Dialog")]
+ public DialogSize dialogSize = DialogSize.Medium;
+
+ [Tooltip("Screen orientation for authentication")]
+ public OrientationMode screenOrientation = OrientationMode.Auto;
+
+ [Tooltip("Disable exit button on login screen")]
+ public bool disableExitLogin = false;
+
+ [Header("Authentication Settings")]
+ [Tooltip("Enable guest login")]
+ public bool enableGuestLogin = false;
+
+ [Tooltip("Require OTP for authentication")]
+ public bool requireOtp = false;
+
+ [Tooltip("Enable Google login")]
+ public bool enableGoogleLogin = false;
+
+ [Tooltip("Enable Facebook login")]
+ public bool enableFacebookLogin = false;
+
+ [Tooltip("Enable Apple login")]
+ public bool enableAppleLogin = false;
+
+ [Header("Maintenance Settings")]
+ [Tooltip("Enable maintenance check")]
+ public bool enableMaintenanceCheck = false;
+
+ [Tooltip("Maintenance check URL")]
+ public string maintenanceCheckUrl;
+
+ [Tooltip("Server IP for maintenance check")]
+ public string serverIp;
+
+ [Header("Network Settings")]
+ [Tooltip("Timeout in seconds for network requests")]
+ public int timeoutSeconds = 30;
+
+ ///
+ /// Creates a default Tech3CConfig asset
+ ///
+ public static Tech3CConfig CreateDefault()
+ {
+ var config = CreateInstance();
+ config.clientId = "";
+ config.clientSecret = "";
+ config.debugMode = false;
+ config.language = Language.English;
+ config.environment = Environment.Production;
+ config.uiMode = UiMode.Fullscreen;
+ config.dialogSize = DialogSize.Medium;
+ config.screenOrientation = OrientationMode.Auto;
+ config.disableExitLogin = false;
+ config.enableGuestLogin = false;
+ config.requireOtp = false;
+ config.enableGoogleLogin = false;
+ config.enableFacebookLogin = false;
+ config.enableAppleLogin = false;
+ config.enableMaintenanceCheck = false;
+ config.maintenanceCheckUrl = "";
+ config.serverIp = "";
+ config.timeoutSeconds = 30;
+ return config;
+ }
+
+ ///
+ /// Validates the configuration
+ ///
+ public bool IsValid()
+ {
+ return !string.IsNullOrEmpty(clientId) && !string.IsNullOrEmpty(clientSecret);
+ }
+
+ ///
+ /// Gets the language code string
+ ///
+ public string GetLanguageCode()
+ {
+ return language == Language.Vietnamese ? "vi" : "en";
+ }
+ }
+}
diff --git a/Assets/Tech3C/Runtime/Tech3CConfig.cs.meta b/Assets/Tech3C/Runtime/Tech3CConfig.cs.meta
new file mode 100644
index 0000000000..39fd2c7efd
--- /dev/null
+++ b/Assets/Tech3C/Runtime/Tech3CConfig.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 95a442ac7dacf8b4484a10035308bc31
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Tech3C/Runtime/Tech3CEnums.cs b/Assets/Tech3C/Runtime/Tech3CEnums.cs
new file mode 100644
index 0000000000..9107769385
--- /dev/null
+++ b/Assets/Tech3C/Runtime/Tech3CEnums.cs
@@ -0,0 +1,64 @@
+using System;
+
+namespace Tech3C
+{
+ ///
+ /// Language options for the SDK
+ ///
+ public enum Language
+ {
+ Vietnamese,
+ English
+ }
+
+ ///
+ /// UI mode for authentication dialog
+ ///
+ public enum UiMode
+ {
+ Fullscreen,
+ Dialog
+ }
+
+ ///
+ /// Screen orientation for authentication
+ ///
+ public enum OrientationMode
+ {
+ Auto,
+ Portrait,
+ Landscape
+ }
+
+ ///
+ /// Login type for authentication
+ /// Matches the native SDK's LoginType enum
+ ///
+ public enum LoginType
+ {
+ GUEST,
+ ACCOUNT,
+ SOCIAL,
+ REGISTER // For register success handling
+ }
+
+ ///
+ /// Environment for the SDK
+ ///
+ public enum Environment
+ {
+ Production,
+ Staging,
+ Development
+ }
+
+ ///
+ /// Dialog size for UI mode
+ ///
+ public enum DialogSize
+ {
+ Small,
+ Medium,
+ Large
+ }
+}
diff --git a/Assets/Tech3C/Runtime/Tech3CEnums.cs.meta b/Assets/Tech3C/Runtime/Tech3CEnums.cs.meta
new file mode 100644
index 0000000000..0b58d42a40
--- /dev/null
+++ b/Assets/Tech3C/Runtime/Tech3CEnums.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c35f3ea36736dfe439eb6f4b8a314dde
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Tech3C/Runtime/Tech3CSDK.cs b/Assets/Tech3C/Runtime/Tech3CSDK.cs
new file mode 100644
index 0000000000..dd9111009a
--- /dev/null
+++ b/Assets/Tech3C/Runtime/Tech3CSDK.cs
@@ -0,0 +1,473 @@
+using UnityEngine;
+using System;
+
+namespace Tech3C
+{
+ ///
+ /// Main API wrapper for Tech3C SDK
+ /// This is the main entry point for using the SDK in Unity
+ ///
+ public class Tech3CSDK : MonoBehaviour
+ {
+ private static Tech3CSDK instance;
+ private ITech3CNativeBridge nativeBridge;
+ private Tech3CConfig config;
+ private bool isInitialized = false;
+
+ #region Singleton
+
+ ///
+ /// Get the singleton instance of Tech3CSDK
+ ///
+ public static Tech3CSDK Instance
+ {
+ get
+ {
+ if (instance == null)
+ {
+ GameObject go = new GameObject("Tech3CSDK");
+ instance = go.AddComponent();
+ DontDestroyOnLoad(go);
+ }
+ return instance;
+ }
+ }
+
+ private void Awake()
+ {
+ if (instance == null)
+ {
+ instance = this;
+ DontDestroyOnLoad(gameObject);
+ }
+ else if (instance != this)
+ {
+ Destroy(gameObject);
+ }
+ }
+
+ #endregion
+
+ #region Initialization
+
+ ///
+ /// Initialize the Tech3C SDK with configuration
+ ///
+ /// The configuration object
+ public void Initialize(Tech3CConfig config)
+ {
+ if (config == null)
+ {
+ Debug.LogError("[Tech3C] Config cannot be null");
+ return;
+ }
+
+ if (!config.IsValid())
+ {
+ Debug.LogError("[Tech3C] Invalid config: Client ID and Client Secret are required");
+ return;
+ }
+
+ this.config = config;
+
+ // Ensure Tech3CSDKBridge is created to receive callbacks from native code
+ var bridge = Tech3CSDKBridge.Instance;
+ Debug.Log("[Tech3C] Tech3CSDKBridge ensured for callbacks");
+
+ // Create platform-specific bridge
+ CreateNativeBridge();
+
+ if (nativeBridge != null)
+ {
+ nativeBridge.Initialize(config.clientId, config.clientSecret, config);
+ isInitialized = true;
+ Debug.Log("[Tech3C] Initialization completed successfully");
+ }
+ else
+ {
+ Debug.LogError("[Tech3C] Failed to create native bridge");
+ }
+ }
+
+ ///
+ /// Initialize the Tech3C SDK with client credentials
+ ///
+ /// The client ID
+ /// The client secret
+ public void Initialize(string clientId, string clientSecret)
+ {
+ if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(clientSecret))
+ {
+ Debug.LogError("[Tech3C] Client ID and Client Secret are required");
+ return;
+ }
+
+ config = Tech3CConfig.CreateDefault();
+ config.clientId = clientId;
+ config.clientSecret = clientSecret;
+
+ Initialize(config);
+ }
+
+ private void CreateNativeBridge()
+ {
+ #if UNITY_ANDROID && !UNITY_EDITOR
+ nativeBridge = new Tech3CAndroidBridge();
+ #elif UNITY_IOS && !UNITY_EDITOR
+ nativeBridge = new Tech3CiOSBridge();
+ #else
+ #if UNITY_ANDROID
+ nativeBridge = new Tech3CAndroidBridge();
+ #elif UNITY_IOS
+ nativeBridge = new Tech3CiOSBridge();
+ #else
+ Debug.LogWarning("[Tech3C] Running in Editor or unsupported platform. Using mock implementation.");
+ nativeBridge = new MockBridge();
+ #endif
+ #endif
+ }
+
+ #endregion
+
+ #region Authentication
+
+ ///
+ /// Show authentication screen (login/register/forgot password)
+ ///
+ /// The callback for authentication result
+ public void ShowAuth(IAuthCallback callback)
+ {
+ if (!isInitialized)
+ {
+ Debug.LogError("[Tech3C] SDK not initialized. Call Initialize() first.");
+ callback?.OnAuthError(-1, "SDK not initialized");
+ return;
+ }
+
+ nativeBridge?.ShowAuth(callback);
+ }
+
+ ///
+ /// Show authentication screen without callback
+ ///
+ public void ShowAuth()
+ {
+ ShowAuth(null);
+ }
+
+ #endregion
+
+ #region User Management
+
+ ///
+ /// Logout current user
+ ///
+ /// The callback for logout result
+ public void Logout(ILogoutCallback callback)
+ {
+ if (!isInitialized)
+ {
+ Debug.LogError("[Tech3C] SDK not initialized. Call Initialize() first.");
+ callback?.OnLogoutError(-1, "SDK not initialized");
+ return;
+ }
+
+ nativeBridge?.Logout(callback);
+ }
+
+ ///
+ /// Logout current user without callback
+ ///
+ public void Logout()
+ {
+ Logout(null);
+ }
+
+ ///
+ /// Get user information
+ ///
+ /// The callback for user info result
+ public void GetUserInfo(IUserInfoCallback callback)
+ {
+ if (!isInitialized)
+ {
+ Debug.LogError("[Tech3C] SDK not initialized. Call Initialize() first.");
+ callback?.OnUserInfoError(-1, "SDK not initialized");
+ return;
+ }
+
+ nativeBridge?.GetUserInfo(callback);
+ }
+
+ ///
+ /// Get user information without callback
+ ///
+ public void GetUserInfo()
+ {
+ GetUserInfo(null);
+ }
+
+ #endregion
+
+ #region State Checkers
+
+ ///
+ /// Check if user is logged in
+ ///
+ public bool IsLoggedIn()
+ {
+ if (!isInitialized)
+ {
+ Debug.LogWarning("[Tech3C] SDK not initialized");
+ return false;
+ }
+
+ return nativeBridge?.IsLoggedIn() ?? false;
+ }
+
+ ///
+ /// Check if access token is expired
+ ///
+ public bool IsTokenExpired()
+ {
+ if (!isInitialized)
+ {
+ Debug.LogWarning("[Tech3C] SDK not initialized");
+ return true;
+ }
+
+ return nativeBridge?.IsTokenExpired() ?? true;
+ }
+
+ #endregion
+
+ #region Token Management
+
+ ///
+ /// Get access token
+ ///
+ public string GetAccessToken()
+ {
+ if (!isInitialized)
+ {
+ Debug.LogWarning("[Tech3C] SDK not initialized");
+ return null;
+ }
+
+ return nativeBridge?.GetAccessToken();
+ }
+
+ ///
+ /// Get refresh token
+ ///
+ public string GetRefreshToken()
+ {
+ if (!isInitialized)
+ {
+ Debug.LogWarning("[Tech3C] SDK not initialized");
+ return null;
+ }
+
+ return nativeBridge?.GetRefreshToken();
+ }
+
+ #endregion
+
+ #region User Information
+
+ ///
+ /// Get user ID
+ ///
+ public string GetUserId()
+ {
+ if (!isInitialized)
+ {
+ Debug.LogWarning("[Tech3C] SDK not initialized");
+ return null;
+ }
+
+ return nativeBridge?.GetUserId();
+ }
+
+ ///
+ /// Get device ID
+ ///
+ public string GetDeviceId()
+ {
+ if (!isInitialized)
+ {
+ Debug.LogWarning("[Tech3C] SDK not initialized");
+ return null;
+ }
+
+ return nativeBridge?.GetDeviceId();
+ }
+
+ ///
+ /// Get login timestamp
+ ///
+ public long GetLoginTime()
+ {
+ if (!isInitialized)
+ {
+ Debug.LogWarning("[Tech3C] SDK not initialized");
+ return 0;
+ }
+
+ return nativeBridge?.GetLoginTime() ?? 0;
+ }
+
+ ///
+ /// Get token expiry timestamp
+ ///
+ public long GetTokenExpiry()
+ {
+ if (!isInitialized)
+ {
+ Debug.LogWarning("[Tech3C] SDK not initialized");
+ return 0;
+ }
+
+ return nativeBridge?.GetTokenExpiry() ?? 0;
+ }
+
+ #endregion
+
+ #region Configuration
+
+ ///
+ /// Set display language
+ ///
+ /// The language to set
+ public void SetLanguage(Language language)
+ {
+ if (!isInitialized)
+ {
+ Debug.LogWarning("[Tech3C] SDK not initialized");
+ return;
+ }
+
+ nativeBridge?.SetLanguage(language);
+ }
+
+ ///
+ /// Set debug mode
+ ///
+ /// Enable or disable debug mode
+ public void SetDebug(bool debug)
+ {
+ if (!isInitialized)
+ {
+ Debug.LogWarning("[Tech3C] SDK not initialized");
+ return;
+ }
+
+ nativeBridge?.SetDebug(debug);
+ }
+
+ #endregion
+
+ #region Cleanup
+
+ ///
+ /// Cleanup SDK resources
+ ///
+ public void Cleanup()
+ {
+ nativeBridge?.Cleanup();
+ nativeBridge = null;
+ config = null;
+ isInitialized = false;
+ Debug.Log("[Tech3C] Cleanup completed");
+ }
+
+ private void OnDestroy()
+ {
+ Cleanup();
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Get the current configuration
+ ///
+ public Tech3CConfig Config => config;
+
+ ///
+ /// Check if SDK is initialized
+ ///
+ public bool IsInitialized => isInitialized;
+
+ #endregion
+ }
+
+ ///
+ /// Mock bridge for testing in editor or unsupported platforms
+ ///
+ internal class MockBridge : ITech3CNativeBridge
+ {
+ private bool isLoggedIn = false;
+ private string accessToken = "mock_access_token";
+ private string refreshToken = "mock_refresh_token";
+ private string userId = "mock_user_id";
+
+ public void Initialize(string clientId, string clientSecret, Tech3CConfig config)
+ {
+ Debug.Log($"[Tech3C Mock] Initialized with Client ID: {clientId}");
+ }
+
+ public void ShowAuth(IAuthCallback callback)
+ {
+ Debug.Log("[Mock3C Mock] ShowAuth called");
+ isLoggedIn = true;
+ string password = "mock_password";
+ callback?.OnAuthSuccess(userId, password, accessToken, refreshToken, LoginType.ACCOUNT, DateTime.Now.Ticks + 3600000);
+ }
+
+ public void Logout(ILogoutCallback callback)
+ {
+ Debug.Log("[Tech3C Mock] Logout called");
+ isLoggedIn = false;
+ callback?.OnLogoutSuccess();
+ }
+
+ public void GetUserInfo(IUserInfoCallback callback)
+ {
+ Debug.Log("[Tech3C Mock] GetUserInfo called");
+ callback?.OnUserInfoReceived(userId, "mock_username", "mock@example.com", "1234567890");
+ }
+
+ public bool IsLoggedIn() => isLoggedIn;
+
+ public string GetAccessToken() => isLoggedIn ? accessToken : null;
+
+ public string GetRefreshToken() => isLoggedIn ? refreshToken : null;
+
+ public string GetUserId() => isLoggedIn ? userId : null;
+
+ public string GetDeviceId() => "mock_device_id";
+
+ public long GetLoginTime() => isLoggedIn ? DateTime.Now.Ticks : 0;
+
+ public long GetTokenExpiry() => isLoggedIn ? DateTime.Now.Ticks + 3600000 : 0;
+
+ public bool IsTokenExpired() => false;
+
+ public void SetLanguage(Language language)
+ {
+ Debug.Log($"[Tech3C Mock] Language set to {language}");
+ }
+
+ public void SetDebug(bool debug)
+ {
+ Debug.Log($"[Tech3C Mock] Debug mode set to {debug}");
+ }
+
+ public void Cleanup()
+ {
+ Debug.Log("[Tech3C Mock] Cleanup completed");
+ }
+ }
+}
diff --git a/Assets/Tech3C/Runtime/Tech3CSDK.cs.meta b/Assets/Tech3C/Runtime/Tech3CSDK.cs.meta
new file mode 100644
index 0000000000..72d6fb5d15
--- /dev/null
+++ b/Assets/Tech3C/Runtime/Tech3CSDK.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3afb6189c673cea45a34e6e75c1f5ccb
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Tech3C/Runtime/Tech3CSDKBridge.cs b/Assets/Tech3C/Runtime/Tech3CSDKBridge.cs
new file mode 100644
index 0000000000..4c0b34086e
--- /dev/null
+++ b/Assets/Tech3C/Runtime/Tech3CSDKBridge.cs
@@ -0,0 +1,332 @@
+using UnityEngine;
+using System;
+
+namespace Tech3C
+{
+ ///
+ /// Bridge component to receive callbacks from native Android/iOS SDK via Unity messaging
+ /// This GameObject receives UnitySendMessage calls from native code
+ ///
+ public class Tech3CSDKBridge : MonoBehaviour
+ {
+ private static Tech3CSDKBridge instance;
+
+ // Callback references
+ private static IAuthCallback authCallback;
+ private static ILogoutCallback logoutCallback;
+ private static IUserInfoCallback userInfoCallback;
+
+ #region Singleton
+
+ public static Tech3CSDKBridge Instance
+ {
+ get
+ {
+ if (instance == null)
+ {
+ GameObject go = new GameObject("Tech3CSDKBridge");
+ instance = go.AddComponent();
+ DontDestroyOnLoad(go);
+ }
+ return instance;
+ }
+ }
+
+ private void Awake()
+ {
+ if (instance == null)
+ {
+ instance = this;
+ DontDestroyOnLoad(gameObject);
+ Debug.Log("[Tech3C Bridge] Bridge initialized");
+ }
+ else if (instance != this)
+ {
+ Destroy(gameObject);
+ }
+ }
+
+ #endregion
+
+ #region Set Callbacks
+
+ ///
+ /// Set the auth callback to be invoked when auth events occur
+ ///
+ public static void SetAuthCallback(IAuthCallback callback)
+ {
+ authCallback = callback;
+ Debug.Log("[Tech3C Bridge] Auth callback set");
+ }
+
+ ///
+ /// Set the logout callback to be invoked when logout events occur
+ ///
+ public static void SetLogoutCallback(ILogoutCallback callback)
+ {
+ logoutCallback = callback;
+ Debug.Log("[Tech3C Bridge] Logout callback set");
+ }
+
+ ///
+ /// Set the user info callback to be invoked when user info events occur
+ ///
+ public static void SetUserInfoCallback(IUserInfoCallback callback)
+ {
+ userInfoCallback = callback;
+ Debug.Log("[Tech3C Bridge] User info callback set");
+ }
+
+ #endregion
+
+ #region Auth Callbacks
+
+ ///
+ /// Called from native code when authentication is successful
+ /// Message format: "userId|password|accessToken|refreshToken|loginType|expiryTime"
+ ///
+ public void OnAuthSuccess(string message)
+ {
+ Debug.Log($"[Tech3C Bridge] OnAuthSuccess received: {message}");
+
+ if (authCallback == null)
+ {
+ Debug.LogWarning("[Tech3C Bridge] No auth callback registered");
+ return;
+ }
+
+ try
+ {
+ string[] parts = message.Split('|');
+ if (parts.Length >= 6)
+ {
+ string userId = parts[0];
+ string password = parts[1];
+ string accessToken = parts[2];
+ string refreshToken = parts[3];
+ LoginType loginType = (LoginType)Enum.Parse(typeof(LoginType), parts[4]);
+ long expiryTime = long.Parse(parts[5]);
+
+ Debug.Log($"[Tech3C Bridge] Parsed auth success: userId={userId}, loginType={loginType}");
+ authCallback.OnAuthSuccess(userId, password, accessToken, refreshToken, loginType, expiryTime);
+ }
+ else
+ {
+ Debug.LogError($"[Tech3C Bridge] Invalid message format: {message}");
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"[Tech3C Bridge] Error parsing auth success message: {e.Message}");
+ }
+ }
+
+ ///
+ /// Called from native code when authentication is cancelled
+ ///
+ public void OnAuthCancelled(string message)
+ {
+ Debug.Log("[Tech3C Bridge] OnAuthCancelled received");
+
+ if (authCallback == null)
+ {
+ Debug.LogWarning("[Tech3C Bridge] No auth callback registered");
+ return;
+ }
+
+ authCallback.OnAuthCancelled();
+ }
+
+ ///
+ /// Called from native code when an authentication error occurs
+ /// Message format: "errorCode|errorMessage"
+ ///
+ public void OnAuthError(string message)
+ {
+ Debug.Log($"[Tech3C Bridge] OnAuthError received: {message}");
+
+ if (authCallback == null)
+ {
+ Debug.LogWarning("[Tech3C Bridge] No auth callback registered");
+ return;
+ }
+
+ try
+ {
+ string[] parts = message.Split('|');
+ int errorCode = -1;
+ string errorMessage = "Unknown error";
+
+ if (parts.Length >= 1)
+ {
+ int.TryParse(parts[0], out errorCode);
+ }
+ if (parts.Length >= 2)
+ {
+ errorMessage = parts[1];
+ }
+
+ Debug.Log($"[Tech3C Bridge] Parsed auth error: code={errorCode}, message={errorMessage}");
+ authCallback.OnAuthError(errorCode, errorMessage);
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"[Tech3C Bridge] Error parsing auth error message: {e.Message}");
+ }
+ }
+
+ #endregion
+
+ #region Logout Callbacks
+
+ ///
+ /// Called from native code when logout is successful
+ ///
+ public void OnLogoutSuccess(string message)
+ {
+ Debug.Log("[Tech3C Bridge] OnLogoutSuccess received");
+
+ if (logoutCallback == null)
+ {
+ Debug.LogWarning("[Tech3C Bridge] No logout callback registered");
+ return;
+ }
+
+ logoutCallback.OnLogoutSuccess();
+ }
+
+ ///
+ /// Called from native code when a logout error occurs
+ /// Message format: "errorCode|errorMessage"
+ ///
+ public void OnLogoutError(string message)
+ {
+ Debug.Log($"[Tech3C Bridge] OnLogoutError received: {message}");
+
+ if (logoutCallback == null)
+ {
+ Debug.LogWarning("[Tech3C Bridge] No logout callback registered");
+ return;
+ }
+
+ try
+ {
+ string[] parts = message.Split('|');
+ int errorCode = -1;
+ string errorMessage = "Unknown error";
+
+ if (parts.Length >= 1)
+ {
+ int.TryParse(parts[0], out errorCode);
+ }
+ if (parts.Length >= 2)
+ {
+ errorMessage = parts[1];
+ }
+
+ logoutCallback.OnLogoutError(errorCode, errorMessage);
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"[Tech3C Bridge] Error parsing logout error message: {e.Message}");
+ }
+ }
+
+ #endregion
+
+ #region User Info Callbacks
+
+ ///
+ /// Called from native code when user info is received
+ /// Message format: "userId|username|email|phone"
+ ///
+ public void OnUserInfoReceived(string message)
+ {
+ Debug.Log($"[Tech3C Bridge] OnUserInfoReceived received: {message}");
+
+ if (userInfoCallback == null)
+ {
+ Debug.LogWarning("[Tech3C Bridge] No user info callback registered");
+ return;
+ }
+
+ try
+ {
+ string[] parts = message.Split('|');
+ if (parts.Length >= 4)
+ {
+ string userId = parts[0];
+ string username = parts[1];
+ string email = parts[2];
+ string phone = parts[3];
+
+ Debug.Log($"[Tech3C Bridge] Parsed user info: userId={userId}, username={username}");
+ userInfoCallback.OnUserInfoReceived(userId, username, email, phone);
+ }
+ else
+ {
+ Debug.LogError($"[Tech3C Bridge] Invalid message format: {message}");
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"[Tech3C Bridge] Error parsing user info message: {e.Message}");
+ }
+ }
+
+ ///
+ /// Called from native code when user info retrieval is cancelled
+ ///
+ public void OnUserInfoCancelled(string message)
+ {
+ Debug.Log("[Tech3C Bridge] OnUserInfoCancelled received");
+
+ if (userInfoCallback == null)
+ {
+ Debug.LogWarning("[Tech3C Bridge] No user info callback registered");
+ return;
+ }
+
+ userInfoCallback.OnUserInfoCancelled();
+ }
+
+ ///
+ /// Called from native code when a user info error occurs
+ /// Message format: "errorCode|errorMessage"
+ ///
+ public void OnUserInfoError(string message)
+ {
+ Debug.Log($"[Tech3C Bridge] OnUserInfoError received: {message}");
+
+ if (userInfoCallback == null)
+ {
+ Debug.LogWarning("[Tech3C Bridge] No user info callback registered");
+ return;
+ }
+
+ try
+ {
+ string[] parts = message.Split('|');
+ int errorCode = -1;
+ string errorMessage = "Unknown error";
+
+ if (parts.Length >= 1)
+ {
+ int.TryParse(parts[0], out errorCode);
+ }
+ if (parts.Length >= 2)
+ {
+ errorMessage = parts[1];
+ }
+
+ userInfoCallback.OnUserInfoError(errorCode, errorMessage);
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"[Tech3C Bridge] Error parsing user info error message: {e.Message}");
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Assets/Tech3C/Runtime/Tech3CSDKBridge.cs.meta b/Assets/Tech3C/Runtime/Tech3CSDKBridge.cs.meta
new file mode 100644
index 0000000000..353dcb291b
--- /dev/null
+++ b/Assets/Tech3C/Runtime/Tech3CSDKBridge.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 46cf26ff28457b04890e322b5b161a67
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Tech3C/Runtime/Tech3CiOSBridge.cs b/Assets/Tech3C/Runtime/Tech3CiOSBridge.cs
new file mode 100644
index 0000000000..2aa3a12b95
--- /dev/null
+++ b/Assets/Tech3C/Runtime/Tech3CiOSBridge.cs
@@ -0,0 +1,147 @@
+using UnityEngine;
+using System;
+
+namespace Tech3C
+{
+ ///
+ /// Bridge class for iOS platform to communicate with Tech3C native SDK
+ /// Note: iOS implementation requires native framework. This is a placeholder.
+ ///
+ public class Tech3CiOSBridge : ITech3CNativeBridge
+ {
+ private bool initialized = false;
+
+ ///
+ /// Initialize the Tech3C SDK
+ ///
+ public void Initialize(string clientId, string clientSecret, Tech3CConfig config)
+ {
+ Debug.LogWarning("[Tech3C] iOS platform is not yet supported. Please provide the native iOS framework.");
+ initialized = true;
+ }
+
+ ///
+ /// Show authentication screen
+ ///
+ public void ShowAuth(IAuthCallback callback)
+ {
+ Debug.LogWarning("[Tech3C] ShowAuth is not implemented for iOS platform");
+ callback?.OnAuthError(-1, "iOS platform not supported");
+ }
+
+ ///
+ /// Logout current user
+ ///
+ public void Logout(ILogoutCallback callback)
+ {
+ Debug.LogWarning("[Tech3C] Logout is not implemented for iOS platform");
+ callback?.OnLogoutError(-1, "iOS platform not supported");
+ }
+
+ ///
+ /// Get user information
+ ///
+ public void GetUserInfo(IUserInfoCallback callback)
+ {
+ Debug.LogWarning("[Tech3C] GetUserInfo is not implemented for iOS platform");
+ callback?.OnUserInfoError(-1, "iOS platform not supported");
+ }
+
+ ///
+ /// Check if user is logged in
+ ///
+ public bool IsLoggedIn()
+ {
+ Debug.LogWarning("[Tech3C] IsLoggedIn is not implemented for iOS platform");
+ return false;
+ }
+
+ ///
+ /// Get access token
+ ///
+ public string GetAccessToken()
+ {
+ Debug.LogWarning("[Tech3C] GetAccessToken is not implemented for iOS platform");
+ return null;
+ }
+
+ ///
+ /// Get refresh token
+ ///
+ public string GetRefreshToken()
+ {
+ Debug.LogWarning("[Tech3C] GetRefreshToken is not implemented for iOS platform");
+ return null;
+ }
+
+ ///
+ /// Get user ID
+ ///
+ public string GetUserId()
+ {
+ Debug.LogWarning("[Tech3C] GetUserId is not implemented for iOS platform");
+ return null;
+ }
+
+ ///
+ /// Get device ID
+ ///
+ public string GetDeviceId()
+ {
+ Debug.LogWarning("[Tech3C] GetDeviceId is not implemented for iOS platform");
+ return null;
+ }
+
+ ///
+ /// Get login time
+ ///
+ public long GetLoginTime()
+ {
+ Debug.LogWarning("[Tech3C] GetLoginTime is not implemented for iOS platform");
+ return 0;
+ }
+
+ ///
+ /// Get token expiry time
+ ///
+ public long GetTokenExpiry()
+ {
+ Debug.LogWarning("[Tech3C] GetTokenExpiry is not implemented for iOS platform");
+ return 0;
+ }
+
+ ///
+ /// Check if token is expired
+ ///
+ public bool IsTokenExpired()
+ {
+ Debug.LogWarning("[Tech3C] IsTokenExpired is not implemented for iOS platform");
+ return false;
+ }
+
+ ///
+ /// Set language
+ ///
+ public void SetLanguage(Language language)
+ {
+ Debug.LogWarning("[Tech3C] SetLanguage is not implemented for iOS platform");
+ }
+
+ ///
+ /// Set debug mode
+ ///
+ public void SetDebug(bool debug)
+ {
+ Debug.LogWarning("[Tech3C] SetDebug is not implemented for iOS platform");
+ }
+
+ ///
+ /// Cleanup resources
+ ///
+ public void Cleanup()
+ {
+ Debug.Log("[Tech3C] iOS bridge cleanup completed");
+ initialized = false;
+ }
+ }
+}
diff --git a/Assets/Tech3C/Runtime/Tech3CiOSBridge.cs.meta b/Assets/Tech3C/Runtime/Tech3CiOSBridge.cs.meta
new file mode 100644
index 0000000000..327d3972f3
--- /dev/null
+++ b/Assets/Tech3C/Runtime/Tech3CiOSBridge.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7199bd7af3b75294c84f9d6451df86d4
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Tech3C/Samples.meta b/Assets/Tech3C/Samples.meta
new file mode 100644
index 0000000000..a7dd7ed56a
--- /dev/null
+++ b/Assets/Tech3C/Samples.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: a947977eda82ae34b85413ea17ef265a
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Tech3C/Samples/Tech3CSimpleDemo.cs b/Assets/Tech3C/Samples/Tech3CSimpleDemo.cs
new file mode 100644
index 0000000000..0c609186bb
--- /dev/null
+++ b/Assets/Tech3C/Samples/Tech3CSimpleDemo.cs
@@ -0,0 +1,473 @@
+using UnityEngine;
+using UnityEngine.UI;
+using UnityEngine.EventSystems;
+using System;
+
+namespace Tech3C
+{
+ ///
+ /// Simple demo that creates UI programmatically
+ /// Just attach this script to any GameObject in your scene
+ ///
+ public class Tech3CSimpleDemo : MonoBehaviour
+ {
+ [Header("Configuration")]
+ public string clientId = "your_client_id";
+ public string clientSecret = "your_client_secret";
+
+ private Canvas canvas;
+ private Text logText;
+ private AuthCallback authCallback;
+ private LogoutCallback logoutCallback;
+
+ private GameObject dialogPanel;
+ private Text dialogText;
+ private Button dialogCloseButton;
+
+ private void Start()
+ {
+ EnsureEventSystem();
+ CreateUI();
+ SetupCallbacks();
+
+ // Auto-initialize SDK
+ if (!string.IsNullOrEmpty(clientId) && !string.IsNullOrEmpty(clientSecret))
+ {
+ Log($"Initializing Tech3C SDK...\n Client ID: {clientId}");
+ Tech3CSDK.Instance.Initialize(clientId, clientSecret);
+
+ if (Tech3CSDK.Instance.IsInitialized)
+ {
+ Log("✓ SDK Initialized Successfully!");
+ }
+ else
+ {
+ Log("✗ Failed to initialize SDK");
+ }
+ }
+ else
+ {
+ Log("⚠ Client ID and/or Client Secret not set. Please configure in Inspector.");
+ }
+
+ Debug.Log("[Tech3C Demo] Tech3C SDK Simple Demo Started");
+ }
+
+ private void EnsureEventSystem()
+ {
+ if (EventSystem.current == null)
+ {
+ GameObject eventSystemGO = new GameObject("EventSystem");
+ eventSystemGO.AddComponent();
+ eventSystemGO.AddComponent();
+ Debug.Log("[Tech3C Demo] EventSystem created");
+ }
+ }
+
+ private void CreateUI()
+ {
+ // Create Canvas
+ GameObject canvasGO = new GameObject("Tech3CDemoCanvas");
+ canvasGO.transform.SetParent(transform);
+ canvas = canvasGO.AddComponent