Working on messenger
This commit is contained in:
parent
9221ab68ac
commit
1fc8120ee0
@ -47,7 +47,7 @@
|
|||||||
"portrait": "resources/dialogue/portrait_phone.png",
|
"portrait": "resources/dialogue/portrait_phone.png",
|
||||||
"text": "Бекзат, сынок, мы c мамой тебе отправили немного денег, постарайся прожить на эти деньги до конца недели!",
|
"text": "Бекзат, сынок, мы c мамой тебе отправили немного денег, постарайся прожить на эти деньги до конца недели!",
|
||||||
"next": "line_2",
|
"next": "line_2",
|
||||||
"bubbleSlot": "message01in"
|
"chatBubble": "in"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "line_2",
|
"id": "line_2",
|
||||||
@ -56,7 +56,7 @@
|
|||||||
"portrait": "resources/dialogue/portrait_phone.png",
|
"portrait": "resources/dialogue/portrait_phone.png",
|
||||||
"text": "Спасибо!",
|
"text": "Спасибо!",
|
||||||
"next": "end_1",
|
"next": "end_1",
|
||||||
"bubbleSlot": "message02out"
|
"chatBubble": "out"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "end_1",
|
"id": "end_1",
|
||||||
@ -75,7 +75,7 @@
|
|||||||
"portrait": "resources/dialogue/portrait_phone.png",
|
"portrait": "resources/dialogue/portrait_phone.png",
|
||||||
"text": "Жители Бишкека все чаще жалуются на депрессию и апатию. Смотрите свежее видео об этом на нашем канале!",
|
"text": "Жители Бишкека все чаще жалуются на депрессию и апатию. Смотрите свежее видео об этом на нашем канале!",
|
||||||
"next": "end_1",
|
"next": "end_1",
|
||||||
"bubbleSlot": "message01in"
|
"chatBubble": "in"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "end_1",
|
"id": "end_1",
|
||||||
@ -94,7 +94,7 @@
|
|||||||
"portrait": "resources/dialogue/portrait_phone.png",
|
"portrait": "resources/dialogue/portrait_phone.png",
|
||||||
"text": "Бекзат, помнишь мы скидывались на торт для Аиды Джаныбековой? Я тогда еще приносила скатерть, тарелки и нож для торта. И я до сих пор не получила назад ничего.",
|
"text": "Бекзат, помнишь мы скидывались на торт для Аиды Джаныбековой? Я тогда еще приносила скатерть, тарелки и нож для торта. И я до сих пор не получила назад ничего.",
|
||||||
"next": "line_2",
|
"next": "line_2",
|
||||||
"bubbleSlot": "message01in"
|
"chatBubble": "in"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "line_2",
|
"id": "line_2",
|
||||||
@ -103,7 +103,7 @@
|
|||||||
"portrait": "resources/dialogue/portrait_phone.png",
|
"portrait": "resources/dialogue/portrait_phone.png",
|
||||||
"text": "Скатерть и тарелки вроде бы лежат в студзоне.",
|
"text": "Скатерть и тарелки вроде бы лежат в студзоне.",
|
||||||
"next": "line_3",
|
"next": "line_3",
|
||||||
"bubbleSlot": "message02out"
|
"chatBubble": "out"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "line_3",
|
"id": "line_3",
|
||||||
@ -112,7 +112,7 @@
|
|||||||
"portrait": "resources/dialogue/portrait_phone.png",
|
"portrait": "resources/dialogue/portrait_phone.png",
|
||||||
"text": "А нож?",
|
"text": "А нож?",
|
||||||
"next": "line_4",
|
"next": "line_4",
|
||||||
"bubbleSlot": "message03in"
|
"chatBubble": "in"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "line_4",
|
"id": "line_4",
|
||||||
@ -121,7 +121,7 @@
|
|||||||
"portrait": "resources/dialogue/portrait_phone.png",
|
"portrait": "resources/dialogue/portrait_phone.png",
|
||||||
"text": "Нож, наверное, так и остался в учительской.",
|
"text": "Нож, наверное, так и остался в учительской.",
|
||||||
"next": "line_5",
|
"next": "line_5",
|
||||||
"bubbleSlot": "message04out"
|
"chatBubble": "out"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "line_5",
|
"id": "line_5",
|
||||||
@ -130,7 +130,7 @@
|
|||||||
"portrait": "resources/dialogue/portrait_phone.png",
|
"portrait": "resources/dialogue/portrait_phone.png",
|
||||||
"text": "А давай не \"наверное\"?",
|
"text": "А давай не \"наверное\"?",
|
||||||
"next": "line_6",
|
"next": "line_6",
|
||||||
"bubbleSlot": "message05in"
|
"chatBubble": "in"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "line_6",
|
"id": "line_6",
|
||||||
@ -139,7 +139,7 @@
|
|||||||
"portrait": "resources/dialogue/portrait_phone.png",
|
"portrait": "resources/dialogue/portrait_phone.png",
|
||||||
"text": "А давай ты приедешь в универ, зайдешь в учительскую, заберешь нож и отдашь мне?",
|
"text": "А давай ты приедешь в универ, зайдешь в учительскую, заберешь нож и отдашь мне?",
|
||||||
"next": "line_7",
|
"next": "line_7",
|
||||||
"bubbleSlot": "message06in"
|
"chatBubble": "in"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "line_7",
|
"id": "line_7",
|
||||||
@ -148,7 +148,7 @@
|
|||||||
"portrait": "resources/dialogue/portrait_phone.png",
|
"portrait": "resources/dialogue/portrait_phone.png",
|
||||||
"text": "У вас сегодня как раз Аида ведет лекцию. После лекции попросишь у нее ключи от учительской и заберешь нож.",
|
"text": "У вас сегодня как раз Аида ведет лекцию. После лекции попросишь у нее ключи от учительской и заберешь нож.",
|
||||||
"next": "line_8",
|
"next": "line_8",
|
||||||
"bubbleSlot": "message07in"
|
"chatBubble": "in"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "line_8",
|
"id": "line_8",
|
||||||
@ -157,7 +157,7 @@
|
|||||||
"portrait": "resources/dialogue/portrait_phone.png",
|
"portrait": "resources/dialogue/portrait_phone.png",
|
||||||
"text": "Почему ты сама не можешь забрать?",
|
"text": "Почему ты сама не можешь забрать?",
|
||||||
"next": "line_9",
|
"next": "line_9",
|
||||||
"bubbleSlot": "message08out"
|
"chatBubble": "out"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "line_9",
|
"id": "line_9",
|
||||||
@ -166,7 +166,7 @@
|
|||||||
"portrait": "resources/dialogue/portrait_phone.png",
|
"portrait": "resources/dialogue/portrait_phone.png",
|
||||||
"text": "Ты же знаешь, если я встречу Аиду, она 100% даст мне какое-нибудь сложное задание.",
|
"text": "Ты же знаешь, если я встречу Аиду, она 100% даст мне какое-нибудь сложное задание.",
|
||||||
"next": "line_10",
|
"next": "line_10",
|
||||||
"bubbleSlot": "message09in"
|
"chatBubble": "in"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "line_10",
|
"id": "line_10",
|
||||||
@ -175,7 +175,7 @@
|
|||||||
"portrait": "resources/dialogue/portrait_phone.png",
|
"portrait": "resources/dialogue/portrait_phone.png",
|
||||||
"text": "И потом, это ты у меня брал нож, с чего я должна ходить искать его по всему универу?",
|
"text": "И потом, это ты у меня брал нож, с чего я должна ходить искать его по всему универу?",
|
||||||
"next": "line_11",
|
"next": "line_11",
|
||||||
"bubbleSlot": "message10in"
|
"chatBubble": "in"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "line_11",
|
"id": "line_11",
|
||||||
@ -184,7 +184,7 @@
|
|||||||
"portrait": "resources/dialogue/portrait_phone.png",
|
"portrait": "resources/dialogue/portrait_phone.png",
|
||||||
"text": "Так что жду тебя в универе! Не вздумай прогулять!",
|
"text": "Так что жду тебя в универе! Не вздумай прогулять!",
|
||||||
"next": "end_1",
|
"next": "end_1",
|
||||||
"bubbleSlot": "message11in",
|
"chatBubble": "in",
|
||||||
"questUnlock" : "aiperi_knife"
|
"questUnlock" : "aiperi_knife"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
BIN
resources/w/ui/img/phone/bubble_in_center.png
(Stored with Git LFS)
Normal file
BIN
resources/w/ui/img/phone/bubble_in_center.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/w/ui/img/phone/bubble_in_corner_left_bottom.png
(Stored with Git LFS)
Normal file
BIN
resources/w/ui/img/phone/bubble_in_corner_left_bottom.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/w/ui/img/phone/bubble_in_corner_left_top.png
(Stored with Git LFS)
Normal file
BIN
resources/w/ui/img/phone/bubble_in_corner_left_top.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/w/ui/img/phone/bubble_in_corner_right_bottom.png
(Stored with Git LFS)
Normal file
BIN
resources/w/ui/img/phone/bubble_in_corner_right_bottom.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/w/ui/img/phone/bubble_in_corner_right_top.png
(Stored with Git LFS)
Normal file
BIN
resources/w/ui/img/phone/bubble_in_corner_right_top.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/w/ui/img/phone/bubble_out_center.png
(Stored with Git LFS)
Normal file
BIN
resources/w/ui/img/phone/bubble_out_center.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/w/ui/img/phone/bubble_out_corner_left_bottom.png
(Stored with Git LFS)
Normal file
BIN
resources/w/ui/img/phone/bubble_out_corner_left_bottom.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/w/ui/img/phone/bubble_out_corner_left_top.png
(Stored with Git LFS)
Normal file
BIN
resources/w/ui/img/phone/bubble_out_corner_left_top.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/w/ui/img/phone/bubble_out_corner_right_bottom.png
(Stored with Git LFS)
Normal file
BIN
resources/w/ui/img/phone/bubble_out_corner_right_bottom.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/w/ui/img/phone/bubble_out_corner_right_top.png
(Stored with Git LFS)
Normal file
BIN
resources/w/ui/img/phone/bubble_out_corner_right_top.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -36,136 +36,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "StaticImage",
|
"type": "FrameLayout",
|
||||||
"name": "message01in",
|
"name": "chatMessagesContainer",
|
||||||
"width": 320.6,
|
"width": "match_parent",
|
||||||
"height": 148.4,
|
"height": "match_parent"
|
||||||
"x" : 430,
|
|
||||||
"y" : 1097,
|
|
||||||
"horizontal_gravity": "left",
|
|
||||||
"vertical_gravity": "bottom",
|
|
||||||
"visible": false,
|
|
||||||
"texture": "resources/w/ui/img/phone/chat02_01in.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "StaticImage",
|
|
||||||
"name": "message02out",
|
|
||||||
"width": 320.6,
|
|
||||||
"height": 64.4,
|
|
||||||
"x" : 430,
|
|
||||||
"y" : 1022.6,
|
|
||||||
"horizontal_gravity": "right",
|
|
||||||
"vertical_gravity": "bottom",
|
|
||||||
"visible": false,
|
|
||||||
"texture": "resources/w/ui/img/phone/chat02_02out.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "StaticImage",
|
|
||||||
"name": "message03in",
|
|
||||||
"width": 103.6,
|
|
||||||
"height": 43.4,
|
|
||||||
"x" : 430,
|
|
||||||
"y" : 969.2,
|
|
||||||
"horizontal_gravity": "left",
|
|
||||||
"vertical_gravity": "bottom",
|
|
||||||
"visible": false,
|
|
||||||
"texture": "resources/w/ui/img/phone/chat02_03in.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "StaticImage",
|
|
||||||
"name": "message04out",
|
|
||||||
"width": 320.6,
|
|
||||||
"height": 64.4,
|
|
||||||
"x" : 430,
|
|
||||||
"y" : 894.8,
|
|
||||||
"horizontal_gravity": "right",
|
|
||||||
"vertical_gravity": "bottom",
|
|
||||||
"visible": false,
|
|
||||||
"texture": "resources/w/ui/img/phone/chat02_04out.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "StaticImage",
|
|
||||||
"name": "message05in",
|
|
||||||
"width": 243.6,
|
|
||||||
"height": 43.4,
|
|
||||||
"x" : 430,
|
|
||||||
"y" : 841.4,
|
|
||||||
"horizontal_gravity": "left",
|
|
||||||
"vertical_gravity": "bottom",
|
|
||||||
"visible": false,
|
|
||||||
"texture": "resources/w/ui/img/phone/chat02_05in.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "StaticImage",
|
|
||||||
"name": "message06in",
|
|
||||||
"width": 320.6,
|
|
||||||
"height": 85.4,
|
|
||||||
"x" : 430,
|
|
||||||
"y" : 746,
|
|
||||||
"horizontal_gravity": "left",
|
|
||||||
"vertical_gravity": "bottom",
|
|
||||||
"visible": false,
|
|
||||||
"texture": "resources/w/ui/img/phone/chat02_06in.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "StaticImage",
|
|
||||||
"name": "message07in",
|
|
||||||
"width": 320.6,
|
|
||||||
"height": 106.4,
|
|
||||||
"x" : 430,
|
|
||||||
"y" : 629.6,
|
|
||||||
"horizontal_gravity": "left",
|
|
||||||
"vertical_gravity": "bottom",
|
|
||||||
"visible": false,
|
|
||||||
"texture": "resources/w/ui/img/phone/chat02_07in.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "StaticImage",
|
|
||||||
"name": "message08out",
|
|
||||||
"width": 320.6,
|
|
||||||
"height": 64.4,
|
|
||||||
"x" : 430,
|
|
||||||
"y" : 555.2,
|
|
||||||
"horizontal_gravity": "right",
|
|
||||||
"vertical_gravity": "bottom",
|
|
||||||
"visible": false,
|
|
||||||
"texture": "resources/w/ui/img/phone/chat02_08out.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "StaticImage",
|
|
||||||
"name": "message09in",
|
|
||||||
"width": 320.6,
|
|
||||||
"height": 85.4,
|
|
||||||
"x" : 430,
|
|
||||||
"y" : 459.8,
|
|
||||||
"horizontal_gravity": "left",
|
|
||||||
"vertical_gravity": "bottom",
|
|
||||||
"visible": false,
|
|
||||||
"texture": "resources/w/ui/img/phone/chat02_09in.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "StaticImage",
|
|
||||||
"name": "message10in",
|
|
||||||
"width": 320.6,
|
|
||||||
"height": 85.4,
|
|
||||||
"x" : 430,
|
|
||||||
"y" : 364.4,
|
|
||||||
"horizontal_gravity": "left",
|
|
||||||
"vertical_gravity": "bottom",
|
|
||||||
"visible": false,
|
|
||||||
"texture": "resources/w/ui/img/phone/chat02_10in.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "StaticImage",
|
|
||||||
"name": "message11in",
|
|
||||||
"width": 320.6,
|
|
||||||
"height": 64.4,
|
|
||||||
"x" : 430,
|
|
||||||
"y" : 290,
|
|
||||||
"horizontal_gravity": "left",
|
|
||||||
"vertical_gravity": "bottom",
|
|
||||||
"visible": false,
|
|
||||||
"texture": "resources/w/ui/img/phone/chat02_11in.png"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "StaticImage",
|
"type": "StaticImage",
|
||||||
@ -193,12 +67,7 @@
|
|||||||
"textCentered": false,
|
"textCentered": false,
|
||||||
"topAligned": true,
|
"topAligned": true,
|
||||||
"wrap": true,
|
"wrap": true,
|
||||||
"color": [
|
"color": [1.0, 1.0, 1.0, 1.0],
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
1.0
|
|
||||||
],
|
|
||||||
"textures": {
|
"textures": {
|
||||||
"normal": "resources/w/ui/img/phone/CharHeader001.png",
|
"normal": "resources/w/ui/img/phone/CharHeader001.png",
|
||||||
"hover": "resources/w/ui/img/phone/CharHeader001.png",
|
"hover": "resources/w/ui/img/phone/CharHeader001.png",
|
||||||
|
|||||||
@ -35,6 +35,12 @@
|
|||||||
"pressed": "resources/w/ui/img/phone/PhoneChat001.png"
|
"pressed": "resources/w/ui/img/phone/PhoneChat001.png"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"name": "chatMessagesContainer",
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": "match_parent"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "StaticImage",
|
"type": "StaticImage",
|
||||||
"name": "chatTopCover",
|
"name": "chatTopCover",
|
||||||
@ -61,41 +67,12 @@
|
|||||||
"textCentered": false,
|
"textCentered": false,
|
||||||
"topAligned": true,
|
"topAligned": true,
|
||||||
"wrap": true,
|
"wrap": true,
|
||||||
"color": [
|
"color": [1.0, 1.0, 1.0, 1.0],
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
1.0
|
|
||||||
],
|
|
||||||
"textures": {
|
"textures": {
|
||||||
"normal": "resources/w/ui/img/phone/CharHeader002.png",
|
"normal": "resources/w/ui/img/phone/CharHeader002.png",
|
||||||
"hover": "resources/w/ui/img/phone/CharHeader002.png",
|
"hover": "resources/w/ui/img/phone/CharHeader002.png",
|
||||||
"pressed": "resources/w/ui/img/phone/CharHeader002.png"
|
"pressed": "resources/w/ui/img/phone/CharHeader002.png"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "StaticImage",
|
|
||||||
"name": "message01in",
|
|
||||||
"width": 320.6,
|
|
||||||
"height": 103.6,
|
|
||||||
"x" : 430,
|
|
||||||
"y" : 506.4,
|
|
||||||
"horizontal_gravity": "left",
|
|
||||||
"vertical_gravity": "bottom",
|
|
||||||
"visible": false,
|
|
||||||
"texture": "resources/w/ui/img/phone/chat01_01in.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "StaticImage",
|
|
||||||
"name": "message02out",
|
|
||||||
"width": 116.2,
|
|
||||||
"height": 43.4,
|
|
||||||
"x" : 430,
|
|
||||||
"y" : 453,
|
|
||||||
"horizontal_gravity": "right",
|
|
||||||
"vertical_gravity": "bottom",
|
|
||||||
"visible": false,
|
|
||||||
"texture": "resources/w/ui/img/phone/chat01_02out.png"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,6 +35,22 @@
|
|||||||
"pressed": "resources/w/ui/img/phone/PhoneChat001.png"
|
"pressed": "resources/w/ui/img/phone/PhoneChat001.png"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"name": "chatMessagesContainer",
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": "match_parent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "StaticImage",
|
||||||
|
"name": "chatTopCover",
|
||||||
|
"width": 446.25,
|
||||||
|
"height": 70.82,
|
||||||
|
"x": 0,
|
||||||
|
"y": -50.82,
|
||||||
|
"horizontal_gravity": "center",
|
||||||
|
"texture": "resources/w/ui/img/phone/chat_top_cover001.png"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "TextButton",
|
"type": "TextButton",
|
||||||
"name": "chatTitleButton",
|
"name": "chatTitleButton",
|
||||||
@ -51,39 +67,12 @@
|
|||||||
"textCentered": false,
|
"textCentered": false,
|
||||||
"topAligned": true,
|
"topAligned": true,
|
||||||
"wrap": true,
|
"wrap": true,
|
||||||
"color": [
|
"color": [1.0, 1.0, 1.0, 1.0],
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
1.0
|
|
||||||
],
|
|
||||||
"textures": {
|
"textures": {
|
||||||
"normal": "resources/w/ui/img/phone/CharHeader003.png",
|
"normal": "resources/w/ui/img/phone/CharHeader003.png",
|
||||||
"hover": "resources/w/ui/img/phone/CharHeader003.png",
|
"hover": "resources/w/ui/img/phone/CharHeader003.png",
|
||||||
"pressed": "resources/w/ui/img/phone/CharHeader003.png"
|
"pressed": "resources/w/ui/img/phone/CharHeader003.png"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "StaticImage",
|
|
||||||
"name": "chatTopCover",
|
|
||||||
"width": 446.25,
|
|
||||||
"height": 70.82,
|
|
||||||
"x" : 0,
|
|
||||||
"y" : -50.82,
|
|
||||||
"horizontal_gravity": "center",
|
|
||||||
"texture": "resources/w/ui/img/phone/chat_top_cover001.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "StaticImage",
|
|
||||||
"name": "message01in",
|
|
||||||
"width": 320.6,
|
|
||||||
"height": 103.6,
|
|
||||||
"x" : 430,
|
|
||||||
"y" : 506.4,
|
|
||||||
"horizontal_gravity": "left",
|
|
||||||
"vertical_gravity": "bottom",
|
|
||||||
"visible": false,
|
|
||||||
"texture": "resources/w/ui/img/phone/chat03_01in.png"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -423,10 +423,10 @@ namespace ZL
|
|||||||
startNightTransition();
|
startNightTransition();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Wire bubble-slot callback so chat bubbles appear as dialogue lines are shown.
|
// Wire chat-bubble callback so dynamic bubbles appear as dialogue lines are shown.
|
||||||
for (auto& [name, loc] : locations) {
|
for (auto& [name, loc] : locations) {
|
||||||
loc->dialogueSystem.setOnBubbleSlotReady([this](const std::string& bubbleSlot) {
|
loc->dialogueSystem.setOnChatBubbleReady([this](const std::string& text, bool incoming) {
|
||||||
menuManager.revealPhoneChatBubble(bubbleSlot);
|
menuManager.onChatBubbleReady(text, incoming);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -122,6 +122,18 @@ namespace ZL {
|
|||||||
|
|
||||||
questJournal.loadFromFile("resources/quests/quests.json", zipFile);
|
questJournal.loadFromFile("resources/quests/quests.json", zipFile);
|
||||||
|
|
||||||
|
const std::string imgDir = "resources/w/ui/img/phone/";
|
||||||
|
texBubbleInCenter_ = renderer.textureManager.LoadFromPng(imgDir + "bubble_in_center.png", zipFile, true);
|
||||||
|
texBubbleInLT_ = renderer.textureManager.LoadFromPng(imgDir + "bubble_in_corner_left_top.png", zipFile, true);
|
||||||
|
texBubbleInLB_ = renderer.textureManager.LoadFromPng(imgDir + "bubble_in_corner_left_bottom.png", zipFile, true);
|
||||||
|
texBubbleInRT_ = renderer.textureManager.LoadFromPng(imgDir + "bubble_in_corner_right_top.png", zipFile, true);
|
||||||
|
texBubbleInRB_ = renderer.textureManager.LoadFromPng(imgDir + "bubble_in_corner_right_bottom.png",zipFile, true);
|
||||||
|
texBubbleOutCenter_ = renderer.textureManager.LoadFromPng(imgDir + "bubble_out_center.png", zipFile, true);
|
||||||
|
texBubbleOutLT_ = renderer.textureManager.LoadFromPng(imgDir + "bubble_out_corner_left_top.png", zipFile, true);
|
||||||
|
texBubbleOutLB_ = renderer.textureManager.LoadFromPng(imgDir + "bubble_out_corner_left_bottom.png",zipFile, true);
|
||||||
|
texBubbleOutRT_ = renderer.textureManager.LoadFromPng(imgDir + "bubble_out_corner_right_top.png", zipFile, true);
|
||||||
|
texBubbleOutRB_ = renderer.textureManager.LoadFromPng(imgDir + "bubble_out_corner_right_bottom.png",zipFile, true);
|
||||||
|
|
||||||
enterGameplay();
|
enterGameplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,15 +347,15 @@ namespace ZL {
|
|||||||
uiManager.setButtonCallback("phoneMain", [this](const std::string&) {});
|
uiManager.setButtonCallback("phoneMain", [this](const std::string&) {});
|
||||||
uiManager.setTextButtonCallback("chat1button", [this](const std::string&) {
|
uiManager.setTextButtonCallback("chat1button", [this](const std::string&) {
|
||||||
chatUnread_[0] = false;
|
chatUnread_[0] = false;
|
||||||
openPhoneChatFromList(phoneChat1Root, "dialog_chat_aiperi001");
|
openPhoneChatFromList(0, phoneChat1Root, "dialog_chat_aiperi001");
|
||||||
});
|
});
|
||||||
uiManager.setTextButtonCallback("chat2button", [this](const std::string&) {
|
uiManager.setTextButtonCallback("chat2button", [this](const std::string&) {
|
||||||
chatUnread_[1] = false;
|
chatUnread_[1] = false;
|
||||||
openPhoneChatFromList(phoneChat2Root, "dialog_chat_parents001");
|
openPhoneChatFromList(1, phoneChat2Root, "dialog_chat_parents001");
|
||||||
});
|
});
|
||||||
uiManager.setTextButtonCallback("chat3button", [this](const std::string&) {
|
uiManager.setTextButtonCallback("chat3button", [this](const std::string&) {
|
||||||
chatUnread_[2] = false;
|
chatUnread_[2] = false;
|
||||||
openPhoneChatFromList(phoneChat3Root, "dialog_chat_news001");
|
openPhoneChatFromList(2, phoneChat3Root, "dialog_chat_news001");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -424,14 +436,14 @@ namespace ZL {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::openPhoneChatFromList(std::shared_ptr<UiNode> chatRoot, const std::string& dialogueId) {
|
void MenuManager::openPhoneChatFromList(int chatIndex, std::shared_ptr<UiNode> chatRoot, const std::string& dialogueId) {
|
||||||
|
activeChatIndex_ = chatIndex;
|
||||||
phoneChatVisibleBubbles_.clear();
|
phoneChatVisibleBubbles_.clear();
|
||||||
uiManager.pushMenuFromSavedRoot(chatRoot);
|
uiManager.pushMenuFromSavedRoot(chatRoot);
|
||||||
|
|
||||||
const bool firstOpen = dialogueId.empty() || startedDialogues_.find(dialogueId) == startedDialogues_.end();
|
const bool firstOpen = dialogueId.empty() || startedDialogues_.find(dialogueId) == startedDialogues_.end();
|
||||||
if (firstOpen) {
|
|
||||||
resetPhoneChatNodes();
|
rebuildChatBubblesFromHistory(chatIndex);
|
||||||
}
|
|
||||||
|
|
||||||
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
|
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
|
||||||
closePhoneScreenFromChat();
|
closePhoneScreenFromChat();
|
||||||
@ -448,12 +460,14 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::returnToPhoneChatList() {
|
void MenuManager::returnToPhoneChatList() {
|
||||||
|
activeChatIndex_ = -1;
|
||||||
phoneChatVisibleBubbles_.clear();
|
phoneChatVisibleBubbles_.clear();
|
||||||
uiManager.popMenu();
|
uiManager.popMenu();
|
||||||
refreshChatUnreadIndicators();
|
refreshChatUnreadIndicators();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::closePhoneEntirely() {
|
void MenuManager::closePhoneEntirely() {
|
||||||
|
activeChatIndex_ = -1;
|
||||||
state = GameState::Gameplay;
|
state = GameState::Gameplay;
|
||||||
phoneChatVisibleBubbles_.clear();
|
phoneChatVisibleBubbles_.clear();
|
||||||
const int depth = uiManager.menuStackSize();
|
const int depth = uiManager.menuStackSize();
|
||||||
@ -754,19 +768,59 @@ namespace ZL {
|
|||||||
uiManager.updateAllLayouts();
|
uiManager.updateAllLayouts();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::revealPhoneChatBubble(const std::string& slotName) {
|
void MenuManager::rebuildChatBubblesFromHistory(int chatIndex) {
|
||||||
if (state != GameState::PhoneScreen) return;
|
uiManager.clearChatBubbles("chatMessagesContainer");
|
||||||
auto node = uiManager.findNode(slotName);
|
phoneChatVisibleBubbles_.clear();
|
||||||
if (!node) return;
|
|
||||||
|
|
||||||
// Zero scale before making visible to avoid a one-frame flash at full size
|
if (chatIndex < 0 || chatIndex > 2) return;
|
||||||
|
for (const auto& msg : chatHistory_[chatIndex]) {
|
||||||
|
const bool inc = msg.incoming;
|
||||||
|
const std::string nodeName = uiManager.addChatBubble(
|
||||||
|
"chatMessagesContainer", msg.text, inc,
|
||||||
|
inc ? texBubbleInCenter_ : texBubbleOutCenter_,
|
||||||
|
inc ? texBubbleInLT_ : texBubbleOutLT_,
|
||||||
|
inc ? texBubbleInLB_ : texBubbleOutLB_,
|
||||||
|
inc ? texBubbleInRT_ : texBubbleOutRT_,
|
||||||
|
inc ? texBubbleInRB_ : texBubbleOutRB_,
|
||||||
|
renderer, "resources/fonts/DroidSans.ttf", 20, zipFile_);
|
||||||
|
if (!nodeName.empty()) {
|
||||||
|
auto n = uiManager.findNode(nodeName);
|
||||||
|
phoneChatVisibleBubbles_.push_back({ nodeName, n ? n->height : 60.0f });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
recomputePhoneChatPositions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MenuManager::onChatBubbleReady(const std::string& text, bool incoming) {
|
||||||
|
if (activeChatIndex_ < 0) return;
|
||||||
|
|
||||||
|
auto& history = chatHistory_[activeChatIndex_];
|
||||||
|
if (static_cast<int>(history.size()) >= 5) {
|
||||||
|
history.erase(history.begin());
|
||||||
|
}
|
||||||
|
history.push_back({ text, incoming });
|
||||||
|
|
||||||
|
if (state != GameState::PhoneScreen) return;
|
||||||
|
|
||||||
|
const std::string nodeName = uiManager.addChatBubble(
|
||||||
|
"chatMessagesContainer", text, incoming,
|
||||||
|
incoming ? texBubbleInCenter_ : texBubbleOutCenter_,
|
||||||
|
incoming ? texBubbleInLT_ : texBubbleOutLT_,
|
||||||
|
incoming ? texBubbleInLB_ : texBubbleOutLB_,
|
||||||
|
incoming ? texBubbleInRT_ : texBubbleOutRT_,
|
||||||
|
incoming ? texBubbleInRB_ : texBubbleOutRB_,
|
||||||
|
renderer, "resources/fonts/DroidSans.ttf", 20, zipFile_);
|
||||||
|
|
||||||
|
if (nodeName.empty()) return;
|
||||||
|
|
||||||
|
auto node = uiManager.findNode(nodeName);
|
||||||
|
if (node) {
|
||||||
node->scaleX = 0.0f;
|
node->scaleX = 0.0f;
|
||||||
node->scaleY = 0.0f;
|
node->scaleY = 0.0f;
|
||||||
|
phoneChatVisibleBubbles_.push_back({ nodeName, node->height });
|
||||||
phoneChatVisibleBubbles_.push_back({slotName, node->height});
|
}
|
||||||
uiManager.setNodeVisible(slotName, true);
|
|
||||||
recomputePhoneChatPositions();
|
recomputePhoneChatPositions();
|
||||||
uiManager.startPopIn(slotName, 300.0f);
|
uiManager.startPopIn(nodeName, 300.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::setDarklandsMode(bool enabled)
|
void MenuManager::setDarklandsMode(bool enabled)
|
||||||
|
|||||||
@ -58,7 +58,6 @@ namespace ZL {
|
|||||||
|
|
||||||
void openPhoneScreen();
|
void openPhoneScreen();
|
||||||
void closePhoneScreen();
|
void closePhoneScreen();
|
||||||
void revealPhoneChatBubble(const std::string& slotName);
|
|
||||||
bool isPhoneScreenOpen() const { return state == GameState::PhoneScreen; }
|
bool isPhoneScreenOpen() const { return state == GameState::PhoneScreen; }
|
||||||
void closePhoneEntirely();
|
void closePhoneEntirely();
|
||||||
|
|
||||||
@ -71,6 +70,9 @@ namespace ZL {
|
|||||||
std::function<void()> startDarklandsTransitionFunc;
|
std::function<void()> startDarklandsTransitionFunc;
|
||||||
std::function<void()> startNightTransitionFunc;
|
std::function<void()> startNightTransitionFunc;
|
||||||
|
|
||||||
|
// Called when a chat message bubble should be shown (text + direction)
|
||||||
|
void onChatBubbleReady(const std::string& text, bool incoming);
|
||||||
|
|
||||||
void setDarklandsMode(bool enabled);
|
void setDarklandsMode(bool enabled);
|
||||||
void advanceTutorialStep();
|
void advanceTutorialStep();
|
||||||
void onItemPickedUp(const std::string& itemId);
|
void onItemPickedUp(const std::string& itemId);
|
||||||
@ -102,10 +104,11 @@ namespace ZL {
|
|||||||
void refreshChatUnreadIndicators();
|
void refreshChatUnreadIndicators();
|
||||||
void resetPhoneChatNodes();
|
void resetPhoneChatNodes();
|
||||||
void recomputePhoneChatPositions();
|
void recomputePhoneChatPositions();
|
||||||
void openPhoneChatFromList(std::shared_ptr<UiNode> chatRoot, const std::string& dialogueId);
|
void openPhoneChatFromList(int chatIndex, std::shared_ptr<UiNode> chatRoot, const std::string& dialogueId);
|
||||||
void returnToPhoneChatList();
|
void returnToPhoneChatList();
|
||||||
void closePhoneScreenFromChat();
|
void closePhoneScreenFromChat();
|
||||||
void applyUniIntHud();
|
void applyUniIntHud();
|
||||||
|
void rebuildChatBubblesFromHistory(int chatIndex);
|
||||||
|
|
||||||
GameState state = GameState::Gameplay;
|
GameState state = GameState::Gameplay;
|
||||||
Inventory* inventory = nullptr;
|
Inventory* inventory = nullptr;
|
||||||
@ -176,6 +179,15 @@ namespace ZL {
|
|||||||
};
|
};
|
||||||
std::vector<PhoneChatBubbleInfo> phoneChatVisibleBubbles_;
|
std::vector<PhoneChatBubbleInfo> phoneChatVisibleBubbles_;
|
||||||
|
|
||||||
|
// Per-chat message history (max 5 messages each)
|
||||||
|
struct StoredChatMessage { std::string text; bool incoming; };
|
||||||
|
std::vector<StoredChatMessage> chatHistory_[3];
|
||||||
|
int activeChatIndex_ = -1;
|
||||||
|
|
||||||
|
// Preloaded bubble textures
|
||||||
|
std::shared_ptr<Texture> texBubbleInCenter_, texBubbleInLT_, texBubbleInLB_, texBubbleInRT_, texBubbleInRB_;
|
||||||
|
std::shared_ptr<Texture> texBubbleOutCenter_, texBubbleOutLT_, texBubbleOutLB_, texBubbleOutRT_, texBubbleOutRB_;
|
||||||
|
|
||||||
static constexpr float CHAT_TOP_Y = 610.0f;
|
static constexpr float CHAT_TOP_Y = 610.0f;
|
||||||
static constexpr float CHAT_BOTTOM_Y = 290.0f;
|
static constexpr float CHAT_BOTTOM_Y = 290.0f;
|
||||||
static constexpr float CHAT_SPACING = 10.0f;
|
static constexpr float CHAT_SPACING = 10.0f;
|
||||||
|
|||||||
@ -824,6 +824,107 @@ namespace ZL {
|
|||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- UiChatBubble helpers ----
|
||||||
|
|
||||||
|
static void buildQuadMesh(VertexRenderStruct& mesh, float x0, float y0, float x1, float y1) {
|
||||||
|
mesh.data.PositionData.clear();
|
||||||
|
mesh.data.TexCoordData.clear();
|
||||||
|
mesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||||
|
mesh.data.TexCoordData.push_back({ 0, 0 });
|
||||||
|
mesh.data.PositionData.push_back({ x0, y1, 0 });
|
||||||
|
mesh.data.TexCoordData.push_back({ 0, 1 });
|
||||||
|
mesh.data.PositionData.push_back({ x1, y1, 0 });
|
||||||
|
mesh.data.TexCoordData.push_back({ 1, 1 });
|
||||||
|
mesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||||
|
mesh.data.TexCoordData.push_back({ 0, 0 });
|
||||||
|
mesh.data.PositionData.push_back({ x1, y1, 0 });
|
||||||
|
mesh.data.TexCoordData.push_back({ 1, 1 });
|
||||||
|
mesh.data.PositionData.push_back({ x1, y0, 0 });
|
||||||
|
mesh.data.TexCoordData.push_back({ 1, 0 });
|
||||||
|
mesh.RefreshVBO();
|
||||||
|
}
|
||||||
|
|
||||||
|
float UiChatBubble::computeHeight() const {
|
||||||
|
if (!textRenderer) return CORNER_SIZE * 2 + PAD_Y * 2 + static_cast<float>(fontSize);
|
||||||
|
const float textW = MAX_WIDTH - CORNER_SIZE * 2.0f - PAD_X * 2.0f;
|
||||||
|
const std::string wrapped = wrapTextByPixels(text, *textRenderer, textW, 1.0f);
|
||||||
|
int lines = 1;
|
||||||
|
for (char c : wrapped) if (c == '\n') ++lines;
|
||||||
|
return CORNER_SIZE * 2.0f + PAD_Y * 2.0f + lines * static_cast<float>(fontSize) * 1.25f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UiChatBubble::buildMeshes() {
|
||||||
|
const float x = rect.x;
|
||||||
|
const float y = rect.y;
|
||||||
|
const float w = rect.w;
|
||||||
|
const float h = rect.h;
|
||||||
|
const float cs = CORNER_SIZE;
|
||||||
|
|
||||||
|
const float OFFSET = 0.5f;
|
||||||
|
|
||||||
|
// 1. Middle center (full width, between top and bottom corner rows)
|
||||||
|
buildQuadMesh(meshMiddle, x, y + cs- OFFSET, x + w, y + h - cs +OFFSET);
|
||||||
|
// 2. Top strip (between corners)
|
||||||
|
buildQuadMesh(meshTopStrip, x + cs- OFFSET, y + h - cs, x + w - cs+ OFFSET, y + h);
|
||||||
|
// 3. Bottom strip
|
||||||
|
buildQuadMesh(meshBotStrip, x + cs- OFFSET, y, x + w - cs+ OFFSET, y + cs);
|
||||||
|
// Corners
|
||||||
|
buildQuadMesh(meshCornerLT, x, y + h - cs-OFFSET, x + cs, y + h);
|
||||||
|
buildQuadMesh(meshCornerRT, x + w - cs, y + h - cs - OFFSET, x + w, y + h);
|
||||||
|
buildQuadMesh(meshCornerLB, x, y, x + cs, y + cs);
|
||||||
|
buildQuadMesh(meshCornerRB, x + w - cs, y, x + w, y + cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UiChatBubble::draw(Renderer& renderer, float nodeScaleX, float nodeScaleY) const {
|
||||||
|
renderer.RenderUniform1i(textureUniformName, 0);
|
||||||
|
|
||||||
|
|
||||||
|
// Corners with blending (semi-transparent rounded corners)
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
|
auto drawCorner = [&](const std::shared_ptr<Texture>& tex, const VertexRenderStruct& mesh) {
|
||||||
|
if (tex) {
|
||||||
|
glBindTexture(GL_TEXTURE_2D, tex->getTexID());
|
||||||
|
renderer.DrawVertexRenderStruct(mesh);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
drawCorner(texCornerLT, meshCornerLT);
|
||||||
|
drawCorner(texCornerRT, meshCornerRT);
|
||||||
|
drawCorner(texCornerLB, meshCornerLB);
|
||||||
|
drawCorner(texCornerRB, meshCornerRB);
|
||||||
|
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
|
||||||
|
// Center parts (no blending needed — opaque fill)
|
||||||
|
if (texCenter) {
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texCenter->getTexID());
|
||||||
|
renderer.DrawVertexRenderStruct(meshMiddle);
|
||||||
|
renderer.DrawVertexRenderStruct(meshTopStrip);
|
||||||
|
renderer.DrawVertexRenderStruct(meshBotStrip);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Text — position is transformed around the bubble centre to match the renderer's
|
||||||
|
// scale matrix (which scales meshes around the same centre point).
|
||||||
|
if (textRenderer && !text.empty()) {
|
||||||
|
const float textW = rect.w - CORNER_SIZE * 2.0f - PAD_X * 2.0f;
|
||||||
|
const std::string wrapped = wrapTextByPixels(text, *textRenderer, textW, 1.0f);
|
||||||
|
|
||||||
|
const float tx0 = rect.x + CORNER_SIZE + PAD_X;
|
||||||
|
const float ty0 = rect.y + rect.h - CORNER_SIZE - PAD_Y - static_cast<float>(fontSize);
|
||||||
|
|
||||||
|
// Scale around bubble centre, matching how the renderer's ScaleMatrix works
|
||||||
|
const float cx = rect.x + rect.w * 0.5f;
|
||||||
|
const float cy = rect.y + rect.h * 0.5f;
|
||||||
|
const float tx = cx + (tx0 - cx) * nodeScaleX;
|
||||||
|
const float ty = cy + (ty0 - cy) * nodeScaleY;
|
||||||
|
|
||||||
|
textRenderer->drawText(wrapped, tx, ty, nodeScaleX, false, { 1.f, 1.f, 1.f, 1.f });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- end UiChatBubble helpers ----
|
||||||
|
|
||||||
void UiManager::replaceRoot(std::shared_ptr<UiNode> newRoot) {
|
void UiManager::replaceRoot(std::shared_ptr<UiNode> newRoot) {
|
||||||
root = newRoot;
|
root = newRoot;
|
||||||
layoutNode(
|
layoutNode(
|
||||||
@ -841,6 +942,7 @@ namespace ZL {
|
|||||||
textViews.clear();
|
textViews.clear();
|
||||||
textFields.clear();
|
textFields.clear();
|
||||||
staticImages.clear();
|
staticImages.clear();
|
||||||
|
chatBubbles.clear();
|
||||||
pulsingNodes.clear();
|
pulsingNodes.clear();
|
||||||
popInNodes.clear();
|
popInNodes.clear();
|
||||||
collectButtonsAndSliders(root);
|
collectButtonsAndSliders(root);
|
||||||
@ -1052,6 +1154,12 @@ namespace ZL {
|
|||||||
node->staticImage->rect = node->screenRect;
|
node->staticImage->rect = node->screenRect;
|
||||||
node->staticImage->buildMesh();
|
node->staticImage->buildMesh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 6. Chat bubble
|
||||||
|
if (node->chatBubble) {
|
||||||
|
node->chatBubble->rect = node->screenRect;
|
||||||
|
node->chatBubble->buildMeshes();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UiManager::updateAllLayouts() {
|
void UiManager::updateAllLayouts() {
|
||||||
@ -1106,6 +1214,9 @@ namespace ZL {
|
|||||||
if (node->pulseEnabled && node->staticImage) {
|
if (node->pulseEnabled && node->staticImage) {
|
||||||
pulsingNodes.push_back(node);
|
pulsingNodes.push_back(node);
|
||||||
}
|
}
|
||||||
|
if (node->chatBubble) {
|
||||||
|
chatBubbles.push_back(node->chatBubble);
|
||||||
|
}
|
||||||
for (auto& c : node->children) collectButtonsAndSliders(c);
|
for (auto& c : node->children) collectButtonsAndSliders(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1215,6 +1326,7 @@ namespace ZL {
|
|||||||
prev.textViews = textViews;
|
prev.textViews = textViews;
|
||||||
prev.textFields = textFields;
|
prev.textFields = textFields;
|
||||||
prev.staticImages = staticImages;
|
prev.staticImages = staticImages;
|
||||||
|
prev.chatBubbles = chatBubbles;
|
||||||
prev.pulsingNodes = pulsingNodes;
|
prev.pulsingNodes = pulsingNodes;
|
||||||
prev.pressedButtons = pressedButtons;
|
prev.pressedButtons = pressedButtons;
|
||||||
prev.pressedTextButtons = pressedTextButtons;
|
prev.pressedTextButtons = pressedTextButtons;
|
||||||
@ -1280,6 +1392,7 @@ namespace ZL {
|
|||||||
textViews = s.textViews;
|
textViews = s.textViews;
|
||||||
textFields = s.textFields;
|
textFields = s.textFields;
|
||||||
staticImages = s.staticImages;
|
staticImages = s.staticImages;
|
||||||
|
chatBubbles = s.chatBubbles;
|
||||||
pulsingNodes = s.pulsingNodes;
|
pulsingNodes = s.pulsingNodes;
|
||||||
popInNodes = s.popInNodes;
|
popInNodes = s.popInNodes;
|
||||||
pressedButtons = s.pressedButtons;
|
pressedButtons = s.pressedButtons;
|
||||||
@ -1348,6 +1461,11 @@ namespace ZL {
|
|||||||
node->staticImage->draw(renderer);
|
node->staticImage->draw(renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1b. Chat bubble (composite widget)
|
||||||
|
if (node->chatBubble) {
|
||||||
|
node->chatBubble->draw(renderer, node->scaleX, node->scaleY);
|
||||||
|
}
|
||||||
|
|
||||||
// 2. Потом кнопки
|
// 2. Потом кнопки
|
||||||
if (node->button) {
|
if (node->button) {
|
||||||
node->button->draw(renderer);
|
node->button->draw(renderer);
|
||||||
@ -1994,4 +2112,86 @@ namespace ZL {
|
|||||||
if (!node) return false;
|
if (!node) return false;
|
||||||
return node->visible;
|
return node->visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string UiManager::addChatBubble(
|
||||||
|
const std::string& parentNodeName,
|
||||||
|
const std::string& text, bool incoming,
|
||||||
|
std::shared_ptr<Texture> texCenter,
|
||||||
|
std::shared_ptr<Texture> texLT, std::shared_ptr<Texture> texLB,
|
||||||
|
std::shared_ptr<Texture> texRT, std::shared_ptr<Texture> texRB,
|
||||||
|
Renderer& renderer, const std::string& fontPath, int fontSize,
|
||||||
|
const std::string& zipFile)
|
||||||
|
{
|
||||||
|
if (!root) return "";
|
||||||
|
auto parent = findNodeByName(root, parentNodeName);
|
||||||
|
if (!parent) {
|
||||||
|
std::cerr << "UiManager::addChatBubble: parent '" << parentNodeName << "' not found\n";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bubbleCounter = 0;
|
||||||
|
const std::string nodeName = "chat_bubble_" + std::to_string(bubbleCounter++);
|
||||||
|
|
||||||
|
// Build the chat bubble component
|
||||||
|
auto bubble = std::make_shared<UiChatBubble>();
|
||||||
|
bubble->name = nodeName;
|
||||||
|
bubble->text = text;
|
||||||
|
bubble->incoming = incoming;
|
||||||
|
bubble->fontSize = fontSize;
|
||||||
|
bubble->texCenter = texCenter;
|
||||||
|
bubble->texCornerLT = texLT;
|
||||||
|
bubble->texCornerLB = texLB;
|
||||||
|
bubble->texCornerRT = texRT;
|
||||||
|
bubble->texCornerRB = texRB;
|
||||||
|
|
||||||
|
bubble->textRenderer = std::make_unique<TextRenderer>();
|
||||||
|
if (!bubble->textRenderer->init(renderer, fontPath, fontSize, zipFile)) {
|
||||||
|
std::cerr << "UiManager::addChatBubble: TextRenderer init failed\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the UiNode
|
||||||
|
auto node = std::make_shared<UiNode>();
|
||||||
|
node->name = nodeName;
|
||||||
|
node->width = UiChatBubble::MAX_WIDTH;
|
||||||
|
node->height = bubble->computeHeight();
|
||||||
|
node->localX = 430.0f;
|
||||||
|
node->localY = 0.0f;
|
||||||
|
node->visible = true;
|
||||||
|
node->chatBubble = bubble;
|
||||||
|
|
||||||
|
if (incoming) {
|
||||||
|
node->layoutSettings.hGravity = HorizontalGravity::Left;
|
||||||
|
} else {
|
||||||
|
node->layoutSettings.hGravity = HorizontalGravity::Right;
|
||||||
|
}
|
||||||
|
// Bottom gravity means localY is used as-is for screenRect.y (no parentH transform).
|
||||||
|
// The original static chat nodes all had "vertical_gravity": "bottom" for the same reason.
|
||||||
|
node->layoutSettings.vGravity = VerticalGravity::Bottom;
|
||||||
|
|
||||||
|
parent->children.push_back(node);
|
||||||
|
|
||||||
|
// Re-layout and collect the new node
|
||||||
|
updateAllLayouts();
|
||||||
|
bubble->rect = node->screenRect;
|
||||||
|
bubble->buildMeshes();
|
||||||
|
chatBubbles.push_back(bubble);
|
||||||
|
|
||||||
|
return nodeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UiManager::clearChatBubbles(const std::string& parentNodeName) {
|
||||||
|
if (!root) return;
|
||||||
|
auto parent = findNodeByName(root, parentNodeName);
|
||||||
|
if (!parent) return;
|
||||||
|
|
||||||
|
// Remove children that have a chatBubble component
|
||||||
|
auto& ch = parent->children;
|
||||||
|
ch.erase(std::remove_if(ch.begin(), ch.end(),
|
||||||
|
[](const std::shared_ptr<UiNode>& n) { return n && n->chatBubble != nullptr; }),
|
||||||
|
ch.end());
|
||||||
|
|
||||||
|
// Remove from chatBubbles collection
|
||||||
|
chatBubbles.clear();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ZL
|
} // namespace ZL
|
||||||
@ -245,6 +245,45 @@ namespace ZL {
|
|||||||
void draw(Renderer& renderer) const;
|
void draw(Renderer& renderer) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct UiChatBubble {
|
||||||
|
std::string name;
|
||||||
|
UiRect rect;
|
||||||
|
std::string text;
|
||||||
|
bool incoming = true;
|
||||||
|
|
||||||
|
std::shared_ptr<Texture> texCenter;
|
||||||
|
std::shared_ptr<Texture> texCornerLT;
|
||||||
|
std::shared_ptr<Texture> texCornerLB;
|
||||||
|
std::shared_ptr<Texture> texCornerRT;
|
||||||
|
std::shared_ptr<Texture> texCornerRB;
|
||||||
|
|
||||||
|
static constexpr float CORNER_SIZE = 22.4f;
|
||||||
|
static constexpr float MAX_WIDTH = 320.6f;
|
||||||
|
//static constexpr float PAD_X = 7.0f;
|
||||||
|
//static constexpr float PAD_Y = 5.0f;
|
||||||
|
static constexpr float PAD_X = -15.0f;
|
||||||
|
static constexpr float PAD_Y = -15.0f;
|
||||||
|
|
||||||
|
// 7 meshes: middle-center, top-strip, bottom-strip, LT, RT, LB, RB
|
||||||
|
VertexRenderStruct meshMiddle;
|
||||||
|
VertexRenderStruct meshTopStrip;
|
||||||
|
VertexRenderStruct meshBotStrip;
|
||||||
|
VertexRenderStruct meshCornerLT;
|
||||||
|
VertexRenderStruct meshCornerRT;
|
||||||
|
VertexRenderStruct meshCornerLB;
|
||||||
|
VertexRenderStruct meshCornerRB;
|
||||||
|
|
||||||
|
std::unique_ptr<TextRenderer> textRenderer;
|
||||||
|
int fontSize = 20;
|
||||||
|
|
||||||
|
// Must be called after rect is set and textRenderer is initialised
|
||||||
|
float computeHeight() const;
|
||||||
|
void buildMeshes();
|
||||||
|
// nodeScaleX/Y: the owning UiNode's current scale (for pop-in / animations).
|
||||||
|
// Text position and glyph size are transformed to match the renderer's scale matrix.
|
||||||
|
void draw(Renderer& renderer, float nodeScaleX = 1.0f, float nodeScaleY = 1.0f) const;
|
||||||
|
};
|
||||||
|
|
||||||
struct UiNode {
|
struct UiNode {
|
||||||
std::string name;
|
std::string name;
|
||||||
LayoutType layoutType = LayoutType::Frame;
|
LayoutType layoutType = LayoutType::Frame;
|
||||||
@ -290,6 +329,7 @@ namespace ZL {
|
|||||||
std::shared_ptr<UiTextView> textView;
|
std::shared_ptr<UiTextView> textView;
|
||||||
std::shared_ptr<UiTextField> textField;
|
std::shared_ptr<UiTextField> textField;
|
||||||
std::shared_ptr<UiStaticImage> staticImage;
|
std::shared_ptr<UiStaticImage> staticImage;
|
||||||
|
std::shared_ptr<UiChatBubble> chatBubble;
|
||||||
|
|
||||||
// Анимации
|
// Анимации
|
||||||
struct AnimStep {
|
struct AnimStep {
|
||||||
@ -409,6 +449,19 @@ namespace ZL {
|
|||||||
void clearMenuStack();
|
void clearMenuStack();
|
||||||
int menuStackSize() const;
|
int menuStackSize() const;
|
||||||
|
|
||||||
|
// Dynamically create a chat bubble node and append it to a named parent node.
|
||||||
|
// Returns the generated node name, or "" on failure.
|
||||||
|
std::string addChatBubble(const std::string& parentNodeName,
|
||||||
|
const std::string& text, bool incoming,
|
||||||
|
std::shared_ptr<Texture> texCenter,
|
||||||
|
std::shared_ptr<Texture> texLT, std::shared_ptr<Texture> texLB,
|
||||||
|
std::shared_ptr<Texture> texRT, std::shared_ptr<Texture> texRB,
|
||||||
|
Renderer& renderer, const std::string& fontPath, int fontSize,
|
||||||
|
const std::string& zipFile);
|
||||||
|
|
||||||
|
// Remove all chat-bubble children from named parent and clear chatBubbles collection.
|
||||||
|
void clearChatBubbles(const std::string& parentNodeName);
|
||||||
|
|
||||||
void update(float deltaMs);
|
void update(float deltaMs);
|
||||||
void startAnimation(const std::string& animName);
|
void startAnimation(const std::string& animName);
|
||||||
bool startAnimationOnNode(const std::string& nodeName, const std::string& animName);
|
bool startAnimationOnNode(const std::string& nodeName, const std::string& animName);
|
||||||
@ -462,6 +515,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<UiChatBubble>> chatBubbles;
|
||||||
std::vector<std::shared_ptr<UiNode>> pulsingNodes;
|
std::vector<std::shared_ptr<UiNode>> pulsingNodes;
|
||||||
std::vector<std::shared_ptr<UiNode>> popInNodes;
|
std::vector<std::shared_ptr<UiNode>> popInNodes;
|
||||||
|
|
||||||
@ -483,6 +537,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<UiChatBubble>> chatBubbles;
|
||||||
std::vector<std::shared_ptr<UiNode>> pulsingNodes;
|
std::vector<std::shared_ptr<UiNode>> pulsingNodes;
|
||||||
std::vector<std::shared_ptr<UiNode>> popInNodes;
|
std::vector<std::shared_ptr<UiNode>> popInNodes;
|
||||||
std::map<int64_t, std::shared_ptr<UiButton>> pressedButtons;
|
std::map<int64_t, std::shared_ptr<UiButton>> pressedButtons;
|
||||||
|
|||||||
@ -105,7 +105,7 @@ Node DialogueDatabase::parseNode(const json& j) {
|
|||||||
node.falseNext = j.value("falseNext", "");
|
node.falseNext = j.value("falseNext", "");
|
||||||
node.cutsceneId = j.value("cutsceneId", "");
|
node.cutsceneId = j.value("cutsceneId", "");
|
||||||
node.luaCallback = j.value("luaCallback", "");
|
node.luaCallback = j.value("luaCallback", "");
|
||||||
node.bubbleSlot = j.value("bubbleSlot", "");
|
node.chatBubble = j.value("chatBubble", "");
|
||||||
node.questUnlock = j.value("questUnlock", "");
|
node.questUnlock = j.value("questUnlock", "");
|
||||||
node.questComplete = j.value("questComplete", "");
|
node.questComplete = j.value("questComplete", "");
|
||||||
node.questFail = j.value("questFail", "");
|
node.questFail = j.value("questFail", "");
|
||||||
|
|||||||
@ -87,8 +87,8 @@ void DialogueRuntime::setOnCutsceneFadeInComplete(std::function<void(const std::
|
|||||||
onCutsceneFadeInComplete = std::move(cb);
|
onCutsceneFadeInComplete = std::move(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueRuntime::setOnBubbleSlotReady(std::function<void(const std::string&)> cb) {
|
void DialogueRuntime::setOnChatBubbleReady(std::function<void(const std::string&, bool)> cb) {
|
||||||
onBubbleSlotReady = std::move(cb);
|
onChatBubbleReady = std::move(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueRuntime::stop() {
|
void DialogueRuntime::stop() {
|
||||||
@ -466,8 +466,8 @@ void DialogueRuntime::presentLine(const Node& node) {
|
|||||||
if (!node.luaCallback.empty() && onDialogueLineStarted) {
|
if (!node.luaCallback.empty() && onDialogueLineStarted) {
|
||||||
onDialogueLineStarted(node.luaCallback);
|
onDialogueLineStarted(node.luaCallback);
|
||||||
}
|
}
|
||||||
if (!node.bubbleSlot.empty() && onBubbleSlotReady) {
|
if (!node.chatBubble.empty() && onChatBubbleReady) {
|
||||||
onBubbleSlotReady(node.bubbleSlot);
|
onChatBubbleReady(node.text, node.chatBubble == "in");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,7 +23,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 setOnBubbleSlotReady(std::function<void(const std::string&)> cb);
|
void setOnChatBubbleReady(std::function<void(const std::string&, bool)> cb);
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
void update(int deltaMs);
|
void update(int deltaMs);
|
||||||
@ -60,7 +60,7 @@ private:
|
|||||||
std::function<void(const std::string&)> onDialogueLineStarted;
|
std::function<void(const std::string&)> onDialogueLineStarted;
|
||||||
std::function<void(const std::string&)> onCutsceneLineStarted;
|
std::function<void(const std::string&)> onCutsceneLineStarted;
|
||||||
std::function<void(const std::string&)> onCutsceneFadeInComplete;
|
std::function<void(const std::string&)> onCutsceneFadeInComplete;
|
||||||
std::function<void(const std::string&)> onBubbleSlotReady;
|
std::function<void(const std::string&, bool)> onChatBubbleReady;
|
||||||
std::string activeCutsceneId;
|
std::string activeCutsceneId;
|
||||||
bool fadeInCallbackFired = false;
|
bool fadeInCallbackFired = false;
|
||||||
|
|
||||||
|
|||||||
@ -140,8 +140,8 @@ void DialogueSystem::setOnCutsceneFadeInComplete(std::function<void(const std::s
|
|||||||
runtime.setOnCutsceneFadeInComplete(std::move(cb));
|
runtime.setOnCutsceneFadeInComplete(std::move(cb));
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueSystem::setOnBubbleSlotReady(std::function<void(const std::string&)> cb) {
|
void DialogueSystem::setOnChatBubbleReady(std::function<void(const std::string&, bool)> cb) {
|
||||||
runtime.setOnBubbleSlotReady(std::move(cb));
|
runtime.setOnChatBubbleReady(std::move(cb));
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogueSystem::stopDialogue() {
|
void DialogueSystem::stopDialogue() {
|
||||||
|
|||||||
@ -28,7 +28,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 setOnBubbleSlotReady(std::function<void(const std::string&)> cb);
|
void setOnChatBubbleReady(std::function<void(const std::string&, bool)> cb);
|
||||||
void setOnDialogueAdvanced(std::function<void()> cb);
|
void setOnDialogueAdvanced(std::function<void()> cb);
|
||||||
void stopDialogue();
|
void stopDialogue();
|
||||||
|
|
||||||
|
|||||||
@ -97,8 +97,8 @@ struct Node {
|
|||||||
// For CutsceneStart
|
// For CutsceneStart
|
||||||
std::string cutsceneId;
|
std::string cutsceneId;
|
||||||
|
|
||||||
// Name of the UI node (StaticImage) to reveal in the phone chat when this line is shown
|
// "in" or "out" — creates a dynamic chat bubble; empty = not a chat message
|
||||||
std::string bubbleSlot;
|
std::string chatBubble;
|
||||||
|
|
||||||
// Quest actions fired when this line is presented (empty = no action)
|
// Quest actions fired when this line is presented (empty = no action)
|
||||||
std::string questUnlock;
|
std::string questUnlock;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user