Added startup scripts

This commit is contained in:
Vladislav Khorev 2026-05-09 14:14:04 +03:00
parent a479fa4ac1
commit 13ef92160d
26 changed files with 117164 additions and 150 deletions

View File

@ -174,6 +174,7 @@ set(EMSCRIPTEN_LINK_FLAGS
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/shaders@resources/shaders" "--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/shaders@resources/shaders"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start.lua@resources/start.lua" "--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start.lua@resources/start.lua"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start2.lua@resources/start2.lua" "--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start2.lua@resources/start2.lua"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/start_dorm.lua@resources/start_dorm.lua"
"--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../audio@audio" "--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../audio@audio"
) )

View File

@ -51,6 +51,87 @@
"positionZ": 0.0, "positionZ": 0.0,
"scale": 1.0, "scale": 1.0,
"interactive": false "interactive": false
},
{
"name": "Bed001",
"texturePath": "resources/w/dorm/ikea_bed_bake002.png",
"meshPath": "resources/w/dorm/ikea_bed001.txt",
"rotationX": 0.0,
"rotationY": 0.0,
"rotationZ": 0.0,
"positionX": 0.0,
"positionY": -0.1,
"positionZ": 0.0,
"scale": 1.0,
"interactive": false
},
{
"name": "Chair001",
"texturePath": "resources/w/dorm/ikea_chair_bake002.png",
"meshPath": "resources/w/dorm/ikea_chair001.txt",
"rotationX": 0.0,
"rotationY": 0.0,
"rotationZ": 0.0,
"positionX": 0.0,
"positionY": -0.1,
"positionZ": 0.0,
"scale": 1.0,
"interactive": false
},
{
"name": "Table001",
"texturePath": "resources/w/dorm/ikea_table_bake003.png",
"meshPath": "resources/w/dorm/ikea_table001.txt",
"rotationX": 0.0,
"rotationY": 0.0,
"rotationZ": 0.0,
"positionX": 0.0,
"positionY": -0.1,
"positionZ": 0.0,
"scale": 1.0,
"interactive": false
},
{
"name": "Phone001",
"texturePath": "resources/w/dorm/phone001_tex001.png",
"meshPath": "resources/w/dorm/phone001.txt",
"rotationX": 0.0,
"rotationY": 0.0,
"rotationZ": 0.0,
"positionX": 5.8729,
"positionY": 1.04917,
"positionZ": -12.5262,
"scale": 1.0,
"interactive": true,
"item": {
"id": "phone",
"name": "Телефон",
"description": "Я не могу себе представить жизнь без своего телефона",
"icon": "resources/fire2.png",
"radius": 0.3
},
"activateFunction" : "on_phone_pickup"
},
{
"name": "Journal001",
"texturePath": "resources/w/dorm/journal001_tex002.png",
"meshPath": "resources/w/dorm/journal001.txt",
"rotationX": 0.0,
"rotationY": 0.0,
"rotationZ": 0.0,
"positionX": 5.05146,
"positionY": 1.06711,
"positionZ": -12.4661,
"scale": 1.0,
"interactive": true,
"item": {
"id": "journal",
"name": "Телефон",
"description": "Это мой журнал куда я вношу свои заметки.",
"icon": "resources/fire2.png",
"radius": 0.3
},
"activateFunction" : "on_journal_pickup"
} }
] ]
} }

View File

@ -28,25 +28,26 @@
] ]
} }
], ],
"obstacles": [ "obstacles": [
{ {
"name": "editor_obstacle_1", "name": "editor_obstacle_1",
"polygon": [ "polygon": [
[ [
1.4521160125732422, 1.2386366128921509,
-15.647087097167969 -16.910831451416016
], ],
[ [
0.7836513519287109, 1.2082710266113281,
-15.604467391967773 -15.74820327758789
], ],
[ [
0.8457736968994141, 0.9836082458496094,
-17.430662155151367 -15.767669677734375
], ],
[ [
1.3778166770935059, 0.9937065839767456,
-17.40001678466797 -16.89757537841797
] ]
] ]
}, },
@ -54,20 +55,20 @@
"name": "editor_obstacle_2", "name": "editor_obstacle_2",
"polygon": [ "polygon": [
[ [
1.238701343536377, 1.2371597290039063,
-17.41904640197754 -14.667628288269043
], ],
[ [
1.2542033195495605, 1.0021405220031738,
-16.888349533081055 -14.565974235534668
], ],
[ [
8.557634353637695, 0.8893742561340332,
-16.686010360717773 -11.02865982055664
], ],
[ [
8.580190658569336, 1.2916733026504517,
-17.49493980407715 -11.256534576416016
] ]
] ]
}, },
@ -75,20 +76,20 @@
"name": "editor_obstacle_3", "name": "editor_obstacle_3",
"polygon": [ "polygon": [
[ [
8.581539154052734, 1.15517258644104,
-17.44879150390625 -16.825780868530273
], ],
[ [
9.2276029586792, 8.42333698272705,
-17.467662811279297 -16.937246322631836
], ],
[ [
9.311224937438965, 8.639735221862793,
-11.442724227905273 -17.410985946655273
], ],
[ [
8.525369644165039, 1.1124024391174316,
-11.465954780578613 -17.468708038330078
] ]
] ]
}, },
@ -96,20 +97,20 @@
"name": "editor_obstacle_4", "name": "editor_obstacle_4",
"polygon": [ "polygon": [
[ [
8.59122085571289, 1.952521800994873,
-11.583345413208008 -13.470939636230469
], ],
[ [
8.580953598022461, 1.947329044342041,
-11.144271850585938 -13.262446403503418
], ],
[ [
3.9417855739593506, 1.2782373428344727,
-11.05494213104248 -13.2465238571167
], ],
[ [
3.879462718963623, 1.2633711099624634,
-11.607364654541016 -13.548421859741211
] ]
] ]
}, },
@ -117,20 +118,20 @@
"name": "editor_obstacle_5", "name": "editor_obstacle_5",
"polygon": [ "polygon": [
[ [
4.017904758453369, 2.958512783050537,
-11.611408233642578 -13.523996353149414
], ],
[ [
4.006087303161621, 2.934694528579712,
-13.59709644317627 -13.240300178527832
], ],
[ [
3.777372121810913, 3.9795844554901123,
-13.521135330200195 -13.26203727722168
], ],
[ [
3.7294042110443115, 3.9670143127441406,
-11.210392951965332 -13.51730728149414
] ]
] ]
}, },
@ -138,20 +139,20 @@
"name": "editor_obstacle_6", "name": "editor_obstacle_6",
"polygon": [ "polygon": [
[ [
2.783853769302368, 3.643568992614746,
-13.538301467895508 -13.285079956054688
], ],
[ [
2.766845703125, 3.648775577545166,
-13.329962730407715 -11.270064353942871
], ],
[ [
3.921206474304199, 3.9994444847106934,
-13.34058952331543 -11.282587051391602
], ],
[ [
3.937540054321289, 3.920609474182129,
-13.572001457214355 -13.174944877624512
] ]
] ]
}, },
@ -159,20 +160,79 @@
"name": "editor_obstacle_7", "name": "editor_obstacle_7",
"polygon": [ "polygon": [
[ [
2.046168327331543, 1.3403878211975098,
-13.493659019470215 -11.485700607299805
], ],
[ [
2.006760597229004, 8.704232215881348,
-13.261573791503906 -11.600449562072754
], ],
[ [
1.1306328773498535, 8.6791410446167,
-13.21288776397705 -11.190678596496582
], ],
[ [
1.190417766571045, 1.2627182006835938,
-13.537941932678223 -10.95035457611084
]
]
},
{
"name": "editor_obstacle_1",
"polygon": [
[
5.460248947143555,
-16.92827796936035
],
[
5.473773956298828,
-15.50655746459961
],
[
8.639936447143555,
-15.49016284942627
],
[
8.641860961914063,
-17.03392791748047
]
]
},
{
"name": "editor_obstacle_2",
"polygon": [
[
4.033830165863037,
-12.481351852416992
],
[
6.007711887359619,
-12.465056419372559
],
[
6.156071186065674,
-11.502941131591797
],
[
4.018871784210205,
-11.45417594909668
]
]
},
{
"name": "editor_obstacle_3",
"polygon": [
[
3.942854404449463,
-13.410148620605469
],
[
3.99332594871521,
-12.681471824645996
],
[
4.6172685623168945,
-12.7000093460083
] ]
] ]
}, },
@ -180,42 +240,106 @@
"name": "editor_obstacle_8", "name": "editor_obstacle_8",
"polygon": [ "polygon": [
[ [
1.2021913528442383, 8.662647247314453,
-14.716050148010254 -11.577871322631836
], ],
[ [
1.3182783126831055, 8.59707260131836,
-11.532548904418945 -17.01089096069336
], ],
[ [
1.0644679069519043, 8.989262580871582,
-11.525135040283203 -16.998661041259766
], ],
[ [
0.9918317794799805, 9.054760932922363,
-14.802279472351074 -11.652122497558594
] ]
] ]
}, },
{ {
"name": "editor_obstacle_9", "name": "editor_obstacle_1x",
"polygon": [ "polygon": [
[ [
1.2470345497131348, 2.8632972240448,
-11.599748611450195 -4.288420677185059
], ],
[ [
3.9473354816436768, 2.4562156200408936,
-11.655533790588379 -4.224598407745361
], ],
[ [
3.9574456214904785, 2.397826910018921,
-11.309428215026855 -5.586108684539795
], ],
[ [
1.2795448303222656, 2.843082904815674,
-11.27739143371582 -5.562361717224121
] ]
] ]
}] },
{
"name": "editor_obstacle_2x",
"polygon": [
[
2.669132709503174,
-5.488218307495117
],
[
8.675596237182617,
-5.436037540435791
],
[
8.74371337890625,
-6.103575706481934
],
[
2.6533708572387695,
-6.057576656341553
]
]
},
{
"name": "editor_obstacle_3x",
"polygon": [
[
2.490814208984375,
-2.175637722015381
],
[
2.5649731159210205,
5.601792812347412
],
[
9.002458572387695,
5.9258809089660645
],
[
8.68370532989502,
-2.235023021697998
]
]
},
{
"name": "editor_obstacle_4x",
"polygon": [
[
6.314299583435059,
-2.1619086265563965
],
[
6.418157577514648,
-5.558330535888672
],
[
8.6978759765625,
-5.538100242614746
],
[
8.630842208862305,
-2.2032206058502197
]
]
}
]
} }

View File

@ -0,0 +1,367 @@
{
"cellSize": 0.1,
"agentRadius": 0.25,
"floorY": 0.0,
"objectPadding": 0.15,
"boundaryPadding": 0.15,
"areas": [
{
"name": "main_corridor",
"available": true,
"polygon": [
[
-100,
100
],
[
100,
100
],
[
100,
-100
],
[
-100,
-100
]
]
}
],
"obstacles": [
{
"name": "editor_obstacle_1",
"polygon": [
[
1.2386366128921509,
-16.910831451416016
],
[
1.2082710266113281,
-15.74820327758789
],
[
0.9836082458496094,
-15.767669677734375
],
[
0.9937065839767456,
-16.89757537841797
]
]
},
{
"name": "editor_obstacle_2",
"polygon": [
[
1.2371597290039063,
-14.667628288269043
],
[
1.0021405220031738,
-14.565974235534668
],
[
0.8893742561340332,
-11.02865982055664
],
[
1.2916733026504517,
-11.256534576416016
]
]
},
{
"name": "editor_obstacle_3",
"polygon": [
[
1.15517258644104,
-16.825780868530273
],
[
8.42333698272705,
-16.937246322631836
],
[
8.639735221862793,
-17.410985946655273
],
[
1.1124024391174316,
-17.468708038330078
]
]
},
{
"name": "editor_obstacle_4",
"polygon": [
[
1.952521800994873,
-13.470939636230469
],
[
1.947329044342041,
-13.262446403503418
],
[
1.2782373428344727,
-13.2465238571167
],
[
1.2633711099624634,
-13.548421859741211
]
]
},
{
"name": "editor_obstacle_5",
"polygon": [
[
2.958512783050537,
-13.523996353149414
],
[
2.934694528579712,
-13.240300178527832
],
[
3.9795844554901123,
-13.26203727722168
],
[
3.9670143127441406,
-13.51730728149414
]
]
},
{
"name": "editor_obstacle_6",
"polygon": [
[
3.643568992614746,
-13.285079956054688
],
[
3.648775577545166,
-11.270064353942871
],
[
3.9994444847106934,
-11.282587051391602
],
[
3.920609474182129,
-13.174944877624512
]
]
},
{
"name": "editor_obstacle_7",
"polygon": [
[
1.3403878211975098,
-11.485700607299805
],
[
8.704232215881348,
-11.600449562072754
],
[
8.6791410446167,
-11.190678596496582
],
[
1.2627182006835938,
-10.95035457611084
]
]
},
{
"name": "editor_obstacle_8",
"polygon": [
[
8.662647247314453,
-11.577871322631836
],
[
8.59707260131836,
-17.01089096069336
],
[
8.989262580871582,
-16.998661041259766
],
[
9.054760932922363,
-11.652122497558594
]
]
},
{
"name": "editor_obstacle_1x",
"polygon": [
[
2.8632972240448,
-4.288420677185059
],
[
2.4562156200408936,
-4.224598407745361
],
[
2.397826910018921,
-5.586108684539795
],
[
2.843082904815674,
-5.562361717224121
]
]
},
{
"name": "editor_obstacle_2x",
"polygon": [
[
2.669132709503174,
-5.488218307495117
],
[
8.675596237182617,
-5.436037540435791
],
[
8.74371337890625,
-6.103575706481934
],
[
2.6533708572387695,
-6.057576656341553
]
]
},
{
"name": "editor_obstacle_3x",
"polygon": [
[
2.490814208984375,
-2.175637722015381
],
[
2.5649731159210205,
5.601792812347412
],
[
9.002458572387695,
5.9258809089660645
],
[
8.68370532989502,
-2.235023021697998
]
]
},
{
"name": "editor_obstacle_4x",
"polygon": [
[
6.314299583435059,
-2.1619086265563965
],
[
6.418157577514648,
-5.558330535888672
],
[
8.6978759765625,
-5.538100242614746
],
[
8.630842208862305,
-2.2032206058502197
]
]
},
{
"name": "editor_obstacle_1",
"polygon": [
[
5.460248947143555,
-16.92827796936035
],
[
5.473773956298828,
-15.50655746459961
],
[
8.639936447143555,
-15.49016284942627
],
[
8.641860961914063,
-17.03392791748047
]
]
},
{
"name": "editor_obstacle_2",
"polygon": [
[
4.033830165863037,
-12.481351852416992
],
[
6.007711887359619,
-12.465056419372559
],
[
6.156071186065674,
-11.502941131591797
],
[
4.018871784210205,
-11.45417594909668
]
]
},
{
"name": "editor_obstacle_3",
"polygon": [
[
3.942854404449463,
-13.410148620605469
],
[
3.99332594871521,
-12.681471824645996
],
[
4.6172685623168945,
-12.7000093460083
]
]
},
{
"name": "dialog_start_block001",
"polygon": [
[
0.9441208839416504,
-14.674051284790039
],
[
1.351646900177002,
-14.717156410217285
],
[
1.3264522552490234,
-15.757955551147461
],
[
0.9525976181030273,
-15.736042976379395
]
]
}
]
}

View File

@ -0,0 +1,4 @@
{
"trigger_zones": [
]
}

View File

@ -0,0 +1,4 @@
{
"trigger_zones": [
]
}

View File

@ -0,0 +1,22 @@
{
"trigger_zones": [
{
"id": "pickup_phone_zone001",
"positionX": 2.54938,
"positionY": 0.0,
"positionZ": -15.2422,
"radius": 1.0,
"hysteresis": 0.1,
"enabled": true
},
{
"id": "ladder_zone001",
"positionX": 3.7569,
"positionY": 0.0,
"positionZ": -3.50058,
"radius": 2.0,
"hysteresis": 0.1,
"enabled": true
}
]
}

View File

@ -25,6 +25,60 @@
"type": "End" "type": "End"
} }
] ]
},
{
"id": "dialog_phone001",
"start": "line_1",
"nodes": [
{
"id": "line_1",
"type": "Line",
"speaker": "Бекзат",
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
"text": "Я не буду никуда идти без своего телефона и записной книжки!",
"next": "end_1"
},
{
"id": "end_1",
"type": "End"
}
]
},
{
"id": "dialog_phone_pickup001",
"start": "line_1",
"nodes": [
{
"id": "line_1",
"type": "Line",
"speaker": "Бекзат",
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
"text": "Отлично, вот и мой телефон! Надо проверить новые сообщения.",
"next": "end_1"
},
{
"id": "end_1",
"type": "End"
}
]
},
{
"id": "dialog_journal_pickup001",
"start": "line_1",
"nodes": [
{
"id": "line_1",
"type": "Line",
"speaker": "Бекзат",
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
"text": "Возьму журнал с собой! Там все мои записи.",
"next": "end_1"
},
{
"id": "end_1",
"type": "End"
}
]
}, },
{ {
"id": "dialog_taxi001", "id": "dialog_taxi001",

View File

@ -14,4 +14,48 @@ step1()
game_api.start_dialogue("dialog_start001") game_api.start_dialogue("dialog_start001")
phone_picked_up = false
journal_picked_up = false
function on_phone_pickup()
game_api.pickup_item("phone")
game_api.start_dialogue("dialog_phone_pickup001")
phone_picked_up = true
end
function on_journal_pickup()
game_api.pickup_item("journal")
game_api.start_dialogue("dialog_journal_pickup001")
journal_picked_up = true
end
function pickup_phone_zone001_enter_callback()
print("pickup_phone_zone001_enter_callback--!")
if (not phone_picked_up) or (not journal_picked_up) then
game_api.start_dialogue("dialog_phone001")
game_api.switch_navigation(1)
end
end
function pickup_phone_zone001_exit_callback()
print("pickup_phone_zone001_exit_callback--!")
game_api.switch_navigation(0)
end
game_api.set_trigger_zone_callbacks("pickup_phone_zone001",
pickup_phone_zone001_enter_callback,
pickup_phone_zone001_exit_callback
)
function ladder_zone001_enter_callback()
game_api.start_dialogue("dialog_second_floor001")
end
game_api.set_trigger_zone_callbacks("ladder_zone001",
ladder_zone001_enter_callback,
nil
)
print("Lua script loaded successfully--!") print("Lua script loaded successfully--!")

File diff suppressed because it is too large Load Diff

BIN
resources/w/dorm/ikea_bed_bake002.png (Stored with Git LFS) Normal file

Binary file not shown.

File diff suppressed because it is too large Load Diff

BIN
resources/w/dorm/ikea_chair_bake002.png (Stored with Git LFS) Normal file

Binary file not shown.

File diff suppressed because it is too large Load Diff

BIN
resources/w/dorm/ikea_table_bake003.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,39 @@
===Vertices (Split by UV/Normal): 24
V 0: Pos(0.26, 0.15, 0.02) Norm(0.577351, 0.57735, 0.57735) UV(0.944444, 0.416667)
V 1: Pos(0.26, -0.15, -0.02) Norm(0.577351, -0.57735, -0.57735) UV(1.0, 0.0)
V 2: Pos(0.26, 0.15, -0.02) Norm(0.577349, 0.577358, -0.577343) UV(1.0, 0.416667)
V 3: Pos(0.26, -0.15, 0.02) Norm(0.577349, -0.577358, 0.577343) UV(0.833333, 0.722223)
V 4: Pos(-0.26, -0.15, -0.02) Norm(-0.577349, -0.577358, -0.577343) UV(0.888889, 0.0)
V 5: Pos(0.26, -0.15, -0.02) Norm(0.577351, -0.57735, -0.57735) UV(0.888889, 0.722223)
V 6: Pos(-0.26, -0.15, 0.02) Norm(-0.577351, -0.57735, 0.57735) UV(0.944444, 0.833334)
V 7: Pos(-0.26, 0.15, -0.02) Norm(-0.577351, 0.57735, -0.57735) UV(1.0, 0.416667)
V 8: Pos(-0.26, -0.15, -0.02) Norm(-0.577349, -0.577358, -0.577343) UV(1.0, 0.833334)
V 9: Pos(-0.26, 0.15, 0.02) Norm(-0.577349, 0.577358, 0.577343) UV(0.888889, 0.722223)
V 10: Pos(0.26, 0.15, -0.02) Norm(0.577349, 0.577358, -0.577343) UV(0.944445, 0.0)
V 11: Pos(-0.26, 0.15, -0.02) Norm(-0.577351, 0.57735, -0.57735) UV(0.944444, 0.722223)
V 12: Pos(-0.26, -0.15, -0.02) Norm(-0.577349, -0.577358, -0.577343) UV(0.0, 0.0)
V 13: Pos(0.26, 0.15, -0.02) Norm(0.577349, 0.577358, -0.577343) UV(0.416667, 0.722223)
V 14: Pos(0.26, -0.15, -0.02) Norm(0.577351, -0.57735, -0.57735) UV(-0.0, 0.722223)
V 15: Pos(0.26, -0.15, 0.02) Norm(0.577349, -0.577358, 0.577343) UV(0.416667, 0.0)
V 16: Pos(-0.26, 0.15, 0.02) Norm(-0.577349, 0.577358, 0.577343) UV(0.833333, 0.722223)
V 17: Pos(-0.26, -0.15, 0.02) Norm(-0.577351, -0.57735, 0.57735) UV(0.416667, 0.722223)
V 18: Pos(0.26, -0.15, 0.02) Norm(0.577349, -0.577358, 0.577343) UV(0.944445, 0.0)
V 19: Pos(-0.26, -0.15, 0.02) Norm(-0.577351, -0.57735, 0.57735) UV(0.833334, 0.0)
V 20: Pos(-0.26, 0.15, 0.02) Norm(-0.577349, 0.577358, 0.577343) UV(0.944444, 0.416667)
V 21: Pos(0.26, 0.15, 0.02) Norm(0.577351, 0.57735, 0.57735) UV(0.888889, 0.0)
V 22: Pos(-0.26, 0.15, -0.02) Norm(-0.577351, 0.57735, -0.57735) UV(0.416667, 0.0)
V 23: Pos(0.26, 0.15, 0.02) Norm(0.577351, 0.57735, 0.57735) UV(0.833334, 0.0)
===Triangles (Indices): 12
Tri: 0 1 2
Tri: 3 4 5
Tri: 6 7 8
Tri: 9 10 11
Tri: 12 13 14
Tri: 15 16 17
Tri: 0 18 1
Tri: 3 19 4
Tri: 6 20 7
Tri: 9 21 10
Tri: 12 22 13
Tri: 15 23 16

BIN
resources/w/dorm/journal001_tex002.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,39 @@
===Vertices (Split by UV/Normal): 24
V 0: Pos(0.180001, 0.08, 0.02) Norm(0.577335, 0.577358, 0.577358) UV(0.909091, 0.363636)
V 1: Pos(0.180001, -0.08, -0.02) Norm(0.577335, -0.577358, -0.577358) UV(1.0, 0.0)
V 2: Pos(0.180001, 0.08, -0.02) Norm(0.577357, 0.577362, -0.577332) UV(1.0, 0.363636)
V 3: Pos(0.180001, -0.08, 0.02) Norm(0.577357, -0.577362, 0.577332) UV(0.727273, 0.818184)
V 4: Pos(-0.179999, -0.08, -0.02) Norm(-0.577357, -0.577362, -0.577332) UV(0.818182, 0.0)
V 5: Pos(0.180001, -0.08, -0.02) Norm(0.577335, -0.577358, -0.577358) UV(0.818182, 0.818184)
V 6: Pos(-0.179999, -0.08, 0.02) Norm(-0.577335, -0.577358, 0.577358) UV(0.909091, 0.727273)
V 7: Pos(-0.179999, 0.08, -0.02) Norm(-0.577335, 0.577358, -0.577358) UV(1.0, 0.363636)
V 8: Pos(-0.179999, -0.08, -0.02) Norm(-0.577357, -0.577362, -0.577332) UV(1.0, 0.727273)
V 9: Pos(-0.179999, 0.08, 0.02) Norm(-0.577357, 0.577362, 0.577332) UV(0.818182, 0.818184)
V 10: Pos(0.180001, 0.08, -0.02) Norm(0.577357, 0.577362, -0.577332) UV(0.909091, 0.0)
V 11: Pos(-0.179999, 0.08, -0.02) Norm(-0.577335, 0.577358, -0.577358) UV(0.909091, 0.818184)
V 12: Pos(-0.179999, -0.08, -0.02) Norm(-0.577357, -0.577362, -0.577332) UV(0.0, 0.0)
V 13: Pos(0.180001, 0.08, -0.02) Norm(0.577357, 0.577362, -0.577332) UV(0.363636, 0.818184)
V 14: Pos(0.180001, -0.08, -0.02) Norm(0.577335, -0.577358, -0.577358) UV(-0.0, 0.818184)
V 15: Pos(0.180001, -0.08, 0.02) Norm(0.577357, -0.577362, 0.577332) UV(0.363636, 0.0)
V 16: Pos(-0.179999, 0.08, 0.02) Norm(-0.577357, 0.577362, 0.577332) UV(0.727273, 0.818184)
V 17: Pos(-0.179999, -0.08, 0.02) Norm(-0.577335, -0.577358, 0.577358) UV(0.363636, 0.818184)
V 18: Pos(0.180001, -0.08, 0.02) Norm(0.577357, -0.577362, 0.577332) UV(0.909091, 0.0)
V 19: Pos(-0.179999, -0.08, 0.02) Norm(-0.577335, -0.577358, 0.577358) UV(0.727273, 0.0)
V 20: Pos(-0.179999, 0.08, 0.02) Norm(-0.577357, 0.577362, 0.577332) UV(0.909091, 0.363636)
V 21: Pos(0.180001, 0.08, 0.02) Norm(0.577335, 0.577358, 0.577358) UV(0.818182, 0.0)
V 22: Pos(-0.179999, 0.08, -0.02) Norm(-0.577335, 0.577358, -0.577358) UV(0.363636, 0.0)
V 23: Pos(0.180001, 0.08, 0.02) Norm(0.577335, 0.577358, 0.577358) UV(0.727273, 0.0)
===Triangles (Indices): 12
Tri: 0 1 2
Tri: 3 4 5
Tri: 6 7 8
Tri: 9 10 11
Tri: 12 13 14
Tri: 15 16 17
Tri: 0 18 1
Tri: 3 19 4
Tri: 6 20 7
Tri: 9 21 10
Tri: 12 22 13
Tri: 15 23 16

BIN
resources/w/dorm/phone001_tex001.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -164,8 +164,9 @@ namespace ZL
params1.gameObjectsJsonPath = "resources/config2/gameobjects.json"; params1.gameObjectsJsonPath = "resources/config2/gameobjects.json";
params1.npcsJsonPath = "resources/config2/npcs.json"; params1.npcsJsonPath = "resources/config2/npcs.json";
params1.dialoguesJsonPath = "resources/dialogue/sample_dialogues.json"; params1.dialoguesJsonPath = "resources/dialogue/sample_dialogues.json";
params1.navigationJsonPath = "resources/config2/navigation.json"; params1.navigationJsonPaths = {"resources/config2/navigation.json"};
params1.teleportsJsonPath = "resources/config2/teleports.json"; params1.teleportsJsonPath = "resources/config2/teleports.json";
params1.triggerZonesJsonPath = "resources/config2/trigger_zones.json";
params1.scriptPath = "resources/start.lua"; params1.scriptPath = "resources/start.lua";
params1.playerPosition = Eigen::Vector3f(0.942694, 0, -9.63104); params1.playerPosition = Eigen::Vector3f(0.942694, 0, -9.63104);
@ -176,8 +177,9 @@ namespace ZL
params2.roomMeshPath = "resources/w/exterior/Segmented_Plane002.txt"; params2.roomMeshPath = "resources/w/exterior/Segmented_Plane002.txt";
params2.roomTexturePath = "resources/w/exterior/Segmented_Plane002.png"; params2.roomTexturePath = "resources/w/exterior/Segmented_Plane002.png";
params2.gameObjectsJsonPath = "resources/config2/gameobjects2.json"; params2.gameObjectsJsonPath = "resources/config2/gameobjects2.json";
params2.navigationJsonPath = "resources/config2/navigation2.json"; params2.navigationJsonPaths = {"resources/config2/navigation2.json"};
params2.teleportsJsonPath = "resources/config2/teleports2.json"; params2.teleportsJsonPath = "resources/config2/teleports2.json";
params2.triggerZonesJsonPath = "resources/config2/trigger_zones2.json";
params2.scriptPath = "resources/start2.lua"; params2.scriptPath = "resources/start2.lua";
params2.playerPosition = Eigen::Vector3f(5, 0, -18.4); params2.playerPosition = Eigen::Vector3f(5, 0, -18.4);
params2.npcsJsonPath = "resources/config2/npcs2.json"; params2.npcsJsonPath = "resources/config2/npcs2.json";
@ -191,11 +193,11 @@ namespace ZL
params_dorm.gameObjectsJsonPath = "resources/config2/gameobjects_dorm.json"; params_dorm.gameObjectsJsonPath = "resources/config2/gameobjects_dorm.json";
params_dorm.npcsJsonPath = "resources/config2/npcs_dorm.json"; params_dorm.npcsJsonPath = "resources/config2/npcs_dorm.json";
params_dorm.dialoguesJsonPath = "resources/dialogue/dorm_dialogues.json"; params_dorm.dialoguesJsonPath = "resources/dialogue/dorm_dialogues.json";
params_dorm.navigationJsonPath = "resources/config2/navigation_dorm.json"; params_dorm.navigationJsonPaths = {"resources/config2/navigation_dorm.json", "resources/config2/navigation_dorm_tutorial001.json" };
params_dorm.teleportsJsonPath = "resources/config2/teleports_dorm.json"; params_dorm.teleportsJsonPath = "resources/config2/teleports_dorm.json";
params_dorm.triggerZonesJsonPath = "resources/config2/trigger_zones_dorm.json";
params_dorm.scriptPath = "resources/start_dorm.lua"; params_dorm.scriptPath = "resources/start_dorm.lua";
params_dorm.playerPosition = Eigen::Vector3f(6.0357, 0, -16.0581); params_dorm.playerPosition = Eigen::Vector3f(6.76345, 0, -14.6022);
locations["location_dorm"] = std::make_shared<Location>(renderer, inventory); locations["location_dorm"] = std::make_shared<Location>(renderer, inventory);
locations["location_dorm"]->setup(params_dorm); locations["location_dorm"]->setup(params_dorm);
@ -534,15 +536,14 @@ namespace ZL
case SDLK_o: case SDLK_o:
//y = y + 0.002; //y = y + 0.002;
//currentLocation->player->hp = 200; //currentLocation->player->hp = 200;
currentLocation->npcs[0]->walkSpeed += 0.02f; currentLocation->npcs[0]->walkSpeed += 0.01f;
std::cout << "Walk speed: " << currentLocation->npcs[0]->walkSpeed << std::endl; std::cout << "Walk speed: " << currentLocation->npcs[0]->walkSpeed << std::endl;
break; break;
case SDLK_k: case SDLK_k:
//y = y - 0.002; //y = y - 0.002;
std::cout << "Player pos: " << currentLocation->player->position.transpose() << std::endl; std::cout << "Player pos: " << currentLocation->player->position.transpose() << std::endl;
currentLocation->npcs[0]->walkSpeed -= 0.02f; currentLocation->npcs[0]->walkSpeed -= 0.01f;
std::cout << "Walk speed: " << currentLocation->npcs[0]->walkSpeed << std::endl; std::cout << "Walk speed: " << currentLocation->npcs[0]->walkSpeed << std::endl;
break; break;

View File

@ -137,6 +137,7 @@ namespace ZL
} }
loadTeleportZones(params.teleportsJsonPath, CONST_ZIP_FILE); loadTeleportZones(params.teleportsJsonPath, CONST_ZIP_FILE);
loadTriggerZones(params.triggerZonesJsonPath, CONST_ZIP_FILE);
#ifndef EMSCRIPTEN #ifndef EMSCRIPTEN
// Create shadow map (2048x2048, ortho size 40, near 0.1, far 100) // Create shadow map (2048x2048, ortho size 40, near 0.1, far 100)
@ -145,7 +146,7 @@ namespace ZL
std::cout << "Shadow map initialized" << std::endl; std::cout << "Shadow map initialized" << std::endl;
#endif #endif
setupNavigation(params.navigationJsonPath); setupNavigation(params.navigationJsonPaths);
dialogueSystem.init(renderer, CONST_ZIP_FILE); dialogueSystem.init(renderer, CONST_ZIP_FILE);
dialogueSystem.loadDatabase(params.dialoguesJsonPath); dialogueSystem.loadDatabase(params.dialoguesJsonPath);
@ -165,7 +166,7 @@ namespace ZL
false false
});*/ });*/
navigationEditorConfigPath = params.navigationJsonPath; // navigationMapPaths is already populated by setupNavigation
scriptEngine.init(this, &inventory, params.scriptPath); scriptEngine.init(this, &inventory, params.scriptPath);
@ -231,13 +232,84 @@ namespace ZL
std::cout << "[TELEPORT] Loaded " << teleportZones.size() << " teleport(s) from " << jsonPath << std::endl; std::cout << "[TELEPORT] Loaded " << teleportZones.size() << " teleport(s) from " << jsonPath << std::endl;
} }
void Location::setupNavigation(const std::string& navigationJsonPath) void Location::loadTriggerZones(const std::string& jsonPath, const char* zipFile)
{ {
// Static navigation blockers are defined in the navigation JSON as polygons. if (jsonPath.empty()) return;
// NPC + player are handled as dynamic obstacles, so they are intentionally NOT in JSON.
navigation.build({}, navigationJsonPath, CONST_ZIP_FILE);
if (navigationEditorMode) { std::string content;
try {
if (!zipFile || zipFile[0] == '\0') {
content = readTextFile(jsonPath);
} else {
auto buf = readFileFromZIP(jsonPath, zipFile);
if (buf.empty()) {
std::cerr << "[TRIGGER] Failed to read " << jsonPath << " from zip" << std::endl;
return;
}
content.assign(buf.begin(), buf.end());
}
} catch (const std::exception& e) {
std::cerr << "[TRIGGER] Failed to open " << jsonPath << ": " << e.what() << std::endl;
return;
}
using json = nlohmann::json;
json j;
try {
j = json::parse(content);
} catch (const std::exception& e) {
std::cerr << "[TRIGGER] JSON parse error in " << jsonPath << ": " << e.what() << std::endl;
return;
}
if (!j.contains("trigger_zones") || !j["trigger_zones"].is_array()) return;
for (const auto& item : j["trigger_zones"]) {
TriggerZone tz;
tz.id = item.value("id", "");
tz.enabled = item.value("enabled", true);
tz.radius = item.value("radius", 1.5f);
tz.hysteresis = item.value("hysteresis", 0.3f);
tz.position = Eigen::Vector3f(
item.value("positionX", 0.0f),
item.value("positionY", 0.0f),
item.value("positionZ", 0.0f)
);
triggerZones.push_back(std::move(tz));
}
std::cout << "[TRIGGER] Loaded " << triggerZones.size() << " trigger zone(s) from " << jsonPath << std::endl;
}
void Location::updateTriggerZones(const Eigen::Vector3f& playerPos)
{
for (auto& tz : triggerZones) {
if (!tz.enabled) continue;
const float dist = (playerPos - tz.position).norm();
if (!tz.playerInside && dist <= tz.radius) {
tz.playerInside = true;
scriptEngine.callTriggerEnterCallback(tz.id);
} else if (tz.playerInside && dist > tz.radius + tz.hysteresis) {
tz.playerInside = false;
scriptEngine.callTriggerExitCallback(tz.id);
}
}
}
void Location::setupNavigation(const std::vector<std::string>& paths)
{
navigationMapPaths = paths;
navigationMaps.clear();
navigationMaps.resize(paths.size());
for (size_t i = 0; i < paths.size(); ++i) {
navigationMaps[i].build({}, paths[i], CONST_ZIP_FILE);
}
activeNavigationIndex = 0;
navigation = navigationMaps.empty() ? nullptr : &navigationMaps[0];
if (navigationEditorMode && navigation) {
navigationEditorBuildNavMeshes(); navigationEditorBuildNavMeshes();
} }
@ -245,6 +317,7 @@ namespace ZL
auto makePlanner = [this](const Character* self) { auto makePlanner = [this](const Character* self) {
return [this, self](const Eigen::Vector3f& start, const Eigen::Vector3f& end) { return [this, self](const Eigen::Vector3f& start, const Eigen::Vector3f& end) {
if (!navigation) return std::vector<Eigen::Vector3f>{};
std::vector<PathFinder::DynamicObstacle> dynamicObstacles; std::vector<PathFinder::DynamicObstacle> dynamicObstacles;
dynamicObstacles.reserve(npcs.size() + 1); dynamicObstacles.reserve(npcs.size() + 1);
@ -257,7 +330,7 @@ namespace ZL
} }
PathFinder::DynamicObstacle obs; PathFinder::DynamicObstacle obs;
obs.position = Eigen::Vector3f(other->position.x(), navigation.getFloorY(), other->position.z()); obs.position = Eigen::Vector3f(other->position.x(), navigation->getFloorY(), other->position.z());
obs.radius = (std::max)(0.0f, other->collisionRadius); obs.radius = (std::max)(0.0f, other->collisionRadius);
dynamicObstacles.push_back(obs); dynamicObstacles.push_back(obs);
}; };
@ -267,7 +340,7 @@ namespace ZL
addCharacter(npc.get()); addCharacter(npc.get());
} }
return navigation.findPath(start, end, dynamicObstacles); return navigation->findPath(start, end, dynamicObstacles);
}; };
}; };
@ -282,12 +355,35 @@ namespace ZL
} }
} }
bool Location::switchNavigation(int index)
{
if (index < 0 || index >= static_cast<int>(navigationMaps.size())) {
std::cerr << "[NAV] switchNavigation: index " << index << " out of range\n";
return false;
}
activeNavigationIndex = index;
navigation = &navigationMaps[index];
// Force all characters to replan their paths against the new nav map.
if (player) player->forceReplan();
for (auto& npc : npcs) {
if (npc) npc->forceReplan();
}
if (navigationEditorMode) {
navigationEditorBuildNavMeshes();
}
std::cout << "[NAV] Switched to navigation map " << index << "\n";
return true;
}
void Location::navigationEditorBuildNavMeshes() void Location::navigationEditorBuildNavMeshes()
{ {
navigationEditorNavMeshes.clear(); navigationEditorNavMeshes.clear();
const float y = navigation.getFloorY() + 0.02f; if (!navigation) return;
const float y = navigation->getFloorY() + 0.02f;
const Eigen::Vector3f red(1.0f, 0.0f, 0.0f); const Eigen::Vector3f red(1.0f, 0.0f, 0.0f);
for (const auto& obs : navigation.getObstaclePolygons()) { for (const auto& obs : navigation->getObstaclePolygons()) {
if (obs.polygon.size() < 3) continue; if (obs.polygon.size() < 3) continue;
VertexRenderStruct mesh; VertexRenderStruct mesh;
mesh.data = CreatePolygonFloor(obs.polygon, y, red); mesh.data = CreatePolygonFloor(obs.polygon, y, red);
@ -311,7 +407,7 @@ namespace ZL
{ {
VertexDataStruct data; VertexDataStruct data;
const Eigen::Vector3f yellow(1.0f, 1.0f, 0.0f); const Eigen::Vector3f yellow(1.0f, 1.0f, 0.0f);
const float y = navigation.getFloorY() + 0.05f; const float y = navigation ? navigation->getFloorY() + 0.05f : 0.05f;
const float s = 0.2f; const float s = 0.2f;
for (const auto& pt : navigationEditorPoints) { for (const auto& pt : navigationEditorPoints) {
@ -381,13 +477,13 @@ namespace ZL
poly.polygon.emplace_back(pt.x(), pt.z()); poly.polygon.emplace_back(pt.x(), pt.z());
} }
navigation.addObstaclePolygon(poly); if (navigation) navigation->addObstaclePolygon(poly);
std::cout << "[NAV_EDITOR] Added obstacle '" << poly.name << "' with " std::cout << "[NAV_EDITOR] Added obstacle '" << poly.name << "' with "
<< poly.polygon.size() << " vertices\n"; << poly.polygon.size() << " vertices\n";
// Add a red mesh for the new obstacle polygon so it's visible immediately. // Add a red mesh for the new obstacle polygon so it's visible immediately.
{ {
const float y = navigation.getFloorY() + 0.02f; const float y = navigation ? navigation->getFloorY() + 0.02f : 0.02f;
const Eigen::Vector3f red(1.0f, 0.0f, 0.0f); const Eigen::Vector3f red(1.0f, 0.0f, 0.0f);
VertexRenderStruct mesh; VertexRenderStruct mesh;
mesh.data = CreatePolygonFloor(poly.polygon, y, red); mesh.data = CreatePolygonFloor(poly.polygon, y, red);
@ -409,7 +505,7 @@ namespace ZL
if (!std::filesystem::exists(filename)) break; if (!std::filesystem::exists(filename)) break;
} }
if (navigation.saveConfig(filename)) { if (navigation && navigation->saveConfig(filename)) {
std::cout << "[NAV_EDITOR] Saved navigation config to: " << filename << "\n"; std::cout << "[NAV_EDITOR] Saved navigation config to: " << filename << "\n";
} else { } else {
std::cerr << "[NAV_EDITOR] Failed to save to: " << filename << "\n"; std::cerr << "[NAV_EDITOR] Failed to save to: " << filename << "\n";
@ -418,8 +514,8 @@ namespace ZL
void Location::navigationEditorReload() void Location::navigationEditorReload()
{ {
setupNavigation(navigationEditorConfigPath); setupNavigation(navigationMapPaths);
std::cout << "[NAV_EDITOR] Reloaded navigation from: " << navigationEditorConfigPath << "\n"; std::cout << "[NAV_EDITOR] Reloaded navigation maps (" << navigationMapPaths.size() << " maps)\n";
} }
@ -821,7 +917,8 @@ namespace ZL
bool Location::setNavigationAreaAvailable(const std::string& areaName, bool available) bool Location::setNavigationAreaAvailable(const std::string& areaName, bool available)
{ {
return navigation.setAreaAvailable(areaName, available); if (!navigation) return false;
return navigation->setAreaAvailable(areaName, available);
} }
void Location::resolveCharacterCollisions() void Location::resolveCharacterCollisions()
@ -882,9 +979,9 @@ namespace ZL
newB.z() += normal.y() * push; newB.z() += normal.y() * push;
newB.y() = 0.f; newB.y() = 0.f;
if (navigation.isReady()) { if (navigation && navigation->isReady()) {
const bool aOk = navigation.isWalkable(newA); const bool aOk = navigation->isWalkable(newA);
const bool bOk = navigation.isWalkable(newB); const bool bOk = navigation->isWalkable(newB);
if (aOk && bOk) { if (aOk && bOk) {
a->position = newA; a->position = newA;
@ -978,7 +1075,8 @@ namespace ZL
{ {
if (player) { if (player) {
player->update(delta); player->update(delta);
dialogueSystem.update(static_cast<int>(delta), player->position); dialogueSystem.update(static_cast<int>(delta));
updateTriggerZones(player->position);
} }
for (auto& npc : npcs) for (auto& npc : npcs)
{ {

View File

@ -19,6 +19,16 @@
namespace ZL namespace ZL
{ {
struct TriggerZone
{
std::string id;
Eigen::Vector3f position = Eigen::Vector3f::Zero();
float radius = 1.5f;
float hysteresis = 0.3f; // exit fires at radius + hysteresis to prevent flickering
bool enabled = true;
bool playerInside = false; // runtime tracking state, not serialized
};
struct LocationSetup struct LocationSetup
{ {
std::string roomMeshPath; std::string roomMeshPath;
@ -26,9 +36,10 @@ namespace ZL
std::string gameObjectsJsonPath; std::string gameObjectsJsonPath;
std::string npcsJsonPath; std::string npcsJsonPath;
std::string dialoguesJsonPath; std::string dialoguesJsonPath;
std::string navigationJsonPath; std::vector<std::string> navigationJsonPaths;
std::string scriptPath; std::string scriptPath;
std::string teleportsJsonPath; std::string teleportsJsonPath;
std::string triggerZonesJsonPath;
Eigen::Vector3f playerPosition = Eigen::Vector3f::Zero(); Eigen::Vector3f playerPosition = Eigen::Vector3f::Zero();
}; };
@ -52,7 +63,9 @@ namespace ZL
float cameraAzimuth = 0.0f; float cameraAzimuth = 0.0f;
float cameraInclination = M_PI * 30.f / 180.f; float cameraInclination = M_PI * 30.f / 180.f;
PathFinder navigation; std::vector<PathFinder> navigationMaps;
PathFinder* navigation = nullptr;
int activeNavigationIndex = 0;
std::unique_ptr<ShadowMap> shadowMap; std::unique_ptr<ShadowMap> shadowMap;
Eigen::Matrix4f cameraViewMatrix = Eigen::Matrix4f::Identity(); Eigen::Matrix4f cameraViewMatrix = Eigen::Matrix4f::Identity();
@ -73,13 +86,15 @@ namespace ZL
TeleportZone* targetTeleportZone = nullptr; TeleportZone* targetTeleportZone = nullptr;
std::function<void(const std::string&, const Eigen::Vector3f&, float)> onTeleport; std::function<void(const std::string&, const Eigen::Vector3f&, float)> onTeleport;
std::vector<TriggerZone> triggerZones;
// Navigation editor — toggle with 'N', save with 'B', right-click to finalize polygon // Navigation editor — toggle with 'N', save with 'B', right-click to finalize polygon
bool navigationEditorMode = false; bool navigationEditorMode = false;
std::vector<Eigen::Vector3f> navigationEditorPoints; std::vector<Eigen::Vector3f> navigationEditorPoints;
VertexRenderStruct navigationEditorPointsMesh; VertexRenderStruct navigationEditorPointsMesh;
std::vector<VertexRenderStruct> navigationEditorNavMeshes; std::vector<VertexRenderStruct> navigationEditorNavMeshes;
std::string navigationEditorConfigPath; std::vector<std::string> navigationMapPaths;
int navigationEditorObstacleCounter = 0; int navigationEditorObstacleCounter = 0;
void navigationEditorBuildNavMeshes(); void navigationEditorBuildNavMeshes();
@ -99,7 +114,8 @@ namespace ZL
void setup(const LocationSetup& params); void setup(const LocationSetup& params);
void setupNavigation(const std::string& navigationJsonPath); void setupNavigation(const std::vector<std::string>& paths);
bool switchNavigation(int index);
InteractiveObject* raycastInteractiveObjects(const Eigen::Vector3f& rayOrigin, const Eigen::Vector3f& rayDir); InteractiveObject* raycastInteractiveObjects(const Eigen::Vector3f& rayOrigin, const Eigen::Vector3f& rayDir);
Character* raycastNpcs(const Eigen::Vector3f& rayOrigin, const Eigen::Vector3f& rayDir, float maxDistance = 100.0f); Character* raycastNpcs(const Eigen::Vector3f& rayOrigin, const Eigen::Vector3f& rayDir, float maxDistance = 100.0f);
@ -127,6 +143,8 @@ namespace ZL
void resolveCharacterCollisions(); void resolveCharacterCollisions();
void updateDynamicReplans(int64_t deltaMs); void updateDynamicReplans(int64_t deltaMs);
void loadTeleportZones(const std::string& jsonPath, const char* zipFile); void loadTeleportZones(const std::string& jsonPath, const char* zipFile);
void loadTriggerZones(const std::string& jsonPath, const char* zipFile);
void updateTriggerZones(const Eigen::Vector3f& playerPos);
std::unordered_map<Character*, Eigen::Vector3f> lastCharacterPositions; std::unordered_map<Character*, Eigen::Vector3f> lastCharacterPositions;
std::unordered_map<Character*, int64_t> replanCooldownRemainingMs; std::unordered_map<Character*, int64_t> replanCooldownRemainingMs;

View File

@ -2,6 +2,7 @@
#include "Game.h" #include "Game.h"
#include <iostream> #include <iostream>
#include <stdexcept> #include <stdexcept>
#include <unordered_map>
#include "Location.h" #include "Location.h"
#define SOL_ALL_SAFETIES_ON 1 #define SOL_ALL_SAFETIES_ON 1
@ -11,6 +12,8 @@ namespace ZL {
struct ScriptEngine::Impl { struct ScriptEngine::Impl {
sol::state lua; sol::state lua;
std::unordered_map<std::string, sol::protected_function> triggerEnterCallbacks;
std::unordered_map<std::string, sol::protected_function> triggerExitCallbacks;
}; };
ScriptEngine::ScriptEngine() = default; ScriptEngine::ScriptEngine() = default;
@ -133,6 +136,24 @@ namespace ZL {
} }
}); });
api.set_function("switch_navigation",
[game](int index) {
if (!game->switchNavigation(index)) {
std::cerr << "[script] switch_navigation: index " << index << " out of range\n";
}
});
// set_trigger_zone_callbacks(zone_id, on_enter, on_exit)
// on_enter and on_exit are optional Lua functions (pass nil to omit).
// Called when the player enters or exits the named trigger zone.
api.set_function("set_trigger_zone_callbacks",
[this_impl = impl.get()](const std::string& zoneId, sol::object onEnter, sol::object onExit) {
if (onEnter.is<sol::protected_function>())
this_impl->triggerEnterCallbacks[zoneId] = onEnter.as<sol::protected_function>();
if (onExit.is<sol::protected_function>())
this_impl->triggerExitCallbacks[zoneId] = onExit.as<sol::protected_function>();
});
// receive_npc_gift(npc_index) // receive_npc_gift(npc_index)
api.set_function("receive_npc_gift", [game, inventory](int npcIndex) { api.set_function("receive_npc_gift", [game, inventory](int npcIndex) {
std::cout << "[script] receive_npc_gift: npc index " << npcIndex << std::endl; std::cout << "[script] receive_npc_gift: npc index " << npcIndex << std::endl;
@ -254,4 +275,26 @@ namespace ZL {
} }
} }
void ScriptEngine::callTriggerEnterCallback(const std::string& zoneId) {
if (!impl) return;
auto it = impl->triggerEnterCallbacks.find(zoneId);
if (it == impl->triggerEnterCallbacks.end()) return;
auto result = it->second();
if (!result.valid()) {
sol::error err = result;
std::cerr << "[SCRIPT] trigger enter callback error for '" << zoneId << "': " << err.what() << "\n";
}
}
void ScriptEngine::callTriggerExitCallback(const std::string& zoneId) {
if (!impl) return;
auto it = impl->triggerExitCallbacks.find(zoneId);
if (it == impl->triggerExitCallbacks.end()) return;
auto result = it->second();
if (!result.valid()) {
sol::error err = result;
std::cerr << "[SCRIPT] trigger exit callback error for '" << zoneId << "': " << err.what() << "\n";
}
}
} // namespace ZL } // namespace ZL

View File

@ -24,6 +24,9 @@ public:
void callNpcInteractCallback(int npcIndex); void callNpcInteractCallback(int npcIndex);
void callTriggerEnterCallback(const std::string& zoneId);
void callTriggerExitCallback(const std::string& zoneId);
private: private:
struct Impl; struct Impl;
std::unique_ptr<Impl> impl; std::unique_ptr<Impl> impl;

View File

@ -11,23 +11,7 @@ bool DialogueSystem::loadDatabase(const std::string& path) {
return database.loadFromFile(path); return database.loadFromFile(path);
} }
void DialogueSystem::update(int deltaMs, const Eigen::Vector3f& playerPosition) { void DialogueSystem::update(int deltaMs) {
if (!runtime.isActive()) {
for (TriggerZone& zone : triggerZones) {
if (zone.once && zone.triggered) {
continue;
}
const Eigen::Vector3f diff = playerPosition - zone.center;
if (diff.norm() <= zone.radius) {
if (startDialogue(zone.dialogueId)) {
zone.triggered = true;
break;
}
}
}
}
runtime.update(deltaMs); runtime.update(deltaMs);
overlay.update(runtime.getPresentation(), deltaMs); overlay.update(runtime.getPresentation(), deltaMs);
if (overlay.consumeSkipRequested()) { if (overlay.consumeSkipRequested()) {
@ -139,12 +123,4 @@ void DialogueSystem::stopDialogue() {
runtime.stop(); runtime.stop();
} }
void DialogueSystem::addTriggerZone(const TriggerZone& zone) {
triggerZones.push_back(zone);
}
void DialogueSystem::clearTriggerZones() {
triggerZones.clear();
}
} // namespace ZL::Dialogue } // namespace ZL::Dialogue

View File

@ -2,28 +2,17 @@
#include "dialogue/DialogueOverlay.h" #include "dialogue/DialogueOverlay.h"
#include "dialogue/DialogueRuntime.h" #include "dialogue/DialogueRuntime.h"
#include <Eigen/Dense>
#include <SDL.h> #include <SDL.h>
#include <string> #include <string>
#include <vector>
namespace ZL::Dialogue { namespace ZL::Dialogue {
struct TriggerZone {
std::string id;
std::string dialogueId;
Eigen::Vector3f center = Eigen::Vector3f::Zero();
float radius = 1.5f;
bool once = true;
bool triggered = false;
};
class DialogueSystem { class DialogueSystem {
public: public:
bool init(Renderer& renderer, const std::string& zipFile = ""); bool init(Renderer& renderer, const std::string& zipFile = "");
bool loadDatabase(const std::string& path); bool loadDatabase(const std::string& path);
void update(int deltaMs, const Eigen::Vector3f& playerPosition); void update(int deltaMs);
void draw(Renderer& renderer); void draw(Renderer& renderer);
bool handleKeyDown(SDL_Keycode key); bool handleKeyDown(SDL_Keycode key);
@ -40,14 +29,10 @@ public:
void setFlag(const std::string& name, int value) { runtime.setFlag(name, value); } void setFlag(const std::string& name, int value) { runtime.setFlag(name, value); }
int getFlag(const std::string& name) const { return runtime.getFlag(name); } int getFlag(const std::string& name) const { return runtime.getFlag(name); }
void addTriggerZone(const TriggerZone& zone);
void clearTriggerZones();
private: private:
DialogueDatabase database; DialogueDatabase database;
DialogueRuntime runtime; DialogueRuntime runtime;
DialogueOverlay overlay; DialogueOverlay overlay;
std::vector<TriggerZone> triggerZones;
}; };
} // namespace ZL::Dialogue } // namespace ZL::Dialogue