Working on UI improvement
This commit is contained in:
1 parent
0da1ae124c
commit
80af0e61ae
43 files changed
+883
-58
No files matched your search
@@ -1,5 +1,6 @@
|
|||||||
//precisionmediump float;
|
//precisionmediump float;
|
||||||
uniform sampler2D Texture;
|
uniform sampler2D Texture;
|
||||||
|
uniform float uAlpha;
|
||||||
varying vec2 texCoord;
|
varying vec2 texCoord;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
@@ -7,6 +8,6 @@ void main()
|
|||||||
vec4 color = texture2D(Texture,texCoord).rgba;
|
vec4 color = texture2D(Texture,texCoord).rgba;
|
||||||
//gl_FragColor = vec4(color.rgb*0.1 + vec3(0.9, 0.9, 0.9), 1.0);
|
//gl_FragColor = vec4(color.rgb*0.1 + vec3(0.9, 0.9, 0.9), 1.0);
|
||||||
|
|
||||||
gl_FragColor = color;
|
gl_FragColor = vec4(color.rgb, color.a * uAlpha);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
precision mediump float;
|
precision mediump float;
|
||||||
uniform sampler2D Texture;
|
uniform sampler2D Texture;
|
||||||
|
uniform float uAlpha;
|
||||||
varying vec2 texCoord;
|
varying vec2 texCoord;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
@@ -7,6 +8,6 @@ void main()
|
|||||||
vec4 color = texture2D(Texture,texCoord).rgba;
|
vec4 color = texture2D(Texture,texCoord).rgba;
|
||||||
//gl_FragColor = vec4(color.rgb*0.1 + vec3(0.9, 0.9, 0.9), 1.0);
|
//gl_FragColor = vec4(color.rgb*0.1 + vec3(0.9, 0.9, 0.9), 1.0);
|
||||||
|
|
||||||
gl_FragColor = color;
|
gl_FragColor = vec4(color.rgb, color.a * uAlpha);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"root": {
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"name": "hud_root",
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": "match_parent",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "LinearLayout",
|
||||||
|
"orientation": "vertical",
|
||||||
|
"vertical_align": "center",
|
||||||
|
"horizontal_align": "center",
|
||||||
|
"spacing": 0,
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": 600,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "phoneButton",
|
||||||
|
"width": 229,
|
||||||
|
"height": 229,
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/w/ui/img/Phone001_State=Default.png",
|
||||||
|
"hover": "resources/w/ui/img/Phone001_State=Selected.png",
|
||||||
|
"pressed": "resources/w/ui/img/Phone001_State=Tap.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "journalButton",
|
||||||
|
"width": 229,
|
||||||
|
"height": 229,
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/w/ui/img/Phone001_State=Default.png",
|
||||||
|
"hover": "resources/w/ui/img/Phone001_State=Selected.png",
|
||||||
|
"pressed": "resources/w/ui/img/Phone001_State=Tap.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
"spacing": 0,
|
"spacing": 0,
|
||||||
"width": "match_parent",
|
"width": "match_parent",
|
||||||
"height": 600,
|
"height": 600,
|
||||||
|
"x" : 300,
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"type": "StaticImage",
|
"type": "StaticImage",
|
||||||
@@ -20,7 +21,13 @@
|
|||||||
"width": 440,
|
"width": 440,
|
||||||
"height": 134,
|
"height": 134,
|
||||||
"horizontal_gravity": "center",
|
"horizontal_gravity": "center",
|
||||||
"texture": "resources/w/ui/img/Hint2.png"
|
"texture": "resources/w/ui/img/Hint1.png",
|
||||||
|
"pulse": {
|
||||||
|
"minScale": 0.92,
|
||||||
|
"maxScale": 1.08,
|
||||||
|
"periodMs": 1500
|
||||||
|
},
|
||||||
|
"fadeIn": { "durationMs": 600 }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "StaticImage",
|
"type": "StaticImage",
|
||||||
|
|||||||
@@ -5,42 +5,31 @@
|
|||||||
"width": "match_parent",
|
"width": "match_parent",
|
||||||
"height": "match_parent",
|
"height": "match_parent",
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"type": "TextButton",
|
"type": "LinearLayout",
|
||||||
"name": "inventory_button",
|
"orientation": "vertical",
|
||||||
"x": 50.0,
|
"vertical_align": "center",
|
||||||
"y": 50.0,
|
"horizontal_align": "center",
|
||||||
"width": 150.0,
|
"spacing": 0,
|
||||||
"height": 60.0,
|
"width": "match_parent",
|
||||||
"text": "Inventory",
|
"height": 600,
|
||||||
"fontSize": 24,
|
"y" : 300,
|
||||||
"fontPath": "resources/fonts/DroidSans.ttf",
|
"children": [
|
||||||
"textCentered": true,
|
{
|
||||||
"color": [1.0, 1.0, 1.0, 1.0],
|
"type": "StaticImage",
|
||||||
"textures": {
|
"name": "hint2",
|
||||||
"normal": "resources/w/red.png",
|
"width": 440,
|
||||||
"hover": "resources/w/red.png",
|
"height": 134,
|
||||||
"pressed": "resources/w/red.png"
|
"horizontal_gravity": "center",
|
||||||
}
|
"texture": "resources/w/ui/img/Hint2.png",
|
||||||
},
|
"pulse": {
|
||||||
{
|
"minScale": 0.92,
|
||||||
"type": "TextButton",
|
"maxScale": 1.08,
|
||||||
"name": "quest_journal_button",
|
"periodMs": 1500
|
||||||
"x": 220.0,
|
},
|
||||||
"y": 50.0,
|
"fadeIn": { "durationMs": 600 }
|
||||||
"width": 170.0,
|
}
|
||||||
"height": 60.0,
|
]
|
||||||
"text": "Quests",
|
}]
|
||||||
"fontSize": 24,
|
|
||||||
"fontPath": "resources/fonts/DroidSans.ttf",
|
|
||||||
"textCentered": true,
|
|
||||||
"color": [1.0, 1.0, 1.0, 1.0],
|
|
||||||
"textures": {
|
|
||||||
"normal": "resources/w/red.png",
|
|
||||||
"hover": "resources/w/red.png",
|
|
||||||
"pressed": "resources/w/red.png"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"root": {
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"name": "hud_root",
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": "match_parent",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "LinearLayout",
|
||||||
|
"orientation": "vertical",
|
||||||
|
"vertical_align": "center",
|
||||||
|
"horizontal_align": "center",
|
||||||
|
"spacing": 0,
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": 600,
|
||||||
|
"y" : 300,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "StaticImage",
|
||||||
|
"name": "hint3",
|
||||||
|
"width": 440,
|
||||||
|
"height": 134,
|
||||||
|
"horizontal_gravity": "center",
|
||||||
|
"texture": "resources/w/ui/img/Hint3.png",
|
||||||
|
"pulse": {
|
||||||
|
"minScale": 0.92,
|
||||||
|
"maxScale": 1.08,
|
||||||
|
"periodMs": 1500
|
||||||
|
},
|
||||||
|
"fadeIn": { "durationMs": 600 }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"root": {
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"name": "hud_root",
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": "match_parent",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "LinearLayout",
|
||||||
|
"orientation": "vertical",
|
||||||
|
"vertical_align": "center",
|
||||||
|
"horizontal_align": "center",
|
||||||
|
"spacing": 0,
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": 600,
|
||||||
|
"y" : 300,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "StaticImage",
|
||||||
|
"name": "hint4",
|
||||||
|
"width": 440,
|
||||||
|
"height": 134,
|
||||||
|
"horizontal_gravity": "center",
|
||||||
|
"texture": "resources/w/ui/img/Hint4.png",
|
||||||
|
"pulse": {
|
||||||
|
"minScale": 0.92,
|
||||||
|
"maxScale": 1.08,
|
||||||
|
"periodMs": 1500
|
||||||
|
},
|
||||||
|
"fadeIn": { "durationMs": 600 }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"root": {
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"name": "hud_root",
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": "match_parent",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "LinearLayout",
|
||||||
|
"orientation": "vertical",
|
||||||
|
"vertical_align": "center",
|
||||||
|
"horizontal_align": "center",
|
||||||
|
"spacing": 0,
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": 600,
|
||||||
|
"y" : 300,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "StaticImage",
|
||||||
|
"name": "hint5",
|
||||||
|
"width": 440,
|
||||||
|
"height": 134,
|
||||||
|
"horizontal_gravity": "center",
|
||||||
|
"texture": "resources/w/ui/img/Hint5.png",
|
||||||
|
"pulse": {
|
||||||
|
"minScale": 0.92,
|
||||||
|
"maxScale": 1.08,
|
||||||
|
"periodMs": 1500
|
||||||
|
},
|
||||||
|
"fadeIn": { "durationMs": 600 }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
"root": {
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"name": "hud_root",
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": "match_parent",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "LinearLayout",
|
||||||
|
"orientation": "vertical",
|
||||||
|
"vertical_align": "center",
|
||||||
|
"horizontal_align": "center",
|
||||||
|
"spacing": 0,
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": 600,
|
||||||
|
"y" : -250,
|
||||||
|
"x" : 300,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "StaticImage",
|
||||||
|
"name": "hint6a",
|
||||||
|
"width": 440,
|
||||||
|
"height": 134,
|
||||||
|
"horizontal_gravity": "center",
|
||||||
|
"texture": "resources/w/ui/img/Hint6a.png",
|
||||||
|
"pulse": {
|
||||||
|
"minScale": 0.92,
|
||||||
|
"maxScale": 1.08,
|
||||||
|
"periodMs": 1500
|
||||||
|
},
|
||||||
|
"fadeIn": {
|
||||||
|
"durationMs": 600
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "phoneButton",
|
||||||
|
"horizontal_gravity": "right",
|
||||||
|
"vertical_gravity": "top",
|
||||||
|
"x": -46,
|
||||||
|
"y" :-46,
|
||||||
|
"width": 229,
|
||||||
|
"height": 229,
|
||||||
|
"clickZoneWidth": 104,
|
||||||
|
"clickZoneHeight": 104,
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/w/ui/img/Phone001_State=Default.png",
|
||||||
|
"hover": "resources/w/ui/img/Phone001_State=Selected.png",
|
||||||
|
"pressed": "resources/w/ui/img/Phone001_State=Tap.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"root": {
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"name": "hud_root",
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": "match_parent",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "LinearLayout",
|
||||||
|
"orientation": "vertical",
|
||||||
|
"vertical_align": "center",
|
||||||
|
"horizontal_align": "center",
|
||||||
|
"spacing": 0,
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": 600,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "phoneButton",
|
||||||
|
"width": 229,
|
||||||
|
"height": 229,
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/w/ui/img/Phone001_State=Default.png",
|
||||||
|
"hover": "resources/w/ui/img/Phone001_State=Selected.png",
|
||||||
|
"pressed": "resources/w/ui/img/Phone001_State=Tap.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
{
|
||||||
|
"root": {
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"name": "hud_root",
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": "match_parent",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "LinearLayout",
|
||||||
|
"orientation": "horizontal",
|
||||||
|
"vertical_align": "center",
|
||||||
|
"horizontal_align": "right",
|
||||||
|
"spacing": 16,
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": 600,
|
||||||
|
"y" : -200,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "StaticImage",
|
||||||
|
"name": "hint6a",
|
||||||
|
"width": 440,
|
||||||
|
"height": 134,
|
||||||
|
"horizontal_gravity": "center",
|
||||||
|
"texture": "resources/w/ui/img/Hint6a.png",
|
||||||
|
"pulse": {
|
||||||
|
"minScale": 0.92,
|
||||||
|
"maxScale": 1.08,
|
||||||
|
"periodMs": 1500
|
||||||
|
},
|
||||||
|
"fadeIn": { "durationMs": 600 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "StaticImage",
|
||||||
|
"name": "hint6b",
|
||||||
|
"width": 440,
|
||||||
|
"height": 134,
|
||||||
|
"horizontal_gravity": "center",
|
||||||
|
"texture": "resources/w/ui/img/Hint6b.png",
|
||||||
|
"pulse": {
|
||||||
|
"minScale": 0.92,
|
||||||
|
"maxScale": 1.08,
|
||||||
|
"periodMs": 1500
|
||||||
|
},
|
||||||
|
"fadeIn": { "durationMs": 600 }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "phoneButton",
|
||||||
|
"width": 229,
|
||||||
|
"height": 229,
|
||||||
|
"x": 74,
|
||||||
|
"y" :-46,
|
||||||
|
"clickZoneWidth": 104,
|
||||||
|
"clickZoneHeight": 104,
|
||||||
|
"horizontal_gravity": "right",
|
||||||
|
"vertical_gravity": "top",
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/w/ui/img/Phone001_State=Default.png",
|
||||||
|
"hover": "resources/w/ui/img/Phone001_State=Selected.png",
|
||||||
|
"pressed": "resources/w/ui/img/Phone001_State=Tap.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "journalButton",
|
||||||
|
"width": 163,
|
||||||
|
"height": 163,
|
||||||
|
"x": -13,
|
||||||
|
"y" :-13,
|
||||||
|
"clickZoneWidth": 89,
|
||||||
|
"clickZoneHeight": 89,
|
||||||
|
"horizontal_gravity": "right",
|
||||||
|
"vertical_gravity": "top",
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/w/ui/img/Journal001_State=Default.png",
|
||||||
|
"hover": "resources/w/ui/img/Journal001_State=Selected.png",
|
||||||
|
"pressed": "resources/w/ui/img/Journal001_State=Tap.png"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"root": {
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"name": "hud_root",
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": "match_parent",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "LinearLayout",
|
||||||
|
"orientation": "vertical",
|
||||||
|
"vertical_align": "center",
|
||||||
|
"horizontal_align": "center",
|
||||||
|
"spacing": 0,
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": 600,
|
||||||
|
"y" : -250,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "StaticImage",
|
||||||
|
"name": "hint6b",
|
||||||
|
"width": 440,
|
||||||
|
"height": 134,
|
||||||
|
"horizontal_gravity": "center",
|
||||||
|
"texture": "resources/w/ui/img/Hint6b.png",
|
||||||
|
"pulse": {
|
||||||
|
"minScale": 0.92,
|
||||||
|
"maxScale": 1.08,
|
||||||
|
"periodMs": 1500
|
||||||
|
},
|
||||||
|
"fadeIn": { "durationMs": 600 }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "journalButton",
|
||||||
|
"horizontal_gravity": "right",
|
||||||
|
"vertical_gravity": "top",
|
||||||
|
"x": -13,
|
||||||
|
"y" :-13,
|
||||||
|
"width": 163,
|
||||||
|
"height": 163,
|
||||||
|
"clickZoneWidth": 89,
|
||||||
|
"clickZoneHeight": 89,
|
||||||
|
"horizontal_gravity": "right",
|
||||||
|
"vertical_gravity": "top",
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/w/ui/img/Journal001_State=Default.png",
|
||||||
|
"hover": "resources/w/ui/img/Journal001_State=Selected.png",
|
||||||
|
"pressed": "resources/w/ui/img/Journal001_State=Tap.png"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"root": {
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"name": "hud_root",
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": "match_parent",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "LinearLayout",
|
||||||
|
"orientation": "vertical",
|
||||||
|
"vertical_align": "center",
|
||||||
|
"horizontal_align": "center",
|
||||||
|
"spacing": 0,
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": 600,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "journalButton",
|
||||||
|
"width": 229,
|
||||||
|
"height": 229,
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/w/ui/img/Phone001_State=Default.png",
|
||||||
|
"hover": "resources/w/ui/img/Phone001_State=Selected.png",
|
||||||
|
"pressed": "resources/w/ui/img/Phone001_State=Tap.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"root": {
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"name": "hud_root",
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": "match_parent",
|
||||||
|
"vertical_align": "center",
|
||||||
|
"horizontal_align": "center",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "journalExitButton",
|
||||||
|
"horizontal_gravity": "center",
|
||||||
|
"vertical_gravity": "center",
|
||||||
|
"width": 1125,
|
||||||
|
"height": 520,
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/w/ui/img/QuestJournal001.png",
|
||||||
|
"hover": "resources/w/ui/img/QuestJournal001.png",
|
||||||
|
"pressed": "resources/w/ui/img/QuestJournal001.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"root": {
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"name": "hud_root",
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": "match_parent",
|
||||||
|
"vertical_align": "center",
|
||||||
|
"horizontal_align": "center",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "phoneExitButton",
|
||||||
|
"horizontal_gravity": "center",
|
||||||
|
"vertical_gravity": "center",
|
||||||
|
"width": 1232,
|
||||||
|
"height": 627,
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/w/ui/img/PhoneTest001.png",
|
||||||
|
"hover": "resources/w/ui/img/PhoneTest001.png",
|
||||||
|
"pressed": "resources/w/ui/img/PhoneTest001.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -15,7 +15,7 @@ namespace ZL {
|
|||||||
using std::max;
|
using std::max;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
constexpr float DEFAULT_ZOOM = 24.f;
|
constexpr float DEFAULT_ZOOM = 6.f;
|
||||||
|
|
||||||
class Environment {
|
class Environment {
|
||||||
public:
|
public:
|
||||||
|
|||||||
+52
-2
@@ -369,6 +369,24 @@ namespace ZL
|
|||||||
locations["uni_interior"]->onTeleport = teleportCallback;
|
locations["uni_interior"]->onTeleport = teleportCallback;
|
||||||
locations["location_dorm"]->onTeleport = teleportCallback;
|
locations["location_dorm"]->onTeleport = teleportCallback;
|
||||||
|
|
||||||
|
// Wire tutorial advance callbacks for all locations.
|
||||||
|
// advanceTutorialStep() guards against double-advancing, so sharing is safe.
|
||||||
|
for (auto& [name, loc] : locations) {
|
||||||
|
loc->dialogueSystem.setOnDialogueAdvanced([this]() {
|
||||||
|
menuManager.advanceTutorialStep();
|
||||||
|
});
|
||||||
|
loc->onPlayerFloorWalk = [this]() {
|
||||||
|
if (menuManager.tutorialStep == TutorialStep::Step2) {
|
||||||
|
menuManager.advanceTutorialStep();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wire inventory item-pickup callback for tutorial step4/5 item tracking.
|
||||||
|
inventory.onItemAdded = [this](const std::string& itemId) {
|
||||||
|
menuManager.onItemPickedUp(itemId);
|
||||||
|
};
|
||||||
|
|
||||||
currentLocation = locations["location_dorm"];
|
currentLocation = locations["location_dorm"];
|
||||||
currentLocation->scriptEngine.callLocationEnterCallback();
|
currentLocation->scriptEngine.callLocationEnterCallback();
|
||||||
|
|
||||||
@@ -552,6 +570,8 @@ namespace ZL
|
|||||||
|
|
||||||
updateDarklandsFlash(delta);
|
updateDarklandsFlash(delta);
|
||||||
|
|
||||||
|
menuManager.uiManager.update(static_cast<float>(delta));
|
||||||
|
|
||||||
if (currentLocation)
|
if (currentLocation)
|
||||||
{
|
{
|
||||||
currentLocation->update(delta);
|
currentLocation->update(delta);
|
||||||
@@ -661,6 +681,10 @@ namespace ZL
|
|||||||
if (Environment::zoom < zoomstep) {
|
if (Environment::zoom < zoomstep) {
|
||||||
Environment::zoom = zoomstep;
|
Environment::zoom = zoomstep;
|
||||||
}
|
}
|
||||||
|
// Tutorial step3 → step4: any mouse-wheel scroll counts as "zoom gesture".
|
||||||
|
if (menuManager.tutorialStep == TutorialStep::Step3) {
|
||||||
|
menuManager.advanceTutorialStep();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.type == SDL_KEYDOWN && event.key.repeat == 0) {
|
if (event.type == SDL_KEYDOWN && event.key.repeat == 0) {
|
||||||
@@ -722,8 +746,10 @@ namespace ZL
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_l:
|
case SDLK_l:
|
||||||
x = x - 1;
|
//x = x - 1;
|
||||||
std::cout << "current x: " << x << std::endl;
|
//std::cout << "current x: " << x << std::endl;
|
||||||
|
std::cout << "Azimuth: " << currentLocation->cameraAzimuth << std::endl;
|
||||||
|
std::cout << "Inclination: " << currentLocation->cameraInclination << std::endl;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_c:
|
case SDLK_c:
|
||||||
@@ -826,6 +852,10 @@ namespace ZL
|
|||||||
// movement threshold was crossed.
|
// movement threshold was crossed.
|
||||||
currentLocation->lastMouseX = eventX;
|
currentLocation->lastMouseX = eventX;
|
||||||
currentLocation->lastMouseY = eventY;
|
currentLocation->lastMouseY = eventY;
|
||||||
|
|
||||||
|
// Snapshot current angles so we can measure how far the user rotates.
|
||||||
|
dragStartAzimuth = currentLocation->cameraAzimuth;
|
||||||
|
dragStartInclination = currentLocation->cameraInclination;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -881,6 +911,13 @@ namespace ZL
|
|||||||
static const float zoomMin = 2.0f;
|
static const float zoomMin = 2.0f;
|
||||||
if (newZoom < zoomMin) newZoom = zoomMin;
|
if (newZoom < zoomMin) newZoom = zoomMin;
|
||||||
Environment::zoom = newZoom;
|
Environment::zoom = newZoom;
|
||||||
|
|
||||||
|
// Tutorial step3 → step4: detect a significant pinch-zoom (≥ 2 zoom units).
|
||||||
|
if (menuManager.tutorialStep == TutorialStep::Step3) {
|
||||||
|
if (std::abs(Environment::zoom - pinchStartZoom) >= 2.0f) {
|
||||||
|
menuManager.advanceTutorialStep();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::endPinch()
|
void Game::endPinch()
|
||||||
@@ -1004,6 +1041,19 @@ namespace ZL
|
|||||||
if (currentLocation) {
|
if (currentLocation) {
|
||||||
// Forwarded for dialogue hover and (when cameraDragging) camera rotation.
|
// Forwarded for dialogue hover and (when cameraDragging) camera rotation.
|
||||||
currentLocation->handleMotion(fingerId, eventX, eventY, mx, my);
|
currentLocation->handleMotion(fingerId, eventX, eventY, mx, my);
|
||||||
|
|
||||||
|
// Tutorial step1 → step2: detect a significant camera rotation on BOTH axes.
|
||||||
|
// ~0.15 rad (≈8.6°) per axis ensures the user intentionally panned in 2D,
|
||||||
|
// not just nudged a single axis by accident.
|
||||||
|
static constexpr float TUTORIAL_ROTATION_THRESHOLD = 0.15f;
|
||||||
|
if (cameraDragging
|
||||||
|
&& menuManager.tutorialStep == TutorialStep::Step1) {
|
||||||
|
float deltaAz = std::abs(currentLocation->cameraAzimuth - dragStartAzimuth);
|
||||||
|
float deltaInc = std::abs(currentLocation->cameraInclination - dragStartInclination);
|
||||||
|
if (deltaAz >= TUTORIAL_ROTATION_THRESHOLD && deltaInc >= TUTORIAL_ROTATION_THRESHOLD) {
|
||||||
|
menuManager.advanceTutorialStep();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,6 +87,11 @@ namespace ZL {
|
|||||||
float pinchStartDistance = 0.0f;
|
float pinchStartDistance = 0.0f;
|
||||||
float pinchStartZoom = 0.0f;
|
float pinchStartZoom = 0.0f;
|
||||||
|
|
||||||
|
// Tutorial: azimuth and inclination captured at the start of each camera drag,
|
||||||
|
// used to measure how far the user has rotated before advancing the tutorial step.
|
||||||
|
float dragStartAzimuth = 0.0f;
|
||||||
|
float dragStartInclination = 0.0f;
|
||||||
|
|
||||||
std::unique_ptr<AudioPlayerAsync> audioPlayer;
|
std::unique_ptr<AudioPlayerAsync> audioPlayer;
|
||||||
|
|
||||||
int64_t getSyncTimeMs();
|
int64_t getSyncTimeMs();
|
||||||
|
|||||||
@@ -1519,6 +1519,7 @@ namespace ZL
|
|||||||
targetInteractNpc = nullptr;
|
targetInteractNpc = nullptr;
|
||||||
targetInteractNpcIndex = -1;
|
targetInteractNpcIndex = -1;
|
||||||
targetTeleportZone = nullptr;
|
targetTeleportZone = nullptr;
|
||||||
|
if (onPlayerFloorWalk) onPlayerFloorWalk();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
+6
-3
@@ -51,12 +51,11 @@ namespace ZL
|
|||||||
|
|
||||||
std::vector<InteractiveObject> interactiveObjects;
|
std::vector<InteractiveObject> interactiveObjects;
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<Character> player;
|
std::unique_ptr<Character> player;
|
||||||
std::vector<std::unique_ptr<Character>> npcs;
|
std::vector<std::unique_ptr<Character>> npcs;
|
||||||
|
|
||||||
float cameraAzimuth = 0.0f;
|
float cameraAzimuth = -2.35;
|
||||||
float cameraInclination = M_PI * 30.f / 180.f;
|
float cameraInclination = 1.1036;//M_PI * 30.f / 180.f;
|
||||||
|
|
||||||
std::vector<PathFinder> navigationMaps;
|
std::vector<PathFinder> navigationMaps;
|
||||||
PathFinder* navigation = nullptr;
|
PathFinder* navigation = nullptr;
|
||||||
@@ -87,6 +86,10 @@ namespace ZL
|
|||||||
// Read by draw functions and raycast — do not write from Location code.
|
// Read by draw functions and raycast — do not write from Location code.
|
||||||
bool isDarklands = false;
|
bool isDarklands = false;
|
||||||
|
|
||||||
|
// Called when the player successfully taps the ground and a floor walk target is set.
|
||||||
|
// Used by the tutorial system to detect the "tap to walk" gesture.
|
||||||
|
std::function<void()> onPlayerFloorWalk;
|
||||||
|
|
||||||
// Set by Game after setup(). Lua and onDeathAnimComplete call this to
|
// Set by Game after setup(). Lua and onDeathAnimComplete call this to
|
||||||
// request the transition without coupling Location to Game directly.
|
// request the transition without coupling Location to Game directly.
|
||||||
std::function<bool()> requestDarklandsTransition;
|
std::function<bool()> requestDarklandsTransition;
|
||||||
|
|||||||
+123
-1
@@ -34,7 +34,16 @@ namespace ZL {
|
|||||||
inventory = &inv;
|
inventory = &inv;
|
||||||
|
|
||||||
//hudRoot = loadUiFromFile("resources/config2/hud.json", renderer, zipFile);
|
//hudRoot = loadUiFromFile("resources/config2/hud.json", renderer, zipFile);
|
||||||
hudRoot = loadUiFromFile("resources/w/ui/hud_step0.json", renderer, zipFile);
|
hudRoot = loadUiFromFile("resources/w/ui/hud_step0.json", renderer, zipFile);
|
||||||
|
hudStep1Root = loadUiFromFile("resources/w/ui/hud_step1.json", renderer, zipFile);
|
||||||
|
hudStep2Root = loadUiFromFile("resources/w/ui/hud_step2.json", renderer, zipFile);
|
||||||
|
hudStep3Root = loadUiFromFile("resources/w/ui/hud_step3.json", renderer, zipFile);
|
||||||
|
hudStep4Root = loadUiFromFile("resources/w/ui/hud_step4.json", renderer, zipFile);
|
||||||
|
hudStep5aRoot = loadUiFromFile("resources/w/ui/hud_step5a.json", renderer, zipFile);
|
||||||
|
hudStep5bRoot = loadUiFromFile("resources/w/ui/hud_step5b.json", renderer, zipFile);
|
||||||
|
hudStep5abRoot = loadUiFromFile("resources/w/ui/hud_step5ab.json", renderer, zipFile);
|
||||||
|
phoneScreenRoot = loadUiFromFile("resources/w/ui/screen_phone.json", renderer, zipFile);
|
||||||
|
journalScreenRoot= loadUiFromFile("resources/w/ui/screen_journal.json", renderer, zipFile);
|
||||||
inventoryRoot = loadUiFromFile("resources/config2/ui_inventory.json", renderer, zipFile);
|
inventoryRoot = loadUiFromFile("resources/config2/ui_inventory.json", renderer, zipFile);
|
||||||
questJournalRoot = loadUiFromFile("resources/config2/ui_quest_journal.json", renderer, zipFile);
|
questJournalRoot = loadUiFromFile("resources/config2/ui_quest_journal.json", renderer, zipFile);
|
||||||
|
|
||||||
@@ -124,6 +133,119 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MenuManager::openPhoneScreen() {
|
||||||
|
state = GameState::PhoneScreen;
|
||||||
|
tutorialPhoneScreenOpened = true;
|
||||||
|
// Hide the phone hint on the current HUD so it stays hidden when we return.
|
||||||
|
uiManager.setNodeVisible("hint6a", false);
|
||||||
|
uiManager.pushMenuFromSavedRoot(phoneScreenRoot);
|
||||||
|
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
|
||||||
|
closePhoneScreen();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void MenuManager::closePhoneScreen() {
|
||||||
|
state = GameState::Gameplay;
|
||||||
|
uiManager.popMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MenuManager::openJournalScreen() {
|
||||||
|
state = GameState::JournalScreen;
|
||||||
|
tutorialJournalScreenOpened = true;
|
||||||
|
// Hide the journal hint on the current HUD so it stays hidden when we return.
|
||||||
|
uiManager.setNodeVisible("hint6b", false);
|
||||||
|
uiManager.pushMenuFromSavedRoot(journalScreenRoot);
|
||||||
|
uiManager.setButtonCallback("journalExitButton", [this](const std::string&) {
|
||||||
|
closeJournalScreen();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void MenuManager::closeJournalScreen() {
|
||||||
|
state = GameState::Gameplay;
|
||||||
|
uiManager.popMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registers phoneButton / journalButton callbacks on the current HUD root
|
||||||
|
// and hides any hints that have already been completed.
|
||||||
|
// Called after every replaceRoot during step 5.
|
||||||
|
void MenuManager::setupStep5Callbacks() {
|
||||||
|
if (uiManager.findButton("phoneButton")) {
|
||||||
|
uiManager.setButtonCallback("phoneButton", [this](const std::string&) {
|
||||||
|
openPhoneScreen();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (uiManager.findButton("journalButton")) {
|
||||||
|
uiManager.setButtonCallback("journalButton", [this](const std::string&) {
|
||||||
|
openJournalScreen();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (tutorialPhoneScreenOpened) uiManager.setNodeVisible("hint6a", false);
|
||||||
|
if (tutorialJournalScreenOpened) uiManager.setNodeVisible("hint6b", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MenuManager::advanceTutorialStep() {
|
||||||
|
std::shared_ptr<UiNode> nextRoot;
|
||||||
|
|
||||||
|
switch (tutorialStep) {
|
||||||
|
case TutorialStep::Step0:
|
||||||
|
tutorialStep = TutorialStep::Step1;
|
||||||
|
nextRoot = hudStep1Root;
|
||||||
|
break;
|
||||||
|
case TutorialStep::Step1:
|
||||||
|
tutorialStep = TutorialStep::Step2;
|
||||||
|
nextRoot = hudStep2Root;
|
||||||
|
break;
|
||||||
|
case TutorialStep::Step2:
|
||||||
|
tutorialStep = TutorialStep::Step3;
|
||||||
|
nextRoot = hudStep3Root;
|
||||||
|
break;
|
||||||
|
case TutorialStep::Step3:
|
||||||
|
tutorialStep = TutorialStep::Step4;
|
||||||
|
nextRoot = hudStep4Root;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return; // Step4/Step5 transitions are driven by onItemPickedUp
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == GameState::Gameplay && nextRoot) {
|
||||||
|
uiManager.replaceRoot(nextRoot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MenuManager::onItemPickedUp(const std::string& itemId) {
|
||||||
|
if (tutorialStep != TutorialStep::Step4 && tutorialStep != TutorialStep::Step5) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemId == "phone") tutorialPhonePickedUp = true;
|
||||||
|
if (itemId == "journal") tutorialJournalPickedUp = true;
|
||||||
|
|
||||||
|
if (tutorialStep == TutorialStep::Step4) {
|
||||||
|
tutorialStep = TutorialStep::Step5;
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshItemPickupHud();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MenuManager::refreshItemPickupHud() {
|
||||||
|
if (state != GameState::Gameplay) return;
|
||||||
|
|
||||||
|
std::shared_ptr<UiNode> nextRoot;
|
||||||
|
if (tutorialPhonePickedUp && tutorialJournalPickedUp) {
|
||||||
|
nextRoot = hudStep5abRoot;
|
||||||
|
} else if (tutorialPhonePickedUp) {
|
||||||
|
nextRoot = hudStep5aRoot;
|
||||||
|
} else if (tutorialJournalPickedUp) {
|
||||||
|
nextRoot = hudStep5bRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextRoot) {
|
||||||
|
uiManager.replaceRoot(nextRoot);
|
||||||
|
// Register item-screen buttons and re-apply any already-completed hint visibility.
|
||||||
|
setupStep5Callbacks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MenuManager::refreshQuestJournalUi() {
|
void MenuManager::refreshQuestJournalUi() {
|
||||||
visibleQuestIds.clear();
|
visibleQuestIds.clear();
|
||||||
auto quests = questJournal.getVisibleQuests();
|
auto quests = questJournal.getVisibleQuests();
|
||||||
|
|||||||
+38
-1
@@ -16,7 +16,18 @@ namespace ZL {
|
|||||||
enum class GameState {
|
enum class GameState {
|
||||||
Gameplay,
|
Gameplay,
|
||||||
Inventory,
|
Inventory,
|
||||||
QuestJournal
|
QuestJournal,
|
||||||
|
PhoneScreen,
|
||||||
|
JournalScreen
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class TutorialStep {
|
||||||
|
Step0, // Dialogue hint: "click to advance"
|
||||||
|
Step1, // Camera rotation hint
|
||||||
|
Step2, // Floor tap / walk hint
|
||||||
|
Step3, // Pinch-zoom hint
|
||||||
|
Step4, // Pick-up item hint
|
||||||
|
Step5, // Post-pickup reaction (sub-state: which items were collected)
|
||||||
};
|
};
|
||||||
|
|
||||||
class MenuManager {
|
class MenuManager {
|
||||||
@@ -38,6 +49,16 @@ namespace ZL {
|
|||||||
bool isInventoryOpen() const { return state == GameState::Inventory; }
|
bool isInventoryOpen() const { return state == GameState::Inventory; }
|
||||||
bool isQuestJournalOpen() const { return state == GameState::QuestJournal; }
|
bool isQuestJournalOpen() const { return state == GameState::QuestJournal; }
|
||||||
|
|
||||||
|
void openPhoneScreen();
|
||||||
|
void closePhoneScreen();
|
||||||
|
void openJournalScreen();
|
||||||
|
void closeJournalScreen();
|
||||||
|
|
||||||
|
void advanceTutorialStep();
|
||||||
|
void onItemPickedUp(const std::string& itemId);
|
||||||
|
|
||||||
|
TutorialStep tutorialStep = TutorialStep::Step0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Renderer& renderer;
|
Renderer& renderer;
|
||||||
|
|
||||||
@@ -45,11 +66,27 @@ namespace ZL {
|
|||||||
void enterGameplay();
|
void enterGameplay();
|
||||||
void refreshQuestJournalUi();
|
void refreshQuestJournalUi();
|
||||||
void selectQuestByIndex(int index);
|
void selectQuestByIndex(int index);
|
||||||
|
void refreshItemPickupHud();
|
||||||
|
void setupStep5Callbacks();
|
||||||
|
|
||||||
GameState state = GameState::Gameplay;
|
GameState state = GameState::Gameplay;
|
||||||
Inventory* inventory = nullptr;
|
Inventory* inventory = nullptr;
|
||||||
|
|
||||||
|
bool tutorialPhonePickedUp = false;
|
||||||
|
bool tutorialJournalPickedUp = false;
|
||||||
|
bool tutorialPhoneScreenOpened = false;
|
||||||
|
bool tutorialJournalScreenOpened = false;
|
||||||
|
|
||||||
std::shared_ptr<UiNode> hudRoot;
|
std::shared_ptr<UiNode> hudRoot;
|
||||||
|
std::shared_ptr<UiNode> hudStep1Root;
|
||||||
|
std::shared_ptr<UiNode> hudStep2Root;
|
||||||
|
std::shared_ptr<UiNode> hudStep3Root;
|
||||||
|
std::shared_ptr<UiNode> hudStep4Root;
|
||||||
|
std::shared_ptr<UiNode> hudStep5aRoot;
|
||||||
|
std::shared_ptr<UiNode> hudStep5bRoot;
|
||||||
|
std::shared_ptr<UiNode> hudStep5abRoot;
|
||||||
|
std::shared_ptr<UiNode> phoneScreenRoot;
|
||||||
|
std::shared_ptr<UiNode> journalScreenRoot;
|
||||||
std::shared_ptr<UiNode> inventoryRoot;
|
std::shared_ptr<UiNode> inventoryRoot;
|
||||||
std::shared_ptr<UiNode> questJournalRoot;
|
std::shared_ptr<UiNode> questJournalRoot;
|
||||||
|
|
||||||
|
|||||||
+68
-6
@@ -373,9 +373,17 @@ namespace ZL {
|
|||||||
void UiStaticImage::draw(Renderer& renderer) const {
|
void UiStaticImage::draw(Renderer& renderer) const {
|
||||||
if (!texture) return;
|
if (!texture) return;
|
||||||
|
|
||||||
|
const float alpha = (fadeInEnabled && fadeInDurationMs > 0.0f)
|
||||||
|
? std::clamp(fadeInElapsedMs / fadeInDurationMs, 0.0f, 1.0f)
|
||||||
|
: 1.0f;
|
||||||
|
|
||||||
renderer.RenderUniform1i(textureUniformName, 0);
|
renderer.RenderUniform1i(textureUniformName, 0);
|
||||||
|
renderer.RenderUniform1f("uAlpha", alpha);
|
||||||
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
|
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
|
||||||
renderer.DrawVertexRenderStruct(mesh);
|
renderer.DrawVertexRenderStruct(mesh);
|
||||||
|
if (alpha < 1.0f) {
|
||||||
|
renderer.RenderUniform1f("uAlpha", 1.0f); // restore for subsequent draws
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UiTextField::draw(Renderer& renderer) const {
|
void UiTextField::draw(Renderer& renderer) const {
|
||||||
@@ -504,6 +512,8 @@ namespace ZL {
|
|||||||
btn->texDisabled = loadTex("disabled");
|
btn->texDisabled = loadTex("disabled");
|
||||||
|
|
||||||
btn->border = j.value("border", 0.0f);
|
btn->border = j.value("border", 0.0f);
|
||||||
|
if (j.contains("clickZoneWidth")) btn->clickZoneWidth = j["clickZoneWidth"].get<float>();
|
||||||
|
if (j.contains("clickZoneHeight")) btn->clickZoneHeight = j["clickZoneHeight"].get<float>();
|
||||||
|
|
||||||
node->button = btn;
|
node->button = btn;
|
||||||
}
|
}
|
||||||
@@ -616,6 +626,8 @@ namespace ZL {
|
|||||||
if (j.contains("color") && j["color"].is_array() && j["color"].size() == 4) {
|
if (j.contains("color") && j["color"].is_array() && j["color"].size() == 4) {
|
||||||
for (int i = 0; i < 4; ++i) tb->color[i] = j["color"][i].get<float>();
|
for (int i = 0; i < 4; ++i) tb->color[i] = j["color"][i].get<float>();
|
||||||
}
|
}
|
||||||
|
if (j.contains("clickZoneWidth")) tb->clickZoneWidth = j["clickZoneWidth"].get<float>();
|
||||||
|
if (j.contains("clickZoneHeight")) tb->clickZoneHeight = j["clickZoneHeight"].get<float>();
|
||||||
|
|
||||||
tb->textRenderer = std::make_unique<TextRenderer>();
|
tb->textRenderer = std::make_unique<TextRenderer>();
|
||||||
if (!tb->textRenderer->init(renderer, tb->fontPath, tb->fontSize, zipFile)) {
|
if (!tb->textRenderer->init(renderer, tb->fontPath, tb->fontSize, zipFile)) {
|
||||||
@@ -679,6 +691,23 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
node->staticImage = img;
|
node->staticImage = img;
|
||||||
|
|
||||||
|
// Optional fade-in on display
|
||||||
|
if (j.contains("fadeIn") && j["fadeIn"].is_object()) {
|
||||||
|
const auto& fi = j["fadeIn"];
|
||||||
|
img->fadeInEnabled = true;
|
||||||
|
img->fadeInDurationMs = fi.value("durationMs", 1000.0f);
|
||||||
|
img->fadeInElapsedMs = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional pulse-scale animation
|
||||||
|
if (j.contains("pulse") && j["pulse"].is_object()) {
|
||||||
|
const auto& p = j["pulse"];
|
||||||
|
node->pulseEnabled = true;
|
||||||
|
node->pulseMinScale = p.value("minScale", 0.9f);
|
||||||
|
node->pulseMaxScale = p.value("maxScale", 1.1f);
|
||||||
|
node->pulsePeriodMs = p.value("periodMs", 1000.0f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeStr == "TextView") {
|
if (typeStr == "TextView") {
|
||||||
@@ -777,6 +806,7 @@ namespace ZL {
|
|||||||
textViews.clear();
|
textViews.clear();
|
||||||
textFields.clear();
|
textFields.clear();
|
||||||
staticImages.clear();
|
staticImages.clear();
|
||||||
|
pulsingNodes.clear();
|
||||||
collectButtonsAndSliders(root);
|
collectButtonsAndSliders(root);
|
||||||
|
|
||||||
nodeActiveAnims.clear();
|
nodeActiveAnims.clear();
|
||||||
@@ -1020,6 +1050,12 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
if (node->staticImage) {
|
if (node->staticImage) {
|
||||||
staticImages.push_back(node->staticImage);
|
staticImages.push_back(node->staticImage);
|
||||||
|
if (node->staticImage->fadeInEnabled) {
|
||||||
|
node->staticImage->fadeInElapsedMs = 0.0f; // restart fade every time UI is shown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (node->pulseEnabled && node->staticImage) {
|
||||||
|
pulsingNodes.push_back(node);
|
||||||
}
|
}
|
||||||
for (auto& c : node->children) collectButtonsAndSliders(c);
|
for (auto& c : node->children) collectButtonsAndSliders(c);
|
||||||
}
|
}
|
||||||
@@ -1128,6 +1164,7 @@ namespace ZL {
|
|||||||
prev.textViews = textViews;
|
prev.textViews = textViews;
|
||||||
prev.textFields = textFields;
|
prev.textFields = textFields;
|
||||||
prev.staticImages = staticImages;
|
prev.staticImages = staticImages;
|
||||||
|
prev.pulsingNodes = pulsingNodes;
|
||||||
prev.pressedButtons = pressedButtons;
|
prev.pressedButtons = pressedButtons;
|
||||||
prev.pressedTextButtons = pressedTextButtons;
|
prev.pressedTextButtons = pressedTextButtons;
|
||||||
prev.pressedSliders = pressedSliders;
|
prev.pressedSliders = pressedSliders;
|
||||||
@@ -1190,6 +1227,7 @@ namespace ZL {
|
|||||||
textViews = s.textViews;
|
textViews = s.textViews;
|
||||||
textFields = s.textFields;
|
textFields = s.textFields;
|
||||||
staticImages = s.staticImages;
|
staticImages = s.staticImages;
|
||||||
|
pulsingNodes = s.pulsingNodes;
|
||||||
pressedButtons = s.pressedButtons;
|
pressedButtons = s.pressedButtons;
|
||||||
pressedTextButtons = s.pressedTextButtons;
|
pressedTextButtons = s.pressedTextButtons;
|
||||||
pressedSliders = s.pressedSliders;
|
pressedSliders = s.pressedSliders;
|
||||||
@@ -1233,6 +1271,7 @@ namespace ZL {
|
|||||||
renderer.PushProjectionMatrix(Environment::projectionWidth, Environment::projectionHeight, -1, 1);
|
renderer.PushProjectionMatrix(Environment::projectionWidth, Environment::projectionHeight, -1, 1);
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.LoadIdentity();
|
renderer.LoadIdentity();
|
||||||
|
renderer.RenderUniform1f("uAlpha", 1.0f); // ensure alpha is fully opaque unless a widget overrides it
|
||||||
|
|
||||||
std::function<void(const std::shared_ptr<UiNode>&)> drawNode =
|
std::function<void(const std::shared_ptr<UiNode>&)> drawNode =
|
||||||
[&](const std::shared_ptr<UiNode>& node) {
|
[&](const std::shared_ptr<UiNode>& node) {
|
||||||
@@ -1301,6 +1340,29 @@ namespace ZL {
|
|||||||
void UiManager::update(float deltaMs) {
|
void UiManager::update(float deltaMs) {
|
||||||
if (!root) return;
|
if (!root) return;
|
||||||
|
|
||||||
|
// Fade-in animations (StaticImage elements with fadeInEnabled == true)
|
||||||
|
for (auto& img : staticImages) {
|
||||||
|
if (!img || !img->fadeInEnabled) continue;
|
||||||
|
if (img->fadeInElapsedMs < img->fadeInDurationMs) {
|
||||||
|
img->fadeInElapsedMs += deltaMs;
|
||||||
|
if (img->fadeInElapsedMs > img->fadeInDurationMs)
|
||||||
|
img->fadeInElapsedMs = img->fadeInDurationMs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pulse-scale animations (StaticImage nodes with pulseEnabled == true)
|
||||||
|
for (auto& node : pulsingNodes) {
|
||||||
|
if (!node) continue;
|
||||||
|
node->pulseElapsedMs += deltaMs;
|
||||||
|
if (node->pulseElapsedMs >= node->pulsePeriodMs)
|
||||||
|
node->pulseElapsedMs = std::fmod(node->pulseElapsedMs, node->pulsePeriodMs);
|
||||||
|
const float phase = node->pulseElapsedMs / node->pulsePeriodMs;
|
||||||
|
const float t = (std::sin(phase * 2.0f * static_cast<float>(M_PI)) + 1.0f) * 0.5f;
|
||||||
|
const float s = node->pulseMinScale + (node->pulseMaxScale - node->pulseMinScale) * t;
|
||||||
|
node->scaleX = s;
|
||||||
|
node->scaleY = s;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::pair<std::shared_ptr<UiNode>, size_t>> animationsToRemove;
|
std::vector<std::pair<std::shared_ptr<UiNode>, size_t>> animationsToRemove;
|
||||||
std::vector<std::function<void()>> pendingCallbacks;
|
std::vector<std::function<void()>> pendingCallbacks;
|
||||||
|
|
||||||
@@ -1490,7 +1552,7 @@ namespace ZL {
|
|||||||
for (auto& b : buttons) {
|
for (auto& b : buttons) {
|
||||||
if (b->state != ButtonState::Disabled)
|
if (b->state != ButtonState::Disabled)
|
||||||
{
|
{
|
||||||
if (b->rect.containsConsideringBorder((float)x, (float)y, b->border)) {
|
if (b->getClickZoneRect().containsConsideringBorder((float)x, (float)y, b->border)) {
|
||||||
if (b->state != ButtonState::Pressed) b->state = ButtonState::Hover;
|
if (b->state != ButtonState::Pressed) b->state = ButtonState::Hover;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -1501,7 +1563,7 @@ namespace ZL {
|
|||||||
for (auto& tb : textButtons) {
|
for (auto& tb : textButtons) {
|
||||||
if (tb->state != ButtonState::Disabled)
|
if (tb->state != ButtonState::Disabled)
|
||||||
{
|
{
|
||||||
if (tb->rect.containsConsideringBorder((float)x, (float)y, tb->border)) {
|
if (tb->getClickZoneRect().containsConsideringBorder((float)x, (float)y, tb->border)) {
|
||||||
if (tb->state != ButtonState::Pressed) tb->state = ButtonState::Hover;
|
if (tb->state != ButtonState::Pressed) tb->state = ButtonState::Hover;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -1534,7 +1596,7 @@ namespace ZL {
|
|||||||
for (auto& b : buttons) {
|
for (auto& b : buttons) {
|
||||||
if (b->state != ButtonState::Disabled)
|
if (b->state != ButtonState::Disabled)
|
||||||
{
|
{
|
||||||
if (b->rect.containsConsideringBorder((float)x, (float)y, b->border)) {
|
if (b->getClickZoneRect().containsConsideringBorder((float)x, (float)y, b->border)) {
|
||||||
b->state = ButtonState::Pressed;
|
b->state = ButtonState::Pressed;
|
||||||
pressedButtons[fingerId] = b;
|
pressedButtons[fingerId] = b;
|
||||||
if (b->onPress) b->onPress(b->name);
|
if (b->onPress) b->onPress(b->name);
|
||||||
@@ -1546,7 +1608,7 @@ namespace ZL {
|
|||||||
for (auto& tb : textButtons) {
|
for (auto& tb : textButtons) {
|
||||||
if (tb->state != ButtonState::Disabled)
|
if (tb->state != ButtonState::Disabled)
|
||||||
{
|
{
|
||||||
if (tb->rect.containsConsideringBorder((float)x, (float)y, tb->border)) {
|
if (tb->getClickZoneRect().containsConsideringBorder((float)x, (float)y, tb->border)) {
|
||||||
tb->state = ButtonState::Pressed;
|
tb->state = ButtonState::Pressed;
|
||||||
pressedTextButtons[fingerId] = tb;
|
pressedTextButtons[fingerId] = tb;
|
||||||
if (tb->onPress) tb->onPress(tb->name);
|
if (tb->onPress) tb->onPress(tb->name);
|
||||||
@@ -1593,7 +1655,7 @@ namespace ZL {
|
|||||||
if (btnIt != pressedButtons.end()) {
|
if (btnIt != pressedButtons.end()) {
|
||||||
auto b = btnIt->second;
|
auto b = btnIt->second;
|
||||||
if (b) {
|
if (b) {
|
||||||
bool contains = b->rect.contains((float)x, (float)y);
|
bool contains = b->getClickZoneRect().contains((float)x, (float)y);
|
||||||
if (b->state == ButtonState::Pressed) {
|
if (b->state == ButtonState::Pressed) {
|
||||||
if (contains) {
|
if (contains) {
|
||||||
clicked.push_back(b);
|
clicked.push_back(b);
|
||||||
@@ -1609,7 +1671,7 @@ namespace ZL {
|
|||||||
if (tbIt != pressedTextButtons.end()) {
|
if (tbIt != pressedTextButtons.end()) {
|
||||||
auto tb = tbIt->second;
|
auto tb = tbIt->second;
|
||||||
if (tb) {
|
if (tb) {
|
||||||
bool contains = tb->rect.contains((float)x, (float)y);
|
bool contains = tb->getClickZoneRect().contains((float)x, (float)y);
|
||||||
if (tb->state == ButtonState::Pressed) {
|
if (tb->state == ButtonState::Pressed) {
|
||||||
if (contains) {
|
if (contains) {
|
||||||
clickedText.push_back(tb);
|
clickedText.push_back(tb);
|
||||||
|
|||||||
@@ -93,6 +93,17 @@ namespace ZL {
|
|||||||
|
|
||||||
VertexRenderStruct mesh;
|
VertexRenderStruct mesh;
|
||||||
|
|
||||||
|
// Optional explicit hit-test zone, centred on rect.
|
||||||
|
// 0 means "use rect.w / rect.h" (default behaviour).
|
||||||
|
float clickZoneWidth = 0.0f;
|
||||||
|
float clickZoneHeight = 0.0f;
|
||||||
|
|
||||||
|
UiRect getClickZoneRect() const {
|
||||||
|
float cw = (clickZoneWidth > 0.0f) ? clickZoneWidth : rect.w;
|
||||||
|
float ch = (clickZoneHeight > 0.0f) ? clickZoneHeight : rect.h;
|
||||||
|
return { rect.x + (rect.w - cw) * 0.5f, rect.y + (rect.h - ch) * 0.5f, cw, ch };
|
||||||
|
}
|
||||||
|
|
||||||
std::function<void(const std::string&)> onClick;
|
std::function<void(const std::string&)> onClick;
|
||||||
std::function<void(const std::string&)> onPress; // fires on touch/mouse down
|
std::function<void(const std::string&)> onPress; // fires on touch/mouse down
|
||||||
|
|
||||||
@@ -139,6 +150,17 @@ namespace ZL {
|
|||||||
ButtonState state = ButtonState::Normal;
|
ButtonState state = ButtonState::Normal;
|
||||||
VertexRenderStruct mesh;
|
VertexRenderStruct mesh;
|
||||||
|
|
||||||
|
// Optional explicit hit-test zone, centred on rect.
|
||||||
|
// 0 means "use rect.w / rect.h" (default behaviour).
|
||||||
|
float clickZoneWidth = 0.0f;
|
||||||
|
float clickZoneHeight = 0.0f;
|
||||||
|
|
||||||
|
UiRect getClickZoneRect() const {
|
||||||
|
float cw = (clickZoneWidth > 0.0f) ? clickZoneWidth : rect.w;
|
||||||
|
float ch = (clickZoneHeight > 0.0f) ? clickZoneHeight : rect.h;
|
||||||
|
return { rect.x + (rect.w - cw) * 0.5f, rect.y + (rect.h - ch) * 0.5f, cw, ch };
|
||||||
|
}
|
||||||
|
|
||||||
// Text drawn on top of the button
|
// Text drawn on top of the button
|
||||||
std::string text;
|
std::string text;
|
||||||
std::string fontPath = "resources/fonts/DroidSans.ttf";
|
std::string fontPath = "resources/fonts/DroidSans.ttf";
|
||||||
@@ -209,6 +231,11 @@ namespace ZL {
|
|||||||
|
|
||||||
VertexRenderStruct mesh;
|
VertexRenderStruct mesh;
|
||||||
|
|
||||||
|
// Fade-in on first display (triggers each time the containing UI is shown)
|
||||||
|
bool fadeInEnabled = false;
|
||||||
|
float fadeInDurationMs = 1000.0f;
|
||||||
|
float fadeInElapsedMs = 0.0f; // runtime, reset by collectButtonsAndSliders
|
||||||
|
|
||||||
void buildMesh();
|
void buildMesh();
|
||||||
void draw(Renderer& renderer) const;
|
void draw(Renderer& renderer) const;
|
||||||
};
|
};
|
||||||
@@ -236,6 +263,13 @@ namespace ZL {
|
|||||||
|
|
||||||
bool visible = true;
|
bool visible = true;
|
||||||
|
|
||||||
|
// Pulse-scale animation (StaticImage only; auto-starts when pulseEnabled == true)
|
||||||
|
bool pulseEnabled = false;
|
||||||
|
float pulseMinScale = 1.0f;
|
||||||
|
float pulseMaxScale = 1.1f;
|
||||||
|
float pulsePeriodMs = 1000.0f;
|
||||||
|
float pulseElapsedMs = 0.0f; // runtime, not persisted in JSON
|
||||||
|
|
||||||
// Иерархия
|
// Иерархия
|
||||||
std::vector<std::shared_ptr<UiNode>> children;
|
std::vector<std::shared_ptr<UiNode>> children;
|
||||||
|
|
||||||
@@ -322,6 +356,13 @@ namespace ZL {
|
|||||||
tb->animScaleY = 1.0f;
|
tb->animScaleY = 1.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (auto& n : pulsingNodes) {
|
||||||
|
if (n) {
|
||||||
|
n->scaleX = 1.0f;
|
||||||
|
n->scaleY = 1.0f;
|
||||||
|
n->pulseElapsedMs = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<UiButton> findButton(const std::string& name);
|
std::shared_ptr<UiButton> findButton(const std::string& name);
|
||||||
@@ -404,6 +445,7 @@ namespace ZL {
|
|||||||
std::vector<std::shared_ptr<UiTextView>> textViews;
|
std::vector<std::shared_ptr<UiTextView>> textViews;
|
||||||
std::vector<std::shared_ptr<UiTextField>> textFields;
|
std::vector<std::shared_ptr<UiTextField>> textFields;
|
||||||
std::vector<std::shared_ptr<UiStaticImage>> staticImages;
|
std::vector<std::shared_ptr<UiStaticImage>> staticImages;
|
||||||
|
std::vector<std::shared_ptr<UiNode>> pulsingNodes;
|
||||||
|
|
||||||
std::map<std::shared_ptr<UiNode>, std::vector<ActiveAnim>> nodeActiveAnims;
|
std::map<std::shared_ptr<UiNode>, std::vector<ActiveAnim>> nodeActiveAnims;
|
||||||
std::map<std::pair<std::string, std::string>, std::function<void()>> animCallbacks; // key: (nodeName, animName)
|
std::map<std::pair<std::string, std::string>, std::function<void()>> animCallbacks; // key: (nodeName, animName)
|
||||||
@@ -422,6 +464,7 @@ namespace ZL {
|
|||||||
std::vector<std::shared_ptr<UiTextView>> textViews;
|
std::vector<std::shared_ptr<UiTextView>> textViews;
|
||||||
std::vector<std::shared_ptr<UiTextField>> textFields;
|
std::vector<std::shared_ptr<UiTextField>> textFields;
|
||||||
std::vector<std::shared_ptr<UiStaticImage>> staticImages;
|
std::vector<std::shared_ptr<UiStaticImage>> staticImages;
|
||||||
|
std::vector<std::shared_ptr<UiNode>> pulsingNodes;
|
||||||
std::map<int64_t, std::shared_ptr<UiButton>> pressedButtons;
|
std::map<int64_t, std::shared_ptr<UiButton>> pressedButtons;
|
||||||
std::map<int64_t, std::shared_ptr<UiTextButton>> pressedTextButtons;
|
std::map<int64_t, std::shared_ptr<UiTextButton>> pressedTextButtons;
|
||||||
std::map<int64_t, std::shared_ptr<UiSlider>> pressedSliders;
|
std::map<int64_t, std::shared_ptr<UiSlider>> pressedSliders;
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ bool DialogueSystem::handlePointerReleased(float x, float y) {
|
|||||||
|
|
||||||
if (advanceDialogue) {
|
if (advanceDialogue) {
|
||||||
runtime.confirmAdvance();
|
runtime.confirmAdvance();
|
||||||
|
if (onDialogueAdvancedCallback) onDialogueAdvancedCallback();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,4 +144,8 @@ void DialogueSystem::stopDialogue() {
|
|||||||
runtime.stop();
|
runtime.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DialogueSystem::setOnDialogueAdvanced(std::function<void()> cb) {
|
||||||
|
onDialogueAdvancedCallback = std::move(cb);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ZL::Dialogue
|
} // namespace ZL::Dialogue
|
||||||
@@ -27,6 +27,7 @@ public:
|
|||||||
void setOnDialogueLineStarted(std::function<void(const std::string&)> cb);
|
void setOnDialogueLineStarted(std::function<void(const std::string&)> cb);
|
||||||
void setOnCutsceneLineStarted(std::function<void(const std::string&)> cb);
|
void setOnCutsceneLineStarted(std::function<void(const std::string&)> cb);
|
||||||
void setOnCutsceneFadeInComplete(std::function<void(const std::string&)> cb);
|
void setOnCutsceneFadeInComplete(std::function<void(const std::string&)> cb);
|
||||||
|
void setOnDialogueAdvanced(std::function<void()> cb);
|
||||||
void stopDialogue();
|
void stopDialogue();
|
||||||
|
|
||||||
bool isActive() const { return runtime.isActive(); }
|
bool isActive() const { return runtime.isActive(); }
|
||||||
@@ -39,6 +40,7 @@ private:
|
|||||||
DialogueDatabase database;
|
DialogueDatabase database;
|
||||||
DialogueRuntime runtime;
|
DialogueRuntime runtime;
|
||||||
DialogueOverlay overlay;
|
DialogueOverlay overlay;
|
||||||
|
std::function<void()> onDialogueAdvancedCallback;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZL::Dialogue
|
} // namespace ZL::Dialogue
|
||||||
@@ -7,6 +7,7 @@ namespace ZL {
|
|||||||
void Inventory::addItem(const Item& item) {
|
void Inventory::addItem(const Item& item) {
|
||||||
items.push_back(item);
|
items.push_back(item);
|
||||||
std::cout << "Item added to inventory: " << item.name << std::endl;
|
std::cout << "Item added to inventory: " << item.name << std::endl;
|
||||||
|
if (onItemAdded) onItemAdded(item.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Inventory::removeItem(const std::string& itemId) {
|
void Inventory::removeItem(const std::string& itemId) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
namespace ZL {
|
namespace ZL {
|
||||||
|
|
||||||
@@ -28,6 +29,9 @@ namespace ZL {
|
|||||||
bool hasItem(const std::string& itemId) const;
|
bool hasItem(const std::string& itemId) const;
|
||||||
void clear() { items.clear(); }
|
void clear() { items.clear(); }
|
||||||
size_t getCount() const { return items.size(); }
|
size_t getCount() const { return items.size(); }
|
||||||
|
|
||||||
|
// Called whenever an item is added; receives the item id.
|
||||||
|
std::function<void(const std::string&)> onItemAdded;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZL
|
} // namespace ZL
|
||||||
Reference in new issue
Block a user