Major refactoring for game objects
This commit is contained in:
parent
efb85f7328
commit
2cae9998a0
@ -10,8 +10,7 @@
|
|||||||
"positionX": 0.0,
|
"positionX": 0.0,
|
||||||
"positionY": -0.1,
|
"positionY": -0.1,
|
||||||
"positionZ": 0.0,
|
"positionZ": 0.0,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Building",
|
"name": "Building",
|
||||||
@ -23,8 +22,7 @@
|
|||||||
"positionX": 0.0,
|
"positionX": 0.0,
|
||||||
"positionY": -0.1,
|
"positionY": -0.1,
|
||||||
"positionZ": 0.0,
|
"positionZ": 0.0,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Stairs",
|
"name": "Stairs",
|
||||||
@ -36,8 +34,7 @@
|
|||||||
"positionX": 0.0,
|
"positionX": 0.0,
|
||||||
"positionY": -0.1,
|
"positionY": -0.1,
|
||||||
"positionZ": 0.0,
|
"positionZ": 0.0,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Door",
|
"name": "Door",
|
||||||
@ -49,8 +46,7 @@
|
|||||||
"positionX": 0.0,
|
"positionX": 0.0,
|
||||||
"positionY": -0.1,
|
"positionY": -0.1,
|
||||||
"positionZ": 0.0,
|
"positionZ": 0.0,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Bed001",
|
"name": "Bed001",
|
||||||
@ -62,8 +58,7 @@
|
|||||||
"positionX": 0.0,
|
"positionX": 0.0,
|
||||||
"positionY": -0.1,
|
"positionY": -0.1,
|
||||||
"positionZ": 0.0,
|
"positionZ": 0.0,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Chair001",
|
"name": "Chair001",
|
||||||
@ -75,8 +70,7 @@
|
|||||||
"positionX": 0.0,
|
"positionX": 0.0,
|
||||||
"positionY": -0.1,
|
"positionY": -0.1,
|
||||||
"positionZ": 0.0,
|
"positionZ": 0.0,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Table001",
|
"name": "Table001",
|
||||||
@ -88,50 +82,7 @@
|
|||||||
"positionX": 0.0,
|
"positionX": 0.0,
|
||||||
"positionY": -0.1,
|
"positionY": -0.1,
|
||||||
"positionZ": 0.0,
|
"positionZ": 0.0,
|
||||||
"scale": 1.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"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -10,8 +10,7 @@
|
|||||||
"positionX": 0.0,
|
"positionX": 0.0,
|
||||||
"positionY": -5.0,
|
"positionY": -5.0,
|
||||||
"positionZ": 0.0,
|
"positionZ": 0.0,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "door",
|
"name": "door",
|
||||||
@ -23,8 +22,7 @@
|
|||||||
"positionX": 0.0,
|
"positionX": 0.0,
|
||||||
"positionY": -5.0,
|
"positionY": -5.0,
|
||||||
"positionZ": 0.0,
|
"positionZ": 0.0,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "inai",
|
"name": "inai",
|
||||||
@ -36,8 +34,7 @@
|
|||||||
"positionX": 0.0,
|
"positionX": 0.0,
|
||||||
"positionY": -5.0,
|
"positionY": -5.0,
|
||||||
"positionZ": 0.0,
|
"positionZ": 0.0,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Table001",
|
"name": "Table001",
|
||||||
@ -49,8 +46,7 @@
|
|||||||
"positionX": 0.0,
|
"positionX": 0.0,
|
||||||
"positionY": -5.0,
|
"positionY": -5.0,
|
||||||
"positionZ": 0.0,
|
"positionZ": 0.0,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Chair001",
|
"name": "Chair001",
|
||||||
@ -62,43 +58,7 @@
|
|||||||
"positionX": 0.0,
|
"positionX": 0.0,
|
||||||
"positionY": -5.0,
|
"positionY": -5.0,
|
||||||
"positionZ": 0.0,
|
"positionZ": 0.0,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Bookshelf001",
|
|
||||||
"texturePath": "resources/w/interior/ikea_bookshelf001_tex001.png",
|
|
||||||
"meshPath": "resources/w/interior/ikea_bookshelf001.txt",
|
|
||||||
"rotationX": 0.0,
|
|
||||||
"rotationY": 0.0,
|
|
||||||
"rotationZ": 0.0,
|
|
||||||
"positionX": 1.9653,
|
|
||||||
"positionY": 1.0911,
|
|
||||||
"positionZ": 0.91977,
|
|
||||||
"scale": 1.0,
|
|
||||||
"interactive": true,
|
|
||||||
"item": {
|
|
||||||
"id": "phone",
|
|
||||||
"name": "Телефон",
|
|
||||||
"description": "Я не могу себе представить жизнь без своего телефона",
|
|
||||||
"icon": "resources/fire2.png",
|
|
||||||
"radius": 0.3
|
|
||||||
},
|
|
||||||
"activateFunction" : "on_bookshelf_clicked"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Computer001",
|
|
||||||
"texturePath": "resources/w/interior/computer_texture001.png",
|
|
||||||
"meshPath": "resources/w/interior/computer001.txt",
|
|
||||||
"rotationX": 0.0,
|
|
||||||
"rotationY": 0.0,
|
|
||||||
"rotationZ": 0.0,
|
|
||||||
"positionX": 5.0916,
|
|
||||||
"positionY": 1.1534,
|
|
||||||
"positionZ": 1.0568,
|
|
||||||
"scale": 1.0,
|
|
||||||
"interactive": true,
|
|
||||||
"activateFunction" : "on_computer_clicked"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "DiningTable001",
|
"name": "DiningTable001",
|
||||||
@ -110,50 +70,7 @@
|
|||||||
"positionX": 0.0,
|
"positionX": 0.0,
|
||||||
"positionY": -5.0,
|
"positionY": -5.0,
|
||||||
"positionZ": 0.0,
|
"positionZ": 0.0,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Knife001",
|
|
||||||
"texturePath": "resources/w/white.png",
|
|
||||||
"meshPath": "resources/w/interior/Knife.txt",
|
|
||||||
"rotationX": 0.0,
|
|
||||||
"rotationY": 0.0,
|
|
||||||
"rotationZ": 0.0,
|
|
||||||
"positionX": -2.26293,
|
|
||||||
"positionY": 0.91414,
|
|
||||||
"positionZ": 1.56758,
|
|
||||||
"scale": 1.0,
|
|
||||||
"interactive": true,
|
|
||||||
"item": {
|
|
||||||
"id": "knife",
|
|
||||||
"name": "Серебряный нож",
|
|
||||||
"description": "Этот серебряный нож я одолжил у Айпери, и я должен его вернуть.",
|
|
||||||
"icon": "resources/fire2.png",
|
|
||||||
"radius": 0.3
|
|
||||||
},
|
|
||||||
"activateFunction" : "on_knife_pickup"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Book001",
|
|
||||||
"texturePath": "resources/w/interior/book_tex002.png",
|
|
||||||
"meshPath": "resources/w/interior/book001.txt",
|
|
||||||
"rotationX": 0.0,
|
|
||||||
"rotationY": 0.0,
|
|
||||||
"rotationZ": 0.0,
|
|
||||||
"positionX": 1.97952,
|
|
||||||
"positionY": 0.95746,
|
|
||||||
"positionZ": 0.786023,
|
|
||||||
"scale": 1.0,
|
|
||||||
"interactive": true,
|
|
||||||
"item": {
|
|
||||||
"id": "phone",
|
|
||||||
"name": "Книга",
|
|
||||||
"description": "Это книга о манасчи Жусупе Мамае.",
|
|
||||||
"icon": "resources/fire2.png",
|
|
||||||
"radius": 0.3
|
|
||||||
},
|
|
||||||
"activateFunction" : "on_book_pickup"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "stairs",
|
"name": "stairs",
|
||||||
@ -165,8 +82,7 @@
|
|||||||
"positionX": 0.0,
|
"positionX": 0.0,
|
||||||
"positionY": -5.0,
|
"positionY": -5.0,
|
||||||
"positionZ": 0.0,
|
"positionZ": 0.0,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "tree001",
|
"name": "tree001",
|
||||||
@ -178,8 +94,7 @@
|
|||||||
"positionX": 10.0,
|
"positionX": 10.0,
|
||||||
"positionY": -5.0,
|
"positionY": -5.0,
|
||||||
"positionZ": 12.0,
|
"positionZ": 12.0,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "tree002",
|
"name": "tree002",
|
||||||
@ -191,8 +106,7 @@
|
|||||||
"positionX": -12,
|
"positionX": -12,
|
||||||
"positionY": -5.0,
|
"positionY": -5.0,
|
||||||
"positionZ": 19.0,
|
"positionZ": 19.0,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "tree003",
|
"name": "tree003",
|
||||||
@ -204,8 +118,7 @@
|
|||||||
"positionX": -12.0,
|
"positionX": -12.0,
|
||||||
"positionY": -5.0,
|
"positionY": -5.0,
|
||||||
"positionZ": 8.0,
|
"positionZ": 8.0,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "tree004",
|
"name": "tree004",
|
||||||
@ -217,8 +130,7 @@
|
|||||||
"positionX": -12.0,
|
"positionX": -12.0,
|
||||||
"positionY": -5.0,
|
"positionY": -5.0,
|
||||||
"positionZ": 0.0,
|
"positionZ": 0.0,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "tree005",
|
"name": "tree005",
|
||||||
@ -230,8 +142,7 @@
|
|||||||
"positionX": -12.0,
|
"positionX": -12.0,
|
||||||
"positionY": -5.0,
|
"positionY": -5.0,
|
||||||
"positionZ": -8.0,
|
"positionZ": -8.0,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "tree006",
|
"name": "tree006",
|
||||||
@ -243,8 +154,7 @@
|
|||||||
"positionX": 8.49915,
|
"positionX": 8.49915,
|
||||||
"positionY": -5.0,
|
"positionY": -5.0,
|
||||||
"positionZ": -2.59884,
|
"positionZ": -2.59884,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "tree007",
|
"name": "tree007",
|
||||||
@ -256,8 +166,7 @@
|
|||||||
"positionX": 14.5936,
|
"positionX": 14.5936,
|
||||||
"positionY": -5.0,
|
"positionY": -5.0,
|
||||||
"positionZ": 5.3401,
|
"positionZ": 5.3401,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "tree008",
|
"name": "tree008",
|
||||||
@ -269,8 +178,7 @@
|
|||||||
"positionX": 23.9295,
|
"positionX": 23.9295,
|
||||||
"positionY": -5.0,
|
"positionY": -5.0,
|
||||||
"positionZ": 9.00583,
|
"positionZ": 9.00583,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "tree009",
|
"name": "tree009",
|
||||||
@ -282,8 +190,7 @@
|
|||||||
"positionX": 29.8128,
|
"positionX": 29.8128,
|
||||||
"positionY": -5.0,
|
"positionY": -5.0,
|
||||||
"positionZ": -1.45278,
|
"positionZ": -1.45278,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "tree010",
|
"name": "tree010",
|
||||||
@ -295,8 +202,7 @@
|
|||||||
"positionX": 33.1771,
|
"positionX": 33.1771,
|
||||||
"positionY": -5.0,
|
"positionY": -5.0,
|
||||||
"positionZ": 14.609,
|
"positionZ": 14.609,
|
||||||
"scale": 1.0,
|
"scale": 1.0
|
||||||
"interactive": false
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
32
resources/config2/interactive_objects_dorm.json
Normal file
32
resources/config2/interactive_objects_dorm.json
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"objects": [
|
||||||
|
{
|
||||||
|
"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,
|
||||||
|
"interactionRadius": 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,
|
||||||
|
"interactionRadius": 0.3,
|
||||||
|
"activateFunction": "on_journal_pickup"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
59
resources/config2/interactive_objects_uni_interior.json
Normal file
59
resources/config2/interactive_objects_uni_interior.json
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"objects": [
|
||||||
|
{
|
||||||
|
"name": "Bookshelf001",
|
||||||
|
"texturePath": "resources/w/interior/ikea_bookshelf001_tex001.png",
|
||||||
|
"meshPath": "resources/w/interior/ikea_bookshelf001.txt",
|
||||||
|
"rotationX": 0.0,
|
||||||
|
"rotationY": 0.0,
|
||||||
|
"rotationZ": 0.0,
|
||||||
|
"positionX": 1.9653,
|
||||||
|
"positionY": 1.0911,
|
||||||
|
"positionZ": 0.91977,
|
||||||
|
"scale": 1.0,
|
||||||
|
"interactionRadius": 1.5,
|
||||||
|
"activateFunction": "on_bookshelf_clicked"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Computer001",
|
||||||
|
"texturePath": "resources/w/interior/computer_texture001.png",
|
||||||
|
"meshPath": "resources/w/interior/computer001.txt",
|
||||||
|
"rotationX": 0.0,
|
||||||
|
"rotationY": 0.0,
|
||||||
|
"rotationZ": 0.0,
|
||||||
|
"positionX": 5.0916,
|
||||||
|
"positionY": 1.1534,
|
||||||
|
"positionZ": 1.0568,
|
||||||
|
"scale": 1.0,
|
||||||
|
"activateFunction": "on_computer_clicked"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Knife001",
|
||||||
|
"texturePath": "resources/w/white.png",
|
||||||
|
"meshPath": "resources/w/interior/Knife.txt",
|
||||||
|
"rotationX": 0.0,
|
||||||
|
"rotationY": 0.0,
|
||||||
|
"rotationZ": 0.0,
|
||||||
|
"positionX": -2.26293,
|
||||||
|
"positionY": 0.91414,
|
||||||
|
"positionZ": 1.56758,
|
||||||
|
"scale": 1.0,
|
||||||
|
"interactionRadius": 0.3,
|
||||||
|
"activateFunction": "on_knife_pickup"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Book001",
|
||||||
|
"texturePath": "resources/w/interior/book_tex002.png",
|
||||||
|
"meshPath": "resources/w/interior/book001.txt",
|
||||||
|
"rotationX": 0.0,
|
||||||
|
"rotationY": 0.0,
|
||||||
|
"rotationZ": 0.0,
|
||||||
|
"positionX": 1.97952,
|
||||||
|
"positionY": 0.95746,
|
||||||
|
"positionZ": 0.786023,
|
||||||
|
"scale": 1.0,
|
||||||
|
"interactionRadius": 1.5,
|
||||||
|
"activateFunction": "on_book_pickup"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
28
resources/config2/items.json
Normal file
28
resources/config2/items.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": "phone",
|
||||||
|
"name": "Телефон",
|
||||||
|
"description": "Я не могу себе представить жизнь без своего телефона",
|
||||||
|
"icon": "resources/fire2.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "journal",
|
||||||
|
"name": "Журнал",
|
||||||
|
"description": "Это мой журнал куда я вношу свои заметки.",
|
||||||
|
"icon": "resources/fire2.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "knife",
|
||||||
|
"name": "Серебряный нож",
|
||||||
|
"description": "Этот серебряный нож я одолжил у Айпери, и я должен его вернуть.",
|
||||||
|
"icon": "resources/fire2.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "book",
|
||||||
|
"name": "Книга",
|
||||||
|
"description": "Это книга о манасчи Жусупе Мамае.",
|
||||||
|
"icon": "resources/fire2.png"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -20,12 +20,14 @@ journal_picked_up = false
|
|||||||
|
|
||||||
function on_phone_pickup()
|
function on_phone_pickup()
|
||||||
game_api.pickup_item("phone")
|
game_api.pickup_item("phone")
|
||||||
|
game_api.deactivate_interactive_object("Phone001")
|
||||||
game_api.start_dialogue("dialog_phone_pickup001")
|
game_api.start_dialogue("dialog_phone_pickup001")
|
||||||
phone_picked_up = true
|
phone_picked_up = true
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_journal_pickup()
|
function on_journal_pickup()
|
||||||
game_api.pickup_item("journal")
|
game_api.pickup_item("journal")
|
||||||
|
game_api.deactivate_interactive_object("Journal001")
|
||||||
game_api.start_dialogue("dialog_journal_pickup001")
|
game_api.start_dialogue("dialog_journal_pickup001")
|
||||||
journal_picked_up = true
|
journal_picked_up = true
|
||||||
end
|
end
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
lection_is_over = false
|
lection_is_over = false
|
||||||
|
|
||||||
|
player_hold_book = false
|
||||||
|
|
||||||
function lection_hall_zone001_enter_callback()
|
function lection_hall_zone001_enter_callback()
|
||||||
--game_api.start_dialogue("")
|
--game_api.start_dialogue("")
|
||||||
@ -37,12 +38,34 @@ game_api.set_trigger_zone_callbacks("knife_dialog_zone001",
|
|||||||
|
|
||||||
function on_knife_pickup()
|
function on_knife_pickup()
|
||||||
game_api.pickup_item("knife")
|
game_api.pickup_item("knife")
|
||||||
|
game_api.deactivate_interactive_object("Knife001")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function on_book_pickup()
|
function on_book_pickup()
|
||||||
|
if not player_hold_book then
|
||||||
game_api.pickup_item("book")
|
game_api.pickup_item("book")
|
||||||
|
game_api.deactivate_interactive_object("Book001")
|
||||||
|
player_hold_book = true
|
||||||
|
else
|
||||||
|
game_api.remove_item("book")
|
||||||
|
game_api.activate_interactive_object("Book001")
|
||||||
|
player_hold_book = false
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_bookshelf_clicked()
|
||||||
|
if not player_hold_book then
|
||||||
|
game_api.pickup_item("book")
|
||||||
|
game_api.deactivate_interactive_object("Book001")
|
||||||
|
player_hold_book = true
|
||||||
|
else
|
||||||
|
game_api.remove_item("book")
|
||||||
|
game_api.activate_interactive_object("Book001")
|
||||||
|
player_hold_book = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function on_npc_interact(npc_index)
|
function on_npc_interact(npc_index)
|
||||||
print("[Lua] NPC interaction! Index: " .. tostring(npc_index))
|
print("[Lua] NPC interaction! Index: " .. tostring(npc_index))
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
#include "BoneAnimatedModelNew.h"
|
#include "BoneAnimatedModelNew.h"
|
||||||
#include "render/Renderer.h"
|
#include "render/Renderer.h"
|
||||||
#include "render/TextureManager.h"
|
#include "render/TextureManager.h"
|
||||||
#include "items/Item.h"
|
|
||||||
#include "SparkEmitter.h"
|
#include "SparkEmitter.h"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -106,11 +105,8 @@ public:
|
|||||||
// Applied after scale, fixes model-space orientation (e.g. Blender Z-up exports)
|
// Applied after scale, fixes model-space orientation (e.g. Blender Z-up exports)
|
||||||
Eigen::Quaternionf modelCorrectionRotation = Eigen::Quaternionf::Identity();
|
Eigen::Quaternionf modelCorrectionRotation = Eigen::Quaternionf::Identity();
|
||||||
|
|
||||||
// NPC Gift
|
|
||||||
Item giftItem;
|
|
||||||
std::string npcId;
|
std::string npcId;
|
||||||
std::string npcName;
|
std::string npcName;
|
||||||
bool giftReceived = false;
|
|
||||||
float hp = 200.f;
|
float hp = 200.f;
|
||||||
// Captured lazily from `hp` on the first update() tick if left at zero;
|
// Captured lazily from `hp` on the first update() tick if left at zero;
|
||||||
// set explicitly if you need a different reference for the health bar.
|
// set explicitly if you need a different reference for the health bar.
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "AnimatedModel.h"
|
#include "AnimatedModel.h"
|
||||||
#include "utils/Utils.h"
|
#include "utils/Utils.h"
|
||||||
|
#include "items/ItemRegistry.h"
|
||||||
#include "render/OpenGlExtensions.h"
|
#include "render/OpenGlExtensions.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "render/TextureManager.h"
|
#include "render/TextureManager.h"
|
||||||
@ -158,6 +159,8 @@ namespace ZL
|
|||||||
|
|
||||||
std::cout << "Load resurces step 4" << std::endl;
|
std::cout << "Load resurces step 4" << std::endl;
|
||||||
|
|
||||||
|
ItemRegistry::instance().loadFromJson("resources/config2/items.json", CONST_ZIP_FILE);
|
||||||
|
|
||||||
LocationSetup uniInteriorParams;
|
LocationSetup uniInteriorParams;
|
||||||
uniInteriorParams.gameObjectsJsonPath = "resources/config2/gameobjects_uni_interior.json";
|
uniInteriorParams.gameObjectsJsonPath = "resources/config2/gameobjects_uni_interior.json";
|
||||||
uniInteriorParams.npcsJsonPath = "resources/config2/npcs_uni_interior.json";
|
uniInteriorParams.npcsJsonPath = "resources/config2/npcs_uni_interior.json";
|
||||||
@ -167,6 +170,7 @@ namespace ZL
|
|||||||
uniInteriorParams.teleportsJsonPath = "resources/config2/teleports.json";
|
uniInteriorParams.teleportsJsonPath = "resources/config2/teleports.json";
|
||||||
uniInteriorParams.triggerZonesJsonPath = "resources/config2/trigger_zones_uni_interior.json";
|
uniInteriorParams.triggerZonesJsonPath = "resources/config2/trigger_zones_uni_interior.json";
|
||||||
uniInteriorParams.scriptPath = "resources/start_uni_interior.lua";
|
uniInteriorParams.scriptPath = "resources/start_uni_interior.lua";
|
||||||
|
uniInteriorParams.interactiveObjectsJsonPath = "resources/config2/interactive_objects_uni_interior.json";
|
||||||
uniInteriorParams.playerPosition = Eigen::Vector3f(0.942694, 0, -9.63104);
|
uniInteriorParams.playerPosition = Eigen::Vector3f(0.942694, 0, -9.63104);
|
||||||
|
|
||||||
locations["uni_interior"] = std::make_shared<Location>(renderer, inventory);
|
locations["uni_interior"] = std::make_shared<Location>(renderer, inventory);
|
||||||
@ -174,6 +178,7 @@ namespace ZL
|
|||||||
|
|
||||||
LocationSetup uniExteriorParams = uniInteriorParams;
|
LocationSetup uniExteriorParams = uniInteriorParams;
|
||||||
uniExteriorParams.gameObjectsJsonPath = "resources/config2/gameobjects2.json";
|
uniExteriorParams.gameObjectsJsonPath = "resources/config2/gameobjects2.json";
|
||||||
|
uniExteriorParams.interactiveObjectsJsonPath = "";
|
||||||
//uniExteriorParams.navigationJsonPaths = {"resources/config2/navigation2.json"};
|
//uniExteriorParams.navigationJsonPaths = {"resources/config2/navigation2.json"};
|
||||||
uniExteriorParams.navigationJsonPaths = { "resources/w/nav_uni_exterior.txt" };
|
uniExteriorParams.navigationJsonPaths = { "resources/w/nav_uni_exterior.txt" };
|
||||||
uniExteriorParams.teleportsJsonPath = "resources/config2/teleports2.json";
|
uniExteriorParams.teleportsJsonPath = "resources/config2/teleports2.json";
|
||||||
@ -194,6 +199,7 @@ namespace ZL
|
|||||||
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.triggerZonesJsonPath = "resources/config2/trigger_zones_dorm.json";
|
||||||
params_dorm.scriptPath = "resources/start_dorm.lua";
|
params_dorm.scriptPath = "resources/start_dorm.lua";
|
||||||
|
params_dorm.interactiveObjectsJsonPath = "resources/config2/interactive_objects_dorm.json";
|
||||||
params_dorm.playerPosition = Eigen::Vector3f(6.76345, 0, -14.6022);
|
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);
|
||||||
|
|||||||
@ -57,7 +57,7 @@ namespace ZL
|
|||||||
gameObjects = GameObjectLoader::loadAndCreateGameObjects(params.gameObjectsJsonPath, renderer, CONST_ZIP_FILE);
|
gameObjects = GameObjectLoader::loadAndCreateGameObjects(params.gameObjectsJsonPath, renderer, CONST_ZIP_FILE);
|
||||||
|
|
||||||
// Load interactive objects
|
// Load interactive objects
|
||||||
interactiveObjects = GameObjectLoader::loadAndCreateInteractiveObjects(params.gameObjectsJsonPath, renderer, CONST_ZIP_FILE);
|
interactiveObjects = GameObjectLoader::loadAndCreateInteractiveObjects(params.interactiveObjectsJsonPath, renderer, CONST_ZIP_FILE);
|
||||||
|
|
||||||
//auto playerTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/w/gg/IMG_20260413_182354_992.png", CONST_ZIP_FILE));
|
//auto playerTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/w/gg/IMG_20260413_182354_992.png", CONST_ZIP_FILE));
|
||||||
auto playerTexture = renderer.textureManager.LoadFromPng("resources/w/gg/UniV_Grid_2K_Base_color.png", CONST_ZIP_FILE);
|
auto playerTexture = renderer.textureManager.LoadFromPng("resources/w/gg/UniV_Grid_2K_Base_color.png", CONST_ZIP_FILE);
|
||||||
@ -513,7 +513,7 @@ namespace ZL
|
|||||||
InteractiveObject* closestObject = nullptr;
|
InteractiveObject* closestObject = nullptr;
|
||||||
|
|
||||||
for (auto& intObj : interactiveObjects) {
|
for (auto& intObj : interactiveObjects) {
|
||||||
std::cout << "[RAYCAST] Checking object: " << intObj.name << " (active: " << intObj.isActive << ")" << std::endl;
|
std::cout << "[RAYCAST] Checking object: " << intObj.loadedObject.name << " (active: " << intObj.isActive << ")" << std::endl;
|
||||||
|
|
||||||
if (!intObj.isActive) {
|
if (!intObj.isActive) {
|
||||||
std::cout << "[RAYCAST] -> Object inactive, skipping" << std::endl;
|
std::cout << "[RAYCAST] -> Object inactive, skipping" << std::endl;
|
||||||
@ -548,7 +548,7 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (closestObject) {
|
if (closestObject) {
|
||||||
std::cout << "[RAYCAST] *** RAYCAST SUCCESS: Found object " << closestObject->name << " ***" << std::endl;
|
std::cout << "[RAYCAST] *** RAYCAST SUCCESS: Found object " << closestObject->loadedObject.name << " ***" << std::endl;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
std::cout << "[RAYCAST] No objects hit" << std::endl;
|
std::cout << "[RAYCAST] No objects hit" << std::endl;
|
||||||
@ -749,10 +749,10 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (auto& intObj : interactiveObjects) {
|
for (auto& intObj : interactiveObjects) {
|
||||||
if (intObj.isActive && intObj.texture) {
|
if (intObj.isActive && intObj.loadedObject.texture) {
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.TranslateMatrix(intObj.position);
|
renderer.TranslateMatrix(intObj.position);
|
||||||
renderer.DrawVertexRenderStruct(intObj.mesh);
|
renderer.DrawVertexRenderStruct(intObj.loadedObject.mesh);
|
||||||
renderer.PopMatrix();
|
renderer.PopMatrix();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1074,7 +1074,7 @@ namespace ZL
|
|||||||
// If player is close enough to pick up the item
|
// If player is close enough to pick up the item
|
||||||
if (distToObject <= targetInteractiveObject->interactionRadius + 1.0f) {
|
if (distToObject <= targetInteractiveObject->interactionRadius + 1.0f) {
|
||||||
std::cout << "[PICKUP] Player reached object! Distance: " << distToObject << std::endl;
|
std::cout << "[PICKUP] Player reached object! Distance: " << distToObject << std::endl;
|
||||||
std::cout << "[PICKUP] Calling Lua callback for: " << targetInteractiveObject->id << std::endl;
|
std::cout << "[PICKUP] Calling Lua callback for: " << targetInteractiveObject->loadedObject.name << std::endl;
|
||||||
|
|
||||||
// Call custom activate function if specified, otherwise use fallback
|
// Call custom activate function if specified, otherwise use fallback
|
||||||
try {
|
try {
|
||||||
@ -1084,7 +1084,7 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
std::cout << "[PICKUP] Using fallback callback" << std::endl;
|
std::cout << "[PICKUP] Using fallback callback" << std::endl;
|
||||||
scriptEngine.callItemPickupCallback(targetInteractiveObject->id);
|
scriptEngine.callItemPickupCallback(targetInteractiveObject->loadedObject.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
@ -1171,7 +1171,7 @@ namespace ZL
|
|||||||
// First check if we clicked on interactive object
|
// First check if we clicked on interactive object
|
||||||
InteractiveObject* clickedObject = raycastInteractiveObjects(camPos, rayDir);
|
InteractiveObject* clickedObject = raycastInteractiveObjects(camPos, rayDir);
|
||||||
if (clickedObject && player && clickedObject->isActive) {
|
if (clickedObject && player && clickedObject->isActive) {
|
||||||
std::cout << "[CLICK] *** SUCCESS: Clicked on interactive object: " << clickedObject->name << " ***" << std::endl;
|
std::cout << "[CLICK] *** SUCCESS: Clicked on interactive object: " << clickedObject->loadedObject.name << " ***" << std::endl;
|
||||||
std::cout << "[CLICK] Object position: (" << clickedObject->position.x() << ", "
|
std::cout << "[CLICK] Object position: (" << clickedObject->position.x() << ", "
|
||||||
<< clickedObject->position.y() << ", " << clickedObject->position.z() << ")" << std::endl;
|
<< clickedObject->position.y() << ", " << clickedObject->position.z() << ")" << std::endl;
|
||||||
std::cout << "[CLICK] Player position: (" << player->position.x() << ", "
|
std::cout << "[CLICK] Player position: (" << player->position.x() << ", "
|
||||||
|
|||||||
@ -32,6 +32,7 @@ namespace ZL
|
|||||||
struct LocationSetup
|
struct LocationSetup
|
||||||
{
|
{
|
||||||
std::string gameObjectsJsonPath;
|
std::string gameObjectsJsonPath;
|
||||||
|
std::string interactiveObjectsJsonPath;
|
||||||
std::string npcsJsonPath;
|
std::string npcsJsonPath;
|
||||||
std::string dialoguesJsonPath;
|
std::string dialoguesJsonPath;
|
||||||
std::vector<std::string> navigationJsonPaths;
|
std::vector<std::string> navigationJsonPaths;
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include "Location.h"
|
#include "Location.h"
|
||||||
|
#include "items/ItemRegistry.h"
|
||||||
|
|
||||||
#define SOL_ALL_SAFETIES_ON 1
|
#define SOL_ALL_SAFETIES_ON 1
|
||||||
#include <sol/sol.hpp>
|
#include <sol/sol.hpp>
|
||||||
@ -53,34 +54,15 @@ namespace ZL {
|
|||||||
npcs[index]->setTarget(Eigen::Vector3f(x, y, z), std::move(cb));
|
npcs[index]->setTarget(Eigen::Vector3f(x, y, z), std::move(cb));
|
||||||
});
|
});
|
||||||
|
|
||||||
// pickup_item(object_name)
|
// pickup_item(item_id)
|
||||||
api.set_function("pickup_item", [game, inventory](const std::string& objectName) {
|
api.set_function("pickup_item", [inventory](const std::string& itemId) {
|
||||||
|
const Item* item = ItemRegistry::instance().findById(itemId);
|
||||||
|
if (item) {
|
||||||
std::cout << "[script] pickup_item: " << objectName << std::endl;
|
inventory->addItem(*item);
|
||||||
|
std::cout << "[script] pickup_item: " << item->name << std::endl;
|
||||||
for (auto& intObj : game->interactiveObjects) {
|
} else {
|
||||||
if (intObj.id == objectName && intObj.isActive) {
|
std::cerr << "[script] pickup_item: item '" << itemId << "' not found in ItemRegistry\n";
|
||||||
// Add item to inventory
|
|
||||||
inventory->addItem(intObj.dropItem);
|
|
||||||
|
|
||||||
// Deactivate object
|
|
||||||
intObj.isActive = false;
|
|
||||||
|
|
||||||
std::cout << "[script] Item picked up successfully: " << intObj.dropItem.name << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
std::cerr << "[script] Warning: Interactive object not found or already picked up: " << objectName << std::endl;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// add_item(item_id, name, description, icon)
|
|
||||||
api.set_function("add_item", [game, inventory](const std::string& id, const std::string& name,
|
|
||||||
const std::string& description, const std::string& icon) {
|
|
||||||
std::cout << "[script] add_item: " << name << std::endl;
|
|
||||||
Item newItem(id, name, description, icon);
|
|
||||||
inventory->addItem(newItem);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// remove_item(item_id)
|
// remove_item(item_id)
|
||||||
@ -91,15 +73,26 @@ namespace ZL {
|
|||||||
|
|
||||||
// deactivate_interactive_object(object_name)
|
// deactivate_interactive_object(object_name)
|
||||||
api.set_function("deactivate_interactive_object", [game](const std::string& objectName) {
|
api.set_function("deactivate_interactive_object", [game](const std::string& objectName) {
|
||||||
std::cout << "[script] deactivate_interactive_object: " << objectName << std::endl;
|
|
||||||
for (auto& intObj : game->interactiveObjects) {
|
for (auto& intObj : game->interactiveObjects) {
|
||||||
if (intObj.id == objectName) {
|
if (intObj.loadedObject.name == objectName) {
|
||||||
intObj.isActive = false;
|
intObj.isActive = false;
|
||||||
std::cout << "[script] Interactive object deactivated: " << objectName << std::endl;
|
std::cout << "[script] deactivate_interactive_object: " << objectName << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::cerr << "[script] Warning: Interactive object not found: " << objectName << std::endl;
|
std::cerr << "[script] deactivate_interactive_object: not found: " << objectName << std::endl;
|
||||||
|
});
|
||||||
|
|
||||||
|
// activate_interactive_object(object_name)
|
||||||
|
api.set_function("activate_interactive_object", [game](const std::string& objectName) {
|
||||||
|
for (auto& intObj : game->interactiveObjects) {
|
||||||
|
if (intObj.loadedObject.name == objectName) {
|
||||||
|
intObj.isActive = true;
|
||||||
|
std::cout << "[script] activate_interactive_object: " << objectName << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::cerr << "[script] activate_interactive_object: not found: " << objectName << std::endl;
|
||||||
});
|
});
|
||||||
|
|
||||||
// get_inventory_count()
|
// get_inventory_count()
|
||||||
@ -181,38 +174,6 @@ namespace ZL {
|
|||||||
npcs[index]->enabled = value;
|
npcs[index]->enabled = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
// receive_npc_gift(npc_index)
|
|
||||||
api.set_function("receive_npc_gift", [game, inventory](int npcIndex) {
|
|
||||||
std::cout << "[script] receive_npc_gift: npc index " << npcIndex << std::endl;
|
|
||||||
|
|
||||||
auto& npcs = game->npcs;
|
|
||||||
if (npcIndex < 0 || npcIndex >= static_cast<int>(npcs.size())) {
|
|
||||||
std::cerr << "[script] receive_npc_gift: index out of range\n";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& npc = npcs[npcIndex];
|
|
||||||
if (!npc) {
|
|
||||||
std::cerr << "[script] receive_npc_gift: npc is null\n";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (npc->giftReceived) {
|
|
||||||
std::cout << "[script] NPC " << npc->npcName << " already gave gift\n";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (npc->giftItem.id.empty()) {
|
|
||||||
std::cerr << "[script] receive_npc_gift: NPC has no gift\n";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
inventory->addItem(npc->giftItem);
|
|
||||||
npc->giftReceived = true;
|
|
||||||
|
|
||||||
std::cout << "[script] Received gift from " << npc->npcName << ": "
|
|
||||||
<< npc->giftItem.name << std::endl;
|
|
||||||
});
|
|
||||||
|
|
||||||
lua.script_file(scriptPath);
|
lua.script_file(scriptPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
#include "utils/Utils.h"
|
#include "utils/Utils.h"
|
||||||
#include <Eigen/Geometry>
|
#include <Eigen/Geometry>
|
||||||
#include "../Character.h"
|
#include "../Character.h"
|
||||||
|
|
||||||
namespace ZL {
|
namespace ZL {
|
||||||
|
|
||||||
void set_Texture(Character& npc, const TextureDataStruct& texture);
|
void set_Texture(Character& npc, const TextureDataStruct& texture);
|
||||||
@ -14,53 +15,53 @@ namespace ZL {
|
|||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
std::vector<GameObjectData> GameObjectLoader::loadFromJson(const std::string& jsonPath, const std::string& zipPath)
|
// -----------------------------------------------------------------------
|
||||||
{
|
// Private helpers
|
||||||
std::vector<GameObjectData> objects;
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
json GameObjectLoader::loadJson(const std::string& jsonPath, const std::string& zipPath)
|
||||||
|
{
|
||||||
std::string content;
|
std::string content;
|
||||||
try {
|
try {
|
||||||
if (zipPath.empty()) {
|
if (zipPath.empty()) {
|
||||||
content = readTextFile(jsonPath);
|
content = readTextFile(jsonPath);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
auto buf = readFileFromZIP(jsonPath, zipPath);
|
auto buf = readFileFromZIP(jsonPath, zipPath);
|
||||||
if (buf.empty()) {
|
if (buf.empty())
|
||||||
std::cerr << "UiManager: failed to read " << jsonPath << " from zip " << zipPath << std::endl;
|
throw std::runtime_error("empty result from zip");
|
||||||
throw std::runtime_error("Failed to load UI file: " + jsonPath);
|
|
||||||
}
|
|
||||||
content.assign(buf.begin(), buf.end());
|
content.assign(buf.begin(), buf.end());
|
||||||
}
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
throw std::runtime_error("Failed to read " + jsonPath + ": " + e.what());
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
|
||||||
std::cerr << "UiManager: failed to open " << jsonPath << " : " << e.what() << std::endl;
|
|
||||||
throw std::runtime_error("Failed to load UI file: " + jsonPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
json j;
|
|
||||||
try {
|
|
||||||
j = json::parse(content);
|
|
||||||
}
|
|
||||||
catch (const std::exception& e) {
|
|
||||||
std::cerr << "UiManager: json parse error: " << e.what() << std::endl;
|
|
||||||
throw std::runtime_error("Failed to load UI file: " + jsonPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//json j;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/*std::ifstream file(jsonPath);
|
return json::parse(content);
|
||||||
if (!file.is_open()) {
|
} catch (const std::exception& e) {
|
||||||
throw std::runtime_error("Could not open file: " + jsonPath);
|
throw std::runtime_error("JSON parse error in " + jsonPath + ": " + e.what());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.peek() == std::ifstream::traits_type::eof()) {
|
static GameObjectData parseGameObjectData(const json& item)
|
||||||
throw std::runtime_error("JSON file is empty: " + jsonPath);
|
{
|
||||||
|
GameObjectData data;
|
||||||
|
data.name = item.value("name", "Unknown");
|
||||||
|
data.texturePath = item.value("texturePath", "");
|
||||||
|
data.meshPath = item.value("meshPath", "");
|
||||||
|
data.rotationX = item.value("rotationX", 0.0f);
|
||||||
|
data.rotationY = item.value("rotationY", 0.0f);
|
||||||
|
data.rotationZ = item.value("rotationZ", 0.0f);
|
||||||
|
data.positionX = item.value("positionX", 0.0f);
|
||||||
|
data.positionY = item.value("positionY", 0.0f);
|
||||||
|
data.positionZ = item.value("positionZ", 0.0f);
|
||||||
|
data.scale = item.value("scale", 1.0f);
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
file >> j;*/
|
std::vector<GameObjectData> GameObjectLoader::loadFromJson(const std::string& jsonPath, const std::string& zipPath)
|
||||||
|
{
|
||||||
|
std::vector<GameObjectData> objects;
|
||||||
|
json j = loadJson(jsonPath, zipPath);
|
||||||
|
|
||||||
if (!j.contains("objects") || !j["objects"].is_array()) {
|
if (!j.contains("objects") || !j["objects"].is_array()) {
|
||||||
std::cerr << "Warning: 'objects' array not found in " << jsonPath << std::endl;
|
std::cerr << "Warning: 'objects' array not found in " << jsonPath << std::endl;
|
||||||
@ -68,115 +69,90 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& item : j["objects"]) {
|
for (const auto& item : j["objects"]) {
|
||||||
GameObjectData data;
|
GameObjectData data = parseGameObjectData(item);
|
||||||
|
if (!data.meshPath.empty())
|
||||||
data.name = item.value("name", "Unknown");
|
objects.push_back(std::move(data));
|
||||||
data.texturePath = item.value("texturePath", "");
|
|
||||||
data.meshPath = item.value("meshPath", "");
|
|
||||||
|
|
||||||
data.rotationX = item.value("rotationX", 0.0f);
|
|
||||||
data.rotationY = item.value("rotationY", 0.0f);
|
|
||||||
data.rotationZ = item.value("rotationZ", 0.0f);
|
|
||||||
|
|
||||||
data.positionX = item.value("positionX", 0.0f);
|
|
||||||
data.positionY = item.value("positionY", 0.0f);
|
|
||||||
data.positionZ = item.value("positionZ", 0.0f);
|
|
||||||
|
|
||||||
data.scale = item.value("scale", 1.0f);
|
|
||||||
|
|
||||||
// Interactive object properties
|
|
||||||
data.interactive = item.value("interactive", false);
|
|
||||||
if (data.interactive && item.contains("item") && item["item"].is_object()) {
|
|
||||||
const auto& itemData = item["item"];
|
|
||||||
data.itemId = itemData.value("id", "");
|
|
||||||
data.itemName = itemData.value("name", "Unknown Item");
|
|
||||||
data.itemDescription = itemData.value("description", "");
|
|
||||||
data.itemIcon = itemData.value("icon", "");
|
|
||||||
data.interactionRadius = itemData.value("radius", 2.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
data.activateFunctionName = item.value("activateFunction", "");
|
|
||||||
|
|
||||||
if (!data.meshPath.empty()) {
|
|
||||||
objects.push_back(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "Successfully loaded " << objects.size() << " game objects from " << jsonPath << std::endl;
|
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
std::cerr << "Error loading JSON: " << e.what() << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::cout << "Loaded " << objects.size() << " static objects from " << jsonPath << std::endl;
|
||||||
return objects;
|
return objects;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<InteractiveObjectData> GameObjectLoader::loadInteractiveFromJson(const std::string& jsonPath, const std::string& zipPath)
|
||||||
|
{
|
||||||
|
std::vector<InteractiveObjectData> objects;
|
||||||
|
json j = loadJson(jsonPath, zipPath);
|
||||||
|
|
||||||
|
if (!j.contains("objects") || !j["objects"].is_array()) {
|
||||||
|
std::cerr << "Warning: 'objects' array not found in " << jsonPath << std::endl;
|
||||||
|
return objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& item : j["objects"]) {
|
||||||
|
InteractiveObjectData data;
|
||||||
|
data.base = parseGameObjectData(item);
|
||||||
|
data.interactionRadius = item.value("interactionRadius", 2.0f);
|
||||||
|
data.activateFunctionName = item.value("activateFunction", "");
|
||||||
|
if (!data.base.meshPath.empty())
|
||||||
|
objects.push_back(std::move(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Loaded " << objects.size() << " interactive objects from " << jsonPath << std::endl;
|
||||||
|
return objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadedGameObject GameObjectLoader::buildLoadedObject(const GameObjectData& data, Renderer& renderer, const std::string& zipPath)
|
||||||
|
{
|
||||||
|
LoadedGameObject obj;
|
||||||
|
obj.name = data.name;
|
||||||
|
|
||||||
|
obj.texture = renderer.textureManager.LoadFromPng(data.texturePath, zipPath);
|
||||||
|
|
||||||
|
if (data.meshPath.size() > 4 && data.meshPath.compare(data.meshPath.size() - 4, 4, ".bin") == 0)
|
||||||
|
obj.mesh.data = LoadModelFromBinFile(data.meshPath, zipPath);
|
||||||
|
else
|
||||||
|
obj.mesh.data = LoadFromTextFile02(data.meshPath, zipPath);
|
||||||
|
|
||||||
|
Eigen::Quaternionf rot = Eigen::Quaternionf::Identity();
|
||||||
|
if (data.rotationX != 0.0f)
|
||||||
|
rot = Eigen::Quaternionf(Eigen::AngleAxisf(data.rotationX, Eigen::Vector3f::UnitX())) * rot;
|
||||||
|
if (data.rotationY != 0.0f)
|
||||||
|
rot = Eigen::Quaternionf(Eigen::AngleAxisf(data.rotationY, Eigen::Vector3f::UnitY())) * rot;
|
||||||
|
if (data.rotationZ != 0.0f)
|
||||||
|
rot = Eigen::Quaternionf(Eigen::AngleAxisf(data.rotationZ, Eigen::Vector3f::UnitZ())) * rot;
|
||||||
|
obj.mesh.data.RotateByMatrix(rot.toRotationMatrix());
|
||||||
|
|
||||||
|
if (data.scale != 1.0f)
|
||||||
|
obj.mesh.data.Scale(data.scale);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Public API
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
std::unordered_map<std::string, LoadedGameObject> GameObjectLoader::loadAndCreateGameObjects(
|
std::unordered_map<std::string, LoadedGameObject> GameObjectLoader::loadAndCreateGameObjects(
|
||||||
const std::string& jsonPath,
|
const std::string& jsonPath,
|
||||||
Renderer& renderer,
|
Renderer& renderer,
|
||||||
const std::string& zipPath)
|
const std::string& zipPath)
|
||||||
{
|
{
|
||||||
std::unordered_map<std::string, LoadedGameObject> gameObjects;
|
std::unordered_map<std::string, LoadedGameObject> gameObjects;
|
||||||
std::vector<GameObjectData> objectsData = loadFromJson(jsonPath, zipPath);
|
|
||||||
|
|
||||||
for (const auto& objData : objectsData) {
|
for (const auto& data : loadFromJson(jsonPath, zipPath)) {
|
||||||
if (objData.interactive) continue; // Skip interactive objects, they're handled separately
|
std::cout << "Loading game object: " << data.name << std::endl;
|
||||||
|
|
||||||
std::cout << "Loading game object: " << objData.name << std::endl;
|
|
||||||
|
|
||||||
LoadedGameObject gameObj;
|
|
||||||
gameObj.name = objData.name;
|
|
||||||
|
|
||||||
// Load texture
|
|
||||||
try {
|
try {
|
||||||
gameObj.texture = renderer.textureManager.LoadFromPng(objData.texturePath, zipPath);
|
LoadedGameObject obj = buildLoadedObject(data, renderer, zipPath);
|
||||||
}
|
|
||||||
catch (const std::exception& e) {
|
|
||||||
std::cerr << "GameObjectLoader: Failed to load texture for '" << objData.name << "': " << e.what() << std::endl;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load mesh
|
if (data.positionX != 0.0f || data.positionY != 0.0f || data.positionZ != 0.0f)
|
||||||
try {
|
obj.mesh.data.Move({ data.positionX, data.positionY, data.positionZ });
|
||||||
if (objData.meshPath.size() > 4 && objData.meshPath.compare(objData.meshPath.size() - 4, 4, ".bin") == 0)
|
|
||||||
gameObj.mesh.data = LoadModelFromBinFile(objData.meshPath, zipPath);
|
|
||||||
else
|
|
||||||
gameObj.mesh.data = LoadFromTextFile02(objData.meshPath, zipPath);
|
|
||||||
}
|
|
||||||
catch (const std::exception& e) {
|
|
||||||
std::cerr << "GameObjectLoader: Failed to load mesh for '" << objData.name << "': " << e.what() << std::endl;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply rotation
|
obj.mesh.RefreshVBO();
|
||||||
Eigen::Quaternionf rotationQuat = Eigen::Quaternionf::Identity();
|
gameObjects[data.name] = std::move(obj);
|
||||||
|
std::cout << "Successfully loaded: " << data.name << std::endl;
|
||||||
if (objData.rotationX != 0.0f) {
|
} catch (const std::exception& e) {
|
||||||
rotationQuat = Eigen::Quaternionf(Eigen::AngleAxisf(objData.rotationX, Eigen::Vector3f::UnitX())) * rotationQuat;
|
std::cerr << "GameObjectLoader: Failed to load '" << data.name << "': " << e.what() << std::endl;
|
||||||
}
|
}
|
||||||
if (objData.rotationY != 0.0f) {
|
|
||||||
rotationQuat = Eigen::Quaternionf(Eigen::AngleAxisf(objData.rotationY, Eigen::Vector3f::UnitY())) * rotationQuat;
|
|
||||||
}
|
|
||||||
if (objData.rotationZ != 0.0f) {
|
|
||||||
rotationQuat = Eigen::Quaternionf(Eigen::AngleAxisf(objData.rotationZ, Eigen::Vector3f::UnitZ())) * rotationQuat;
|
|
||||||
}
|
|
||||||
|
|
||||||
gameObj.mesh.data.RotateByMatrix(rotationQuat.toRotationMatrix());
|
|
||||||
|
|
||||||
// Apply scale
|
|
||||||
if (objData.scale != 1.0f) {
|
|
||||||
gameObj.mesh.data.Scale(objData.scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply position
|
|
||||||
if (objData.positionX != 0.0f || objData.positionY != 0.0f || objData.positionZ != 0.0f) {
|
|
||||||
gameObj.mesh.data.Move({ objData.positionX, objData.positionY, objData.positionZ });
|
|
||||||
}
|
|
||||||
|
|
||||||
gameObj.mesh.RefreshVBO();
|
|
||||||
gameObjects[objData.name] = gameObj;
|
|
||||||
|
|
||||||
std::cout << "Successfully loaded: " << objData.name << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Total game objects loaded: " << gameObjects.size() << std::endl;
|
std::cout << "Total game objects loaded: " << gameObjects.size() << std::endl;
|
||||||
@ -189,103 +165,49 @@ namespace ZL {
|
|||||||
const std::string& zipPath)
|
const std::string& zipPath)
|
||||||
{
|
{
|
||||||
std::vector<InteractiveObject> interactiveObjects;
|
std::vector<InteractiveObject> interactiveObjects;
|
||||||
std::vector<GameObjectData> objectsData = loadFromJson(jsonPath, zipPath);
|
|
||||||
|
|
||||||
for (const auto& objData : objectsData) {
|
if (jsonPath.empty())
|
||||||
if (!objData.interactive) continue;
|
return interactiveObjects;
|
||||||
|
|
||||||
std::cout << "Loading interactive object: " << objData.name << std::endl;
|
|
||||||
|
|
||||||
|
for (const auto& data : loadInteractiveFromJson(jsonPath, zipPath)) {
|
||||||
|
std::cout << "Loading interactive object: " << data.base.name << std::endl;
|
||||||
|
try {
|
||||||
InteractiveObject intObj;
|
InteractiveObject intObj;
|
||||||
//intObj.id = objData.name;
|
intObj.loadedObject = buildLoadedObject(data.base, renderer, zipPath);
|
||||||
intObj.id = !objData.itemId.empty() ? objData.itemId : objData.name;
|
intObj.interactionRadius = data.interactionRadius;
|
||||||
intObj.name = objData.name;
|
intObj.activateFunctionName = data.activateFunctionName;
|
||||||
intObj.interactionRadius = objData.interactionRadius;
|
|
||||||
intObj.activateFunctionName = objData.activateFunctionName;
|
|
||||||
|
|
||||||
// Load texture
|
intObj.loadedObject.mesh.RefreshVBO();
|
||||||
try {
|
|
||||||
intObj.texture = renderer.textureManager.LoadFromPng(objData.texturePath, zipPath);
|
// Center mesh at origin; store corrected world position separately.
|
||||||
|
if (!intObj.loadedObject.mesh.data.PositionData.empty()) {
|
||||||
|
Eigen::Vector3f meshMin = intObj.loadedObject.mesh.data.PositionData[0];
|
||||||
|
Eigen::Vector3f meshMax = intObj.loadedObject.mesh.data.PositionData[0];
|
||||||
|
for (const auto& v : intObj.loadedObject.mesh.data.PositionData) {
|
||||||
|
meshMin = meshMin.cwiseMin(v);
|
||||||
|
meshMax = meshMax.cwiseMax(v);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
|
||||||
std::cerr << "GameObjectLoader: Failed to load texture for interactive '" << objData.name << "': " << e.what() << std::endl;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load mesh
|
|
||||||
try {
|
|
||||||
if (objData.meshPath.size() > 4 && objData.meshPath.compare(objData.meshPath.size() - 4, 4, ".bin") == 0)
|
|
||||||
intObj.mesh.data = LoadModelFromBinFile(objData.meshPath, zipPath);
|
|
||||||
else
|
|
||||||
intObj.mesh.data = LoadFromTextFile02(objData.meshPath, zipPath);
|
|
||||||
}
|
|
||||||
catch (const std::exception& e) {
|
|
||||||
std::cerr << "GameObjectLoader: Failed to load mesh for interactive '" << objData.name << "': " << e.what() << std::endl;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply rotation
|
|
||||||
Eigen::Quaternionf rotationQuat = Eigen::Quaternionf::Identity();
|
|
||||||
|
|
||||||
if (objData.rotationX != 0.0f) {
|
|
||||||
rotationQuat = Eigen::Quaternionf(Eigen::AngleAxisf(objData.rotationX, Eigen::Vector3f::UnitX())) * rotationQuat;
|
|
||||||
}
|
|
||||||
if (objData.rotationY != 0.0f) {
|
|
||||||
rotationQuat = Eigen::Quaternionf(Eigen::AngleAxisf(objData.rotationY, Eigen::Vector3f::UnitY())) * rotationQuat;
|
|
||||||
}
|
|
||||||
if (objData.rotationZ != 0.0f) {
|
|
||||||
rotationQuat = Eigen::Quaternionf(Eigen::AngleAxisf(objData.rotationZ, Eigen::Vector3f::UnitZ())) * rotationQuat;
|
|
||||||
}
|
|
||||||
|
|
||||||
intObj.mesh.data.RotateByMatrix(rotationQuat.toRotationMatrix());
|
|
||||||
|
|
||||||
// Apply scale
|
|
||||||
if (objData.scale != 1.0f) {
|
|
||||||
intObj.mesh.data.Scale(objData.scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
intObj.mesh.RefreshVBO();
|
|
||||||
|
|
||||||
// Calculate mesh bounds to properly offset position
|
|
||||||
if (!intObj.mesh.data.PositionData.empty()) {
|
|
||||||
Eigen::Vector3f meshMin = intObj.mesh.data.PositionData[0];
|
|
||||||
Eigen::Vector3f meshMax = intObj.mesh.data.PositionData[0];
|
|
||||||
|
|
||||||
for (const auto& vert : intObj.mesh.data.PositionData) {
|
|
||||||
meshMin = meshMin.cwiseMin(vert);
|
|
||||||
meshMax = meshMax.cwiseMax(vert);
|
|
||||||
}
|
|
||||||
|
|
||||||
Eigen::Vector3f meshCenter = (meshMin + meshMax) * 0.5f;
|
Eigen::Vector3f meshCenter = (meshMin + meshMax) * 0.5f;
|
||||||
std::cout << "[LOADER] Mesh bounds:" << std::endl;
|
|
||||||
std::cout << " Min: (" << meshMin.x() << ", " << meshMin.y() << ", " << meshMin.z() << ")" << std::endl;
|
|
||||||
std::cout << " Max: (" << meshMax.x() << ", " << meshMax.y() << ", " << meshMax.z() << ")" << std::endl;
|
|
||||||
std::cout << " Center: (" << meshCenter.x() << ", " << meshCenter.y() << ", " << meshCenter.z() << ")" << std::endl;
|
|
||||||
|
|
||||||
// Translate mesh so it's centered at origin
|
intObj.loadedObject.mesh.data.Move(-meshCenter);
|
||||||
intObj.mesh.data.Move(-meshCenter);
|
intObj.loadedObject.mesh.RefreshVBO();
|
||||||
intObj.mesh.RefreshVBO();
|
|
||||||
|
|
||||||
// Adjust position to account for mesh offset
|
intObj.position = Eigen::Vector3f(data.base.positionX, data.base.positionY, data.base.positionZ) + meshCenter;
|
||||||
intObj.position = Eigen::Vector3f(objData.positionX, objData.positionY, objData.positionZ) + meshCenter;
|
|
||||||
|
|
||||||
std::cout << "[LOADER] Corrected position: (" << intObj.position.x() << ", "
|
|
||||||
<< intObj.position.y() << ", " << intObj.position.z() << ")" << std::endl;
|
|
||||||
} else {
|
} else {
|
||||||
// Fallback if no vertices
|
intObj.position = Eigen::Vector3f(data.base.positionX, data.base.positionY, data.base.positionZ);
|
||||||
intObj.position = Eigen::Vector3f(objData.positionX, objData.positionY, objData.positionZ);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create item
|
interactiveObjects.push_back(std::move(intObj));
|
||||||
intObj.dropItem = Item(objData.itemId, objData.itemName, objData.itemDescription, objData.itemIcon);
|
|
||||||
|
|
||||||
interactiveObjects.push_back(intObj);
|
if (!data.activateFunctionName.empty())
|
||||||
|
std::cout << "Successfully loaded interactive: " << data.base.name
|
||||||
|
<< " (function: " << data.activateFunctionName << ")" << std::endl;
|
||||||
|
else
|
||||||
|
std::cout << "Successfully loaded interactive: " << data.base.name << std::endl;
|
||||||
|
|
||||||
std::cout << "Successfully loaded interactive: " << objData.name << " (item: " << objData.itemName;
|
} catch (const std::exception& e) {
|
||||||
if (!objData.activateFunctionName.empty()) {
|
std::cerr << "GameObjectLoader: Failed to load interactive '" << data.base.name << "': " << e.what() << std::endl;
|
||||||
std::cout << ", function: " << objData.activateFunctionName;
|
|
||||||
}
|
}
|
||||||
std::cout << ")" << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Total interactive objects loaded: " << interactiveObjects.size() << std::endl;
|
std::cout << "Total interactive objects loaded: " << interactiveObjects.size() << std::endl;
|
||||||
@ -295,55 +217,14 @@ namespace ZL {
|
|||||||
std::vector<NpcData> GameObjectLoader::loadNpcsFromJson(const std::string& jsonPath, const std::string& zipPath)
|
std::vector<NpcData> GameObjectLoader::loadNpcsFromJson(const std::string& jsonPath, const std::string& zipPath)
|
||||||
{
|
{
|
||||||
std::vector<NpcData> npcs;
|
std::vector<NpcData> npcs;
|
||||||
|
json j = loadJson(jsonPath, zipPath);
|
||||||
|
|
||||||
std::string content;
|
|
||||||
try {
|
|
||||||
if (zipPath.empty()) {
|
|
||||||
content = readTextFile(jsonPath);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
auto buf = readFileFromZIP(jsonPath, zipPath);
|
|
||||||
if (buf.empty()) {
|
|
||||||
std::cerr << "UiManager: failed to read " << jsonPath << " from zip " << zipPath << std::endl;
|
|
||||||
throw std::runtime_error("Failed to load UI file: " + jsonPath);
|
|
||||||
}
|
|
||||||
content.assign(buf.begin(), buf.end());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception& e) {
|
|
||||||
std::cerr << "UiManager: failed to open " << jsonPath << " : " << e.what() << std::endl;
|
|
||||||
throw std::runtime_error("Failed to load UI file: " + jsonPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
json j;
|
|
||||||
try {
|
|
||||||
j = json::parse(content);
|
|
||||||
}
|
|
||||||
catch (const std::exception& e) {
|
|
||||||
std::cerr << "UiManager: json parse error: " << e.what() << std::endl;
|
|
||||||
throw std::runtime_error("Failed to load UI file: " + jsonPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
//json j;
|
|
||||||
|
|
||||||
try {
|
|
||||||
/*std::ifstream file(jsonPath);
|
|
||||||
if (!file.is_open()) {
|
|
||||||
throw std::runtime_error("Could not open file: " + jsonPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file.peek() == std::ifstream::traits_type::eof()) {
|
|
||||||
throw std::runtime_error("JSON file is empty: " + jsonPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
file >> j;*/
|
|
||||||
|
|
||||||
if (!j.contains("npcs") || !j["npcs"].is_array()) {
|
if (!j.contains("npcs") || !j["npcs"].is_array()) {
|
||||||
std::cerr << "Warning: 'npcs' array not found in " << jsonPath << std::endl;
|
std::cerr << "Warning: 'npcs' array not found in " << jsonPath << std::endl;
|
||||||
return npcs;
|
return npcs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
for (const auto& item : j["npcs"]) {
|
for (const auto& item : j["npcs"]) {
|
||||||
NpcData data;
|
NpcData data;
|
||||||
|
|
||||||
@ -352,11 +233,10 @@ namespace ZL {
|
|||||||
data.texturePath = item.value("texturePath", "");
|
data.texturePath = item.value("texturePath", "");
|
||||||
if (item.contains("meshTextures") && item["meshTextures"].is_object()) {
|
if (item.contains("meshTextures") && item["meshTextures"].is_object()) {
|
||||||
for (auto it = item["meshTextures"].begin(); it != item["meshTextures"].end(); ++it) {
|
for (auto it = item["meshTextures"].begin(); it != item["meshTextures"].end(); ++it) {
|
||||||
if (it.value().is_string()) {
|
if (it.value().is_string())
|
||||||
data.meshTextures[it.key()] = it.value().get<std::string>();
|
data.meshTextures[it.key()] = it.value().get<std::string>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
data.interactionRadius = item.value("interactionRadius", 0.0f);
|
data.interactionRadius = item.value("interactionRadius", 0.0f);
|
||||||
data.animationIdlePath = item.value("animationIdlePath", "");
|
data.animationIdlePath = item.value("animationIdlePath", "");
|
||||||
data.animationWalkPath = item.value("animationWalkPath", "");
|
data.animationWalkPath = item.value("animationWalkPath", "");
|
||||||
@ -366,40 +246,23 @@ namespace ZL {
|
|||||||
data.animationActionToStandPath = item.value("animationActionToStandPath", "");
|
data.animationActionToStandPath = item.value("animationActionToStandPath", "");
|
||||||
data.animationActionToDeathPath = item.value("animationActionToDeathPath", "");
|
data.animationActionToDeathPath = item.value("animationActionToDeathPath", "");
|
||||||
data.animationDeathIdlePath = item.value("animationDeathIdlePath", "");
|
data.animationDeathIdlePath = item.value("animationDeathIdlePath", "");
|
||||||
|
|
||||||
data.positionX = item.value("positionX", 0.0f);
|
data.positionX = item.value("positionX", 0.0f);
|
||||||
data.positionY = item.value("positionY", 0.0f);
|
data.positionY = item.value("positionY", 0.0f);
|
||||||
data.positionZ = item.value("positionZ", 0.0f);
|
data.positionZ = item.value("positionZ", 0.0f);
|
||||||
|
|
||||||
data.walkSpeed = item.value("walkSpeed", 1.5f);
|
data.walkSpeed = item.value("walkSpeed", 1.5f);
|
||||||
data.rotationSpeed = item.value("rotationSpeed", 8.0f);
|
data.rotationSpeed = item.value("rotationSpeed", 8.0f);
|
||||||
data.modelScale = item.value("modelScale", 0.01f);
|
data.modelScale = item.value("modelScale", 0.01f);
|
||||||
|
|
||||||
// Model correction rotation (in degrees, will convert to radians)
|
|
||||||
data.modelCorrectionRotX = item.value("modelCorrectionRotX", 0.0f) * static_cast<float>(M_PI) / 180.0f;
|
data.modelCorrectionRotX = item.value("modelCorrectionRotX", 0.0f) * static_cast<float>(M_PI) / 180.0f;
|
||||||
data.modelCorrectionRotY = item.value("modelCorrectionRotY", 0.0f) * static_cast<float>(M_PI) / 180.0f;
|
data.modelCorrectionRotY = item.value("modelCorrectionRotY", 0.0f) * static_cast<float>(M_PI) / 180.0f;
|
||||||
data.modelCorrectionRotZ = item.value("modelCorrectionRotZ", 0.0f) * static_cast<float>(M_PI) / 180.0f;
|
data.modelCorrectionRotZ = item.value("modelCorrectionRotZ", 0.0f) * static_cast<float>(M_PI) / 180.0f;
|
||||||
|
|
||||||
data.facingAngle = item.value("facingAngle", 0.0f);
|
data.facingAngle = item.value("facingAngle", 0.0f);
|
||||||
|
|
||||||
data.hp = item.value("hp", 100.0f);
|
data.hp = item.value("hp", 100.0f);
|
||||||
data.canAttack = item.value("canAttack", false);
|
data.canAttack = item.value("canAttack", false);
|
||||||
data.enabled = item.value("enabled", true);
|
data.enabled = item.value("enabled", true);
|
||||||
|
|
||||||
// Load gift data if available
|
npcs.push_back(std::move(data));
|
||||||
if (item.contains("gift") && item["gift"].is_object()) {
|
|
||||||
const auto& giftData = item["gift"];
|
|
||||||
data.gift.id = giftData.value("id", "");
|
|
||||||
data.gift.name = giftData.value("name", "Gift");
|
|
||||||
data.gift.description = giftData.value("description", "A gift from an NPC");
|
|
||||||
data.gift.icon = giftData.value("icon", "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
npcs.push_back(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "Successfully loaded " << npcs.size() << " NPCs from " << jsonPath << std::endl;
|
std::cout << "Successfully loaded " << npcs.size() << " NPCs from " << jsonPath << std::endl;
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
std::cerr << "Error loading NPCs from JSON: " << e.what() << std::endl;
|
std::cerr << "Error loading NPCs from JSON: " << e.what() << std::endl;
|
||||||
}
|
}
|
||||||
@ -412,60 +275,43 @@ namespace ZL {
|
|||||||
const std::string& zipPath)
|
const std::string& zipPath)
|
||||||
{
|
{
|
||||||
std::vector<std::unique_ptr<Character>> npcs;
|
std::vector<std::unique_ptr<Character>> npcs;
|
||||||
std::vector<NpcData> npcsData = loadNpcsFromJson(jsonPath, zipPath);
|
|
||||||
|
|
||||||
for (const auto& npcData : npcsData) {
|
for (const auto& npcData : loadNpcsFromJson(jsonPath, zipPath)) {
|
||||||
std::cout << "Loading NPC: " << npcData.name << std::endl;
|
std::cout << "Loading NPC: " << npcData.name << std::endl;
|
||||||
|
|
||||||
auto npc = std::make_unique<Character>();
|
auto npc = std::make_unique<Character>();
|
||||||
|
|
||||||
// Load animations
|
|
||||||
try {
|
try {
|
||||||
if (npcData.animationIdlePath.substr(npcData.animationIdlePath.size() - 5) == ".anim")
|
if (npcData.animationIdlePath.substr(npcData.animationIdlePath.size() - 5) == ".anim")
|
||||||
{
|
|
||||||
npc->loadBinaryAnimation(AnimationState::STAND, npcData.animationIdlePath, zipPath);
|
npc->loadBinaryAnimation(AnimationState::STAND, npcData.animationIdlePath, zipPath);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
npc->loadAnimation(AnimationState::STAND, npcData.animationIdlePath, zipPath);
|
npc->loadAnimation(AnimationState::STAND, npcData.animationIdlePath, zipPath);
|
||||||
}
|
|
||||||
std::cout << " Loaded IDLE animation: " << npcData.animationIdlePath << std::endl;
|
std::cout << " Loaded IDLE animation: " << npcData.animationIdlePath << std::endl;
|
||||||
}
|
} catch (const std::exception& e) {
|
||||||
catch (const std::exception& e) {
|
|
||||||
std::cerr << "GameObjectLoader: Failed to load IDLE animation for '" << npcData.name << "': " << e.what() << std::endl;
|
std::cerr << "GameObjectLoader: Failed to load IDLE animation for '" << npcData.name << "': " << e.what() << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (npcData.animationWalkPath.substr(npcData.animationWalkPath.size() - 5) == ".anim")
|
if (npcData.animationWalkPath.substr(npcData.animationWalkPath.size() - 5) == ".anim")
|
||||||
{
|
|
||||||
npc->loadBinaryAnimation(AnimationState::WALK, npcData.animationWalkPath, zipPath);
|
npc->loadBinaryAnimation(AnimationState::WALK, npcData.animationWalkPath, zipPath);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
npc->loadAnimation(AnimationState::WALK, npcData.animationWalkPath, zipPath);
|
npc->loadAnimation(AnimationState::WALK, npcData.animationWalkPath, zipPath);
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << " Loaded WALK animation: " << npcData.animationWalkPath << std::endl;
|
std::cout << " Loaded WALK animation: " << npcData.animationWalkPath << std::endl;
|
||||||
}
|
} catch (const std::exception& e) {
|
||||||
catch (const std::exception& e) {
|
|
||||||
std::cerr << "GameObjectLoader: Failed to load WALK animation for '" << npcData.name << "': " << e.what() << std::endl;
|
std::cerr << "GameObjectLoader: Failed to load WALK animation for '" << npcData.name << "': " << e.what() << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optional combat/death slots — empty path means "skip this slot".
|
|
||||||
auto loadOptionalAnim = [&](AnimationState state, const std::string& path, const char* label) {
|
auto loadOptionalAnim = [&](AnimationState state, const std::string& path, const char* label) {
|
||||||
if (path.empty()) return;
|
if (path.empty()) return;
|
||||||
try {
|
try {
|
||||||
if (path.size() >= 5 && path.substr(path.size() - 5) == ".anim") {
|
if (path.size() >= 5 && path.substr(path.size() - 5) == ".anim")
|
||||||
npc->loadBinaryAnimation(state, path, zipPath);
|
npc->loadBinaryAnimation(state, path, zipPath);
|
||||||
}
|
else
|
||||||
else {
|
|
||||||
npc->loadAnimation(state, path, zipPath);
|
npc->loadAnimation(state, path, zipPath);
|
||||||
}
|
|
||||||
std::cout << " Loaded " << label << " animation: " << path << std::endl;
|
std::cout << " Loaded " << label << " animation: " << path << std::endl;
|
||||||
}
|
} catch (const std::exception& e) {
|
||||||
catch (const std::exception& e) {
|
|
||||||
std::cerr << "GameObjectLoader: Failed to load " << label
|
std::cerr << "GameObjectLoader: Failed to load " << label
|
||||||
<< " animation for '" << npcData.name << "': " << e.what() << std::endl;
|
<< " animation for '" << npcData.name << "': " << e.what() << std::endl;
|
||||||
}
|
}
|
||||||
@ -477,38 +323,21 @@ namespace ZL {
|
|||||||
loadOptionalAnim(AnimationState::ACTION_TO_DEATH, npcData.animationActionToDeathPath, "ACTION_TO_DEATH");
|
loadOptionalAnim(AnimationState::ACTION_TO_DEATH, npcData.animationActionToDeathPath, "ACTION_TO_DEATH");
|
||||||
loadOptionalAnim(AnimationState::DEATH_IDLE, npcData.animationDeathIdlePath, "DEATH_IDLE");
|
loadOptionalAnim(AnimationState::DEATH_IDLE, npcData.animationDeathIdlePath, "DEATH_IDLE");
|
||||||
|
|
||||||
// Load textures: per-mesh map takes precedence; fall back to single texturePath.
|
|
||||||
try {
|
try {
|
||||||
if (!npcData.meshTextures.empty()) {
|
if (!npcData.meshTextures.empty()) {
|
||||||
std::unordered_map<std::string, std::shared_ptr<Texture>> cache;
|
|
||||||
for (const auto& entry : npcData.meshTextures) {
|
for (const auto& entry : npcData.meshTextures) {
|
||||||
const std::string& meshName = entry.first;
|
set_Texture(*npc, entry.first, CreateTextureDataFromPng(entry.second, zipPath.c_str()));
|
||||||
const std::string& path = entry.second;
|
std::cout << " -> mesh '" << entry.first << "'" << std::endl;
|
||||||
/*auto cit = cache.find(path);
|
|
||||||
if (cit == cache.end()) {
|
|
||||||
auto tex = std::make_shared<Texture>(CreateTextureDataFromPng(path, zipPath.c_str()));
|
|
||||||
cit = cache.emplace(path, std::move(tex)).first;
|
|
||||||
std::cout << " Loaded texture: " << path << std::endl;
|
|
||||||
}*/
|
|
||||||
//npc->setTexture(meshName, std::make_shared<Texture>(CreateTextureDataFromPng(path, zipPath.c_str())));
|
|
||||||
set_Texture(*npc, meshName, CreateTextureDataFromPng(path, zipPath.c_str()));
|
|
||||||
//std::cout << xxa(*npc, CreateTextureDataFromPng(path, zipPath.c_str())) << std::endl;
|
|
||||||
std::cout << " -> mesh '" << meshName << "'" << std::endl;
|
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
//auto texture = std::make_shared<Texture>(CreateTextureDataFromPng(npcData.texturePath, zipPath.c_str()));
|
|
||||||
set_Texture(*npc, CreateTextureDataFromPng(npcData.texturePath, zipPath.c_str()));
|
set_Texture(*npc, CreateTextureDataFromPng(npcData.texturePath, zipPath.c_str()));
|
||||||
//std::cout << xxa(*npc, CreateTextureDataFromPng(npcData.texturePath, zipPath.c_str())) << std::endl;
|
|
||||||
std::cout << " Loaded texture: " << npcData.texturePath << std::endl;
|
std::cout << " Loaded texture: " << npcData.texturePath << std::endl;
|
||||||
}
|
}
|
||||||
}
|
} catch (const std::exception& e) {
|
||||||
catch (const std::exception& e) {
|
|
||||||
std::cerr << "GameObjectLoader: Failed to load texture for '" << npcData.name << "': " << e.what() << std::endl;
|
std::cerr << "GameObjectLoader: Failed to load texture for '" << npcData.name << "': " << e.what() << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set NPC properties
|
|
||||||
npc->walkSpeed = npcData.walkSpeed;
|
npc->walkSpeed = npcData.walkSpeed;
|
||||||
npc->rotationSpeed = npcData.rotationSpeed;
|
npc->rotationSpeed = npcData.rotationSpeed;
|
||||||
npc->modelScale = npcData.modelScale;
|
npc->modelScale = npcData.modelScale;
|
||||||
@ -518,35 +347,22 @@ namespace ZL {
|
|||||||
npc->canAttack = npcData.canAttack;
|
npc->canAttack = npcData.canAttack;
|
||||||
npc->enabled = npcData.enabled;
|
npc->enabled = npcData.enabled;
|
||||||
npc->interactionRadius = npcData.interactionRadius;
|
npc->interactionRadius = npcData.interactionRadius;
|
||||||
|
|
||||||
// Set NPC metadata
|
|
||||||
npc->npcId = npcData.id;
|
npc->npcId = npcData.id;
|
||||||
npc->npcName = npcData.name;
|
npc->npcName = npcData.name;
|
||||||
|
|
||||||
// Set gift
|
Eigen::Quaternionf corrRot = Eigen::Quaternionf::Identity();
|
||||||
if (!npcData.gift.id.empty()) {
|
if (npcData.modelCorrectionRotX != 0.0f)
|
||||||
npc->giftItem = Item(npcData.gift.id, npcData.gift.name, npcData.gift.description, npcData.gift.icon);
|
corrRot = Eigen::Quaternionf(Eigen::AngleAxisf(npcData.modelCorrectionRotX, Eigen::Vector3f::UnitX())) * corrRot;
|
||||||
std::cout << " Gift: " << npcData.gift.name << std::endl;
|
if (npcData.modelCorrectionRotY != 0.0f)
|
||||||
}
|
corrRot = Eigen::Quaternionf(Eigen::AngleAxisf(npcData.modelCorrectionRotY, Eigen::Vector3f::UnitY())) * corrRot;
|
||||||
|
if (npcData.modelCorrectionRotZ != 0.0f)
|
||||||
|
corrRot = Eigen::Quaternionf(Eigen::AngleAxisf(npcData.modelCorrectionRotZ, Eigen::Vector3f::UnitZ())) * corrRot;
|
||||||
|
npc->modelCorrectionRotation = corrRot;
|
||||||
|
|
||||||
Eigen::Quaternionf corrRotQuat = Eigen::Quaternionf::Identity();
|
|
||||||
if (npcData.modelCorrectionRotX != 0.0f) {
|
|
||||||
corrRotQuat = Eigen::Quaternionf(Eigen::AngleAxisf(npcData.modelCorrectionRotX, Eigen::Vector3f::UnitX())) * corrRotQuat;
|
|
||||||
}
|
|
||||||
if (npcData.modelCorrectionRotY != 0.0f) {
|
|
||||||
corrRotQuat = Eigen::Quaternionf(Eigen::AngleAxisf(npcData.modelCorrectionRotY, Eigen::Vector3f::UnitY())) * corrRotQuat;
|
|
||||||
}
|
|
||||||
if (npcData.modelCorrectionRotZ != 0.0f) {
|
|
||||||
corrRotQuat = Eigen::Quaternionf(Eigen::AngleAxisf(npcData.modelCorrectionRotZ, Eigen::Vector3f::UnitZ())) * corrRotQuat;
|
|
||||||
}
|
|
||||||
npc->modelCorrectionRotation = corrRotQuat;
|
|
||||||
|
|
||||||
// Set initial target to current position
|
|
||||||
npc->setTarget(npc->position);
|
npc->setTarget(npc->position);
|
||||||
|
|
||||||
npcs.push_back(std::move(npc));
|
npcs.push_back(std::move(npc));
|
||||||
|
|
||||||
std::cout << "Successfully loaded NPC: " << npcData.name << " at position ("
|
std::cout << "Successfully loaded NPC: " << npcData.name << " at ("
|
||||||
<< npcData.positionX << ", " << npcData.positionY << ", " << npcData.positionZ << ")" << std::endl;
|
<< npcData.positionX << ", " << npcData.positionY << ", " << npcData.positionZ << ")" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,20 +23,12 @@ namespace ZL {
|
|||||||
float positionY = 0.0f;
|
float positionY = 0.0f;
|
||||||
float positionZ = 0.0f;
|
float positionZ = 0.0f;
|
||||||
float scale = 1.0f;
|
float scale = 1.0f;
|
||||||
bool interactive = false;
|
|
||||||
std::string itemId;
|
|
||||||
std::string itemName;
|
|
||||||
std::string itemDescription;
|
|
||||||
std::string itemIcon;
|
|
||||||
float interactionRadius = 2.0f;
|
|
||||||
std::string activateFunctionName;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GiftData {
|
struct InteractiveObjectData {
|
||||||
std::string id;
|
GameObjectData base;
|
||||||
std::string name;
|
float interactionRadius = 2.0f;
|
||||||
std::string description;
|
std::string activateFunctionName;
|
||||||
std::string icon;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NpcData {
|
struct NpcData {
|
||||||
@ -66,20 +58,11 @@ namespace ZL {
|
|||||||
float hp = 100.0f;
|
float hp = 100.0f;
|
||||||
bool canAttack = false;
|
bool canAttack = false;
|
||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
GiftData gift;
|
|
||||||
float interactionRadius = 0.0f;
|
float interactionRadius = 0.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LoadedGameObject {
|
|
||||||
std::shared_ptr<Texture> texture;
|
|
||||||
VertexRenderStruct mesh;
|
|
||||||
std::string name;
|
|
||||||
};
|
|
||||||
|
|
||||||
class GameObjectLoader {
|
class GameObjectLoader {
|
||||||
public:
|
public:
|
||||||
static std::vector<GameObjectData> loadFromJson(const std::string& jsonPath, const std::string& zipPath = "");
|
|
||||||
|
|
||||||
static std::unordered_map<std::string, LoadedGameObject> loadAndCreateGameObjects(
|
static std::unordered_map<std::string, LoadedGameObject> loadAndCreateGameObjects(
|
||||||
const std::string& jsonPath,
|
const std::string& jsonPath,
|
||||||
Renderer& renderer,
|
Renderer& renderer,
|
||||||
@ -98,7 +81,12 @@ namespace ZL {
|
|||||||
);
|
);
|
||||||
|
|
||||||
static std::vector<NpcData> loadNpcsFromJson(const std::string& jsonPath, const std::string& zipPath = "");
|
static std::vector<NpcData> loadNpcsFromJson(const std::string& jsonPath, const std::string& zipPath = "");
|
||||||
|
|
||||||
|
private:
|
||||||
|
static nlohmann::json loadJson(const std::string& jsonPath, const std::string& zipPath);
|
||||||
|
static std::vector<GameObjectData> loadFromJson(const std::string& jsonPath, const std::string& zipPath);
|
||||||
|
static std::vector<InteractiveObjectData> loadInteractiveFromJson(const std::string& jsonPath, const std::string& zipPath);
|
||||||
|
static LoadedGameObject buildLoadedObject(const GameObjectData& data, Renderer& renderer, const std::string& zipPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZL
|
} // namespace ZL
|
||||||
|
|
||||||
|
|||||||
@ -11,26 +11,12 @@ namespace ZL {
|
|||||||
namespace ZL {
|
namespace ZL {
|
||||||
|
|
||||||
void InteractiveObject::draw(Renderer& renderer) const {
|
void InteractiveObject::draw(Renderer& renderer) const {
|
||||||
if (!isActive || !texture) return;
|
if (!isActive || !loadedObject.texture) return;
|
||||||
/*
|
|
||||||
std::cout << "[DRAW] InteractiveObject::draw() called" << std::endl;
|
|
||||||
std::cout << "[DRAW] Object: " << name << std::endl;
|
|
||||||
std::cout << "[DRAW] Position: (" << position.x() << ", " << position.y() << ", " << position.z() << ")" << std::endl;
|
|
||||||
|
|
||||||
// Check mesh bounds
|
|
||||||
if (!mesh.data.PositionData.empty()) {
|
|
||||||
std::cout << "[DRAW] First vertex: (" << mesh.data.PositionData[0].x() << ", "
|
|
||||||
<< mesh.data.PositionData[0].y() << ", " << mesh.data.PositionData[0].z() << ")" << std::endl;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
// Apply position transformation
|
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.TranslateMatrix(position);
|
renderer.TranslateMatrix(position);
|
||||||
|
|
||||||
renderer.RenderUniform1i(textureUniformName, 0);
|
renderer.RenderUniform1i(textureUniformName, 0);
|
||||||
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
|
glBindTexture(GL_TEXTURE_2D, loadedObject.texture->getTexID());
|
||||||
renderer.DrawVertexRenderStruct(mesh);
|
renderer.DrawVertexRenderStruct(loadedObject.mesh);
|
||||||
|
|
||||||
renderer.PopMatrix();
|
renderer.PopMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,42 +2,27 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <Eigen/Core>
|
#include <Eigen/Core>
|
||||||
#include "Item.h"
|
|
||||||
#include "render/Renderer.h"
|
#include "render/Renderer.h"
|
||||||
|
|
||||||
namespace ZL {
|
namespace ZL {
|
||||||
|
|
||||||
struct Texture;
|
|
||||||
struct VertexRenderStruct;
|
|
||||||
class Renderer;
|
class Renderer;
|
||||||
|
|
||||||
struct InteractiveObject {
|
struct LoadedGameObject {
|
||||||
std::string id;
|
|
||||||
std::string name;
|
|
||||||
Eigen::Vector3f position;
|
|
||||||
float interactionRadius;
|
|
||||||
std::shared_ptr<Texture> texture;
|
std::shared_ptr<Texture> texture;
|
||||||
VertexRenderStruct mesh;
|
VertexRenderStruct mesh;
|
||||||
Item dropItem;
|
std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InteractiveObject {
|
||||||
|
LoadedGameObject loadedObject; // name, texture, mesh
|
||||||
|
Eigen::Vector3f position;
|
||||||
|
float interactionRadius;
|
||||||
bool isActive = true;
|
bool isActive = true;
|
||||||
|
|
||||||
bool isNpc = false;
|
|
||||||
std::string npcInteractCallback;
|
|
||||||
float walkSpeed = 1.5f;
|
|
||||||
float rotationSpeed = 8.0f;
|
|
||||||
float modelScale = 0.01f;
|
|
||||||
std::string animationIdlePath;
|
|
||||||
std::string animationWalkPath;
|
|
||||||
std::string texturePath;
|
|
||||||
|
|
||||||
std::string activateFunctionName;
|
std::string activateFunctionName;
|
||||||
|
|
||||||
InteractiveObject() : interactionRadius(2.0f) {}
|
InteractiveObject() : interactionRadius(2.0f) {}
|
||||||
|
|
||||||
bool isInRange(const Eigen::Vector3f& playerPos) const {
|
|
||||||
return (playerPos - position).norm() <= interactionRadius;
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw(Renderer& renderer) const;
|
void draw(Renderer& renderer) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
68
src/items/ItemRegistry.cpp
Normal file
68
src/items/ItemRegistry.cpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#include "ItemRegistry.h"
|
||||||
|
#include "utils/Utils.h"
|
||||||
|
#include "external/nlohmann/json.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace ZL {
|
||||||
|
|
||||||
|
ItemRegistry& ItemRegistry::instance() {
|
||||||
|
static ItemRegistry reg;
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ItemRegistry::loadFromJson(const std::string& jsonPath, const std::string& zipPath) {
|
||||||
|
std::string content;
|
||||||
|
try {
|
||||||
|
if (zipPath.empty()) {
|
||||||
|
content = readTextFile(jsonPath);
|
||||||
|
} else {
|
||||||
|
auto buf = readFileFromZIP(jsonPath, zipPath);
|
||||||
|
if (buf.empty()) {
|
||||||
|
std::cerr << "[ItemRegistry] Failed to read " << jsonPath << " from zip\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
content.assign(buf.begin(), buf.end());
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "[ItemRegistry] Failed to open " << jsonPath << ": " << e.what() << "\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
json j;
|
||||||
|
try {
|
||||||
|
j = json::parse(content);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "[ItemRegistry] JSON parse error in " << jsonPath << ": " << e.what() << "\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!j.contains("items") || !j["items"].is_array()) {
|
||||||
|
std::cerr << "[ItemRegistry] No 'items' array in " << jsonPath << "\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
items_.clear();
|
||||||
|
|
||||||
|
for (const auto& entry : j["items"]) {
|
||||||
|
std::string id = entry.value("id", "");
|
||||||
|
if (id.empty()) continue;
|
||||||
|
|
||||||
|
Item item(
|
||||||
|
id,
|
||||||
|
entry.value("name", "Unknown"),
|
||||||
|
entry.value("description", ""),
|
||||||
|
entry.value("icon", "")
|
||||||
|
);
|
||||||
|
items_[id] = std::move(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "[ItemRegistry] Loaded " << items_.size() << " items from " << jsonPath << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
const Item* ItemRegistry::findById(const std::string& id) const {
|
||||||
|
auto it = items_.find(id);
|
||||||
|
return it != items_.end() ? &it->second : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ZL
|
||||||
23
src/items/ItemRegistry.h
Normal file
23
src/items/ItemRegistry.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include "Item.h"
|
||||||
|
|
||||||
|
namespace ZL {
|
||||||
|
|
||||||
|
class ItemRegistry {
|
||||||
|
public:
|
||||||
|
static ItemRegistry& instance();
|
||||||
|
|
||||||
|
void loadFromJson(const std::string& jsonPath, const std::string& zipPath = "");
|
||||||
|
|
||||||
|
// Returns nullptr if not found.
|
||||||
|
const Item* findById(const std::string& id) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ItemRegistry() = default;
|
||||||
|
|
||||||
|
std::unordered_map<std::string, Item> items_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ZL
|
||||||
Loading…
Reference in New Issue
Block a user