Compare commits

...
Sign in to create a new pull request.

3 commits

16 changed files with 2217 additions and 14 deletions

6
.gitignore vendored
View file

@ -22,6 +22,12 @@ err.txt
out.txt out.txt
run_log.txt run_log.txt
# Debugging, Autonomous Testing & Screenshots
screenshots/
/*.png
/*.json
!assets/**
# OS / Editor files # OS / Editor files
.DS_Store .DS_Store
Thumbs.db Thumbs.db

View file

@ -37,6 +37,7 @@ add_executable(DoorNroll
src/hub/HubRoom.cpp src/hub/HubRoom.cpp
src/hub/RestRoom.cpp src/hub/RestRoom.cpp
src/core/SaveSystem.cpp src/core/SaveSystem.cpp
src/debug/DebugSystem.cpp
) )
target_include_directories(DoorNroll PRIVATE src) target_include_directories(DoorNroll PRIVATE src)

398
assets/models/Bed.fbx Normal file
View file

@ -0,0 +1,398 @@
; FBX 7.3.0 project file
; Created by the Blockbench FBX Exporter
; ----------------------------------------------------
;
FBXHeaderExtension: {
FBXHeaderVersion: 1003
FBXVersion: 7300
EncryptionType: 0
CreationTimeStamp: {
Version: 1000
Year: 2026
Month: 10
Day: 7
Hour: 18
Minute: 44
Second: 12
Millisecond: 852
}
Creator: "Blockbench 5.1.6"
SceneInfo: "SceneInfo::GlobalInfo", "UserData" {
Type: "UserData"
Version: 100
MetaData: {
Version: 100
Title: ""
Subject: ""
Author: ""
Keywords: ""
Revision: ""
Comment: ""
}
Properties70: {
P: "DocumentUrl", "KString", "Url", "", "C:\Users\Blockbench\foobar.fbx"
P: "SrcDocumentUrl", "KString", "Url", "", "C:\Users\Blockbench\foobar.fbx"
P: "Original", "Compound", "", ""
P: "Original|ApplicationVendor", "KString", "", "", "Blockbench"
P: "Original|ApplicationName", "KString", "", "", "Blockbench FBX Exporter"
P: "Original|ApplicationVersion", "KString", "", "", "5.1.6"
P: "Original|DateTime_GMT", "DateTime", "", "", "01/01/1970 00:00:00.000"
P: "Original|FileName", "KString", "", "", "/foobar.fbx"
P: "LastSaved", "Compound", "", ""
P: "LastSaved|ApplicationVendor", "KString", "", "", "Blockbench"
P: "LastSaved|ApplicationName", "KString", "", "", "Blockbench FBX Exporter"
P: "LastSaved|ApplicationVersion", "KString", "", "", "5.1.6"
P: "LastSaved|DateTime_GMT", "DateTime", "", "", "01/01/1970 00:00:00.000"
P: "Original|ApplicationNativeFile", "KString", "", "", ""
}
}
}
FileId: "iVFoobar"
CreationTime: "2026-10-07 15:44:12:852"
Creator: "Made with Blockbench"
GlobalSettings: {
Version: 1000
Properties70: {
P: "UpAxis", "int", "Integer", "",1
P: "UpAxisSign", "int", "Integer", "",1
P: "FrontAxis", "int", "Integer", "",2
P: "FrontAxisSign", "int", "Integer", "",1
P: "CoordAxis", "int", "Integer", "",0
P: "CoordAxisSign", "int", "Integer", "",1
P: "OriginalUpAxis", "int", "Integer", "",-1
P: "OriginalUpAxisSign", "int", "Integer", "",1
P: "UnitScaleFactor", "double", "Number", "",1
P: "OriginalUnitScaleFactor", "double", "Number", "",1
P: "AmbientColor", "ColorRGB", "Color", "",0,0,0
P: "DefaultCamera", "KString", "", "", "Producer Perspective"
P: "TimeMode", "enum", "", "",0
P: "TimeSpanStart", "KTime", "Time", "",0
P: "TimeSpanStop", "KTime", "Time", "",46186158000
P: "CustomFrameRate", "double", "Number", "",24
}
}
; Documents Description
;------------------------------------------------------------------
Documents: {
Count: 1
Document: 1588043550 {
Scene: "Scene"
Properties70: {
P: "SourceObject", "object", "", ""
P: "ActiveAnimStackName", "KString", "", "", ""
}
RootNode: 0
}
}
; Document References
;------------------------------------------------------------------
References:
; Object definitions
;------------------------------------------------------------------
Definitions: {
Version: 100
Count: 6
ObjectType: "GlobalSettings" {
Count: 1
}
ObjectType: "Model" {
Count: 1
PropertyTemplate: "FbxNode" {
Properties70: {
P: "QuaternionInterpolate", "enum", "", "",0
P: "RotationOffset", "Vector3D", "Vector", "",0,0,0
P: "RotationPivot", "Vector3D", "Vector", "",0,0,0
P: "ScalingOffset", "Vector3D", "Vector", "",0,0,0
P: "ScalingPivot", "Vector3D", "Vector", "",0,0,0
P: "TranslationActive", "bool", "", "",0
P: "TranslationMin", "Vector3D", "Vector", "",0,0,0
P: "TranslationMax", "Vector3D", "Vector", "",0,0,0
P: "TranslationMinX", "bool", "", "",0
P: "TranslationMinY", "bool", "", "",0
P: "TranslationMinZ", "bool", "", "",0
P: "TranslationMaxX", "bool", "", "",0
P: "TranslationMaxY", "bool", "", "",0
P: "TranslationMaxZ", "bool", "", "",0
P: "RotationOrder", "enum", "", "",5
P: "RotationSpaceForLimitOnly", "bool", "", "",0
P: "RotationStiffnessX", "double", "Number", "",0
P: "RotationStiffnessY", "double", "Number", "",0
P: "RotationStiffnessZ", "double", "Number", "",0
P: "AxisLen", "double", "Number", "",10
P: "PreRotation", "Vector3D", "Vector", "",0,0,0
P: "PostRotation", "Vector3D", "Vector", "",0,0,0
P: "RotationActive", "bool", "", "",0
P: "RotationMin", "Vector3D", "Vector", "",0,0,0
P: "RotationMax", "Vector3D", "Vector", "",0,0,0
P: "RotationMinX", "bool", "", "",0
P: "RotationMinY", "bool", "", "",0
P: "RotationMinZ", "bool", "", "",0
P: "RotationMaxX", "bool", "", "",0
P: "RotationMaxY", "bool", "", "",0
P: "RotationMaxZ", "bool", "", "",0
P: "InheritType", "enum", "", "",0
P: "ScalingActive", "bool", "", "",0
P: "ScalingMin", "Vector3D", "Vector", "",0,0,0
P: "ScalingMax", "Vector3D", "Vector", "",1,1,1
P: "ScalingMinX", "bool", "", "",0
P: "ScalingMinY", "bool", "", "",0
P: "ScalingMinZ", "bool", "", "",0
P: "ScalingMaxX", "bool", "", "",0
P: "ScalingMaxY", "bool", "", "",0
P: "ScalingMaxZ", "bool", "", "",0
P: "GeometricTranslation", "Vector3D", "Vector", "",0,0,0
P: "GeometricRotation", "Vector3D", "Vector", "",0,0,0
P: "GeometricScaling", "Vector3D", "Vector", "",1,1,1
P: "MinDampRangeX", "double", "Number", "",0
P: "MinDampRangeY", "double", "Number", "",0
P: "MinDampRangeZ", "double", "Number", "",0
P: "MaxDampRangeX", "double", "Number", "",0
P: "MaxDampRangeY", "double", "Number", "",0
P: "MaxDampRangeZ", "double", "Number", "",0
P: "MinDampStrengthX", "double", "Number", "",0
P: "MinDampStrengthY", "double", "Number", "",0
P: "MinDampStrengthZ", "double", "Number", "",0
P: "MaxDampStrengthX", "double", "Number", "",0
P: "MaxDampStrengthY", "double", "Number", "",0
P: "MaxDampStrengthZ", "double", "Number", "",0
P: "PreferedAngleX", "double", "Number", "",0
P: "PreferedAngleY", "double", "Number", "",0
P: "PreferedAngleZ", "double", "Number", "",0
P: "LookAtProperty", "object", "", ""
P: "UpVectorProperty", "object", "", ""
P: "Show", "bool", "", "",1
P: "NegativePercentShapeSupport", "bool", "", "",1
P: "DefaultAttributeIndex", "int", "Integer", "",-1
P: "Freeze", "bool", "", "",0
P: "LODBox", "bool", "", "",0
P: "Lcl Translation", "Lcl Translation", "", "A",0,0,0
P: "Lcl Rotation", "Lcl Rotation", "", "A",0,0,0
P: "Lcl Scaling", "Lcl Scaling", "", "A",1,1,1
P: "Visibility", "Visibility", "", "A",1
P: "Visibility Inheritance", "Visibility Inheritance", "", "",1
}
}
}
ObjectType: "Geometry" {
Count: 1
PropertyTemplate: "FbxMesh" {
Properties70: {
P: "Color", "ColorRGB", "Color", "",0.8,0.8,0.8
P: "BBoxMin", "Vector3D", "Vector", "",0,0,0
P: "BBoxMax", "Vector3D", "Vector", "",0,0,0
P: "Primary Visibility", "bool", "", "",1
P: "Casts Shadows", "bool", "", "",1
P: "Receive Shadows", "bool", "", "",1
}
}
}
ObjectType: "Material" {
Count: 1
PropertyTemplate: "FbxSurfaceLambert" {
Properties70: {
P: "ShadingModel", "KString", "", "", "Lambert"
P: "MultiLayer", "bool", "", "",0
P: "EmissiveColor", "Color", "", "A",0,0,0
P: "EmissiveFactor", "Number", "", "A",1
P: "AmbientColor", "Color", "", "A",0.2,0.2,0.2
P: "AmbientFactor", "Number", "", "A",1
P: "DiffuseColor", "Color", "", "A",0.8,0.8,0.8
P: "DiffuseFactor", "Number", "", "A",1
P: "Bump", "Vector3D", "Vector", "",0,0,0
P: "NormalMap", "Vector3D", "Vector", "",0,0,0
P: "BumpFactor", "double", "Number", "",1
P: "TransparentColor", "Color", "", "A",0,0,0
P: "TransparencyFactor", "Number", "", "A",0
P: "DisplacementColor", "ColorRGB", "Color", "",0,0,0
P: "DisplacementFactor", "double", "Number", "",1
P: "VectorDisplacementColor", "ColorRGB", "Color", "",0,0,0
P: "VectorDisplacementFactor", "double", "Number", "",1
}
}
}
ObjectType: "Texture" {
Count: 1
PropertyTemplate: "FbxFileTexture" {
Properties70: {
P: "TextureTypeUse", "enum", "", "",0
P: "Texture alpha", "Number", "", "A",1
P: "CurrentMappingType", "enum", "", "",0
P: "WrapModeU", "enum", "", "",0
P: "WrapModeV", "enum", "", "",0
P: "UVSwap", "bool", "", "",0
P: "PremultiplyAlpha", "bool", "", "",1
P: "Translation", "Vector", "", "A",0,0,0
P: "Rotation", "Vector", "", "A",0,0,0
P: "Scaling", "Vector", "", "A",1,1,1
P: "TextureRotationPivot", "Vector3D", "Vector", "",0,0,0
P: "TextureScalingPivot", "Vector3D", "Vector", "",0,0,0
P: "CurrentTextureBlendMode", "enum", "", "",1
P: "UVSet", "KString", "", "", "default"
P: "UseMaterial", "bool", "", "",0
P: "UseMipMap", "bool", "", "",0
}
}
}
ObjectType: "Video" {
Count: 1
PropertyTemplate: "FbxVideo" {
Properties70: {
P: "ImageSequence", "bool", "", "",0
P: "ImageSequenceOffset", "int", "Integer", "",0
P: "FrameRate", "double", "Number", "",0
P: "LastFrame", "int", "Integer", "",0
P: "Width", "int", "Integer", "",0
P: "Height", "int", "Integer", "",0
P: "Path", "KString", "XRefUrl", "", ""
P: "StartFrame", "int", "Integer", "",0
P: "StopFrame", "int", "Integer", "",0
P: "PlaySpeed", "double", "Number", "",0
P: "Offset", "KTime", "Time", "",0
P: "InterlaceMode", "enum", "", "",0
P: "FreeRunning", "bool", "", "",0
P: "Loop", "bool", "", "",0
P: "AccessMode", "enum", "", "",0
}
}
}
}
; Object properties
;------------------------------------------------------------------
Objects: {
Model: 70061392, "Model::mesh", "Mesh" {
Version: 232
Properties70: {
P: "RotationActive", "bool", "", "",1
P: "InheritType", "enum", "", "",1
P: "ScalingMax", "Vector3D", "Vector", "",0,0,0
P: "Lcl Translation", "Lcl Translation", "", "A",0,-3.125,-6.25
P: "RotationPivot", "Vector3D", "Vector", "",0,0,0
P: "Lcl Rotation", "Lcl Rotation", "", "A",0,0,0
P: "RotationOrder", "enum", "", "",5
P: "DefaultAttributeIndex", "int", "Integer", "",0
}
Shading: Y
Culling: "CullingOff"
}
Geometry: 79347176, "Geometry::mesh", "Mesh" {
Vertices: *240 {
a: 40.625,32.8125,40.625,40.625,25,-40.625,40.625,17.1875,40.625,40.625,17.1875,-40.625,-40.625,32.8125,40.625,-40.625,25,-40.625,-40.625,17.1875,40.625,-40.625,17.1875,-40.625,-37.5,15.625,-37.5,-37.5,15.625,-43.75,-37.5,3.125,-37.5,-37.5,3.125,-43.75,-43.75,15.625,-37.5,-43.75,15.625,-43.75,-43.75,3.125,-37.5,-43.75,3.125,-43.75,43.75,15.625,-37.5,43.75,15.625,-43.75,43.75,3.125,-37.5,43.75,3.125,-43.75,37.5,15.625,-37.5,37.5,15.625,-43.75,37.5,3.125,-37.5,37.5,3.125,-43.75,43.75,15.625,56.25,43.75,15.625,50,43.75,3.125,56.25,43.75,3.125,50,37.5,15.625,56.25,37.5,15.625,50,37.5,3.125,56.25,37.5,3.125,50,-37.5,15.625,56.25,-37.5,15.625,50,-37.5,3.125,56.25,-37.5,3.125,50,-43.75,15.625,56.25,-43.75,15.625,50,-43.75,3.125,56.25,-43.75,3.125,50,43.75,21.875,56.25,43.75,21.875,-43.75,43.75,15.625,56.25,43.75,15.625,-43.75,-43.75,21.875,56.25,-43.75,21.875,-43.75,-43.75,15.625,56.25,-43.75,15.625,-43.75,43.75,53.125,56.25,43.75,53.125,53.125,43.75,21.875,56.25,43.75,21.875,53.125,-43.75,53.125,56.25,-43.75,53.125,53.125,-43.75,21.875,56.25,-43.75,21.875,53.125,15.000000000000002,31.25,46.56249999999999,15.000000000000002,31.25,31.562500000000004,-15.000000000000002,31.25,46.56249999999999,-15.000000000000002,31.25,31.562500000000004,15.000000000000002,21.875,46.56249999999999,15.000000000000002,21.875,31.562500000000004,-15.000000000000002,21.875,46.56249999999999,-15.000000000000002,21.875,31.562500000000004,-25,26.5625,46.56249999999999,-25,26.5625,46.56249999999999,-25,26.5625,31.562500000000004,-25,26.5625,31.562500000000004,25,26.5625,46.56249999999999,25,26.5625,46.56249999999999,25,26.5625,31.562500000000004,25,26.5625,31.562500000000004,15.000000000000002,26.5625,26.5625,15.000000000000002,26.5625,26.5625,-15.000000000000002,26.5625,26.5625,-15.000000000000002,26.5625,26.5625,15.000000000000002,26.5625,51.5625,15.000000000000002,26.5625,51.5625,-15.000000000000002,26.5625,51.5625,-15.000000000000002,26.5625,51.5625
}
PolygonVertexIndex: *264 {
a: 1,0,2,-4,6,4,5,-8,4,0,1,-6,3,2,6,-8,2,0,4,-7,5,1,3,-8,9,8,10,-12,14,12,13,-16,12,8,9,-14,11,10,14,-16,10,8,12,-15,13,9,11,-16,17,16,18,-20,22,20,21,-24,20,16,17,-22,19,18,22,-24,18,16,20,-23,21,17,19,-24,25,24,26,-28,30,28,29,-32,28,24,25,-30,27,26,30,-32,26,24,28,-31,29,25,27,-32,33,32,34,-36,38,36,37,-40,36,32,33,-38,35,34,38,-40,34,32,36,-39,37,33,35,-40,41,40,42,-44,46,44,45,-48,44,40,41,-46,43,42,46,-48,42,40,44,-47,45,41,43,-48,49,48,50,-52,54,52,53,-56,52,48,49,-54,51,50,54,-56,50,48,52,-55,53,49,51,-56,69,68,71,-71,64,65,67,-67,56,57,59,-59,63,61,60,-63,76,77,79,-79,73,72,75,-75,57,56,69,-72,65,58,59,-68,77,56,58,-80,59,57,73,-76,68,60,61,-71,63,62,64,-67,62,60,76,-79,72,61,63,-75,72,73,70,-72,77,76,68,-70,75,74,66,-68,78,79,64,-66,60,68,-77,62,78,-65,61,72,-71,63,66,-75,56,77,-70,58,65,-80,57,71,-74,59,75,-68
}
GeometryVersion: 124
LayerElementNormal: 0 {
Version: 101
Name: ""
MappingInformationType: "ByPolygon"
ReferenceInformationType: "Direct"
Normals: *204 {
a: 1,0,0,-1,0,0,0,0.9954090291002811,-0.0957124066442578,0,-1,0,0,0,1,0,0,-1,1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1,1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1,1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1,1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1,1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1,1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,0,0,0,0,1,0,0,-1,0,0,0,0,0,0,0,0.42443387623071965,0.9054589359588684,0,-0.42443387623071965,0.9054589359588684,0,0,0.7295372041400855,0.6839411288813295,0,0.7295372041400855,-0.6839411288813295,0.42443387623071965,-0.9054589359588684,0,-0.42443387623071965,-0.9054589359588684,0,0,-0.7295372041400855,0.6839411288813295,0,-0.7295372041400855,-0.6839411288813295,0,0,0,0,0,0,0,0,0,0,0,0,0.3235735780316221,-0.6902902998007938,0.6471471560632436,-0.3235735780316221,-0.6902902998007938,0.6471471560632436,0.3235735780316221,-0.6902902998007938,-0.6471471560632436,-0.3235735780316221,-0.6902902998007938,-0.6471471560632436,0.3235735780316221,0.6902902998007938,0.6471471560632436,-0.3235735780316221,0.6902902998007938,0.6471471560632436,0.3235735780316221,0.6902902998007938,-0.6471471560632436,-0.3235735780316221,0.6902902998007938,-0.6471471560632436
}
}
LayerElementUV: 0 {
Version: 101
Name: ""
MappingInformationType: "ByPolygonVertex"
ReferenceInformationType: "Direct"
UV: *528 {
a: 0.640625,0.52734375,0.4375,0.546875,0.4375,0.5,0.640625,0.5,0.671875,0.953125,0.671875,1,0.46875,0.98046875,0.46875,0.953125,0.421875,0.734375,0.21875,0.734375,0.21875,0.53125,0.421875,0.53125,0.203125,0.53125,0.203125,0.734375,0,0.734375,0,0.53125,0.671875,0.890625,0.671875,0.9375,0.46875,0.9375,0.46875,0.890625,0.671875,0.453125,0.46875,0.453125,0.46875,0.4375,0.671875,0.4375,0.078125,0.375,0.0625,0.375,0.0625,0.34375,0.078125,0.34375,0.109375,0.34375,0.109375,0.375,0.09375,0.375,0.09375,0.34375,0.265625,0.5,0.28125,0.5,0.28125,0.515625,0.265625,0.515625,0.3125,0.5,0.3125,0.515625,0.296875,0.515625,0.296875,0.5,0.140625,0.34375,0.140625,0.375,0.125,0.375,0.125,0.34375,0.640625,0.875,0.625,0.875,0.625,0.84375,0.640625,0.84375,0.171875,0.375,0.15625,0.375,0.15625,0.34375,0.171875,0.34375,0.640625,0.796875,0.640625,0.828125,0.625,0.828125,0.625,0.796875,0.328125,0.5,0.34375,0.5,0.34375,0.515625,0.328125,0.515625,0.375,0.5,0.375,0.515625,0.359375,0.515625,0.359375,0.5,0.203125,0.34375,0.203125,0.375,0.1875,0.375,0.1875,0.34375,0.234375,0.375,0.21875,0.375,0.21875,0.34375,0.234375,0.34375,0.640625,0.78125,0.625,0.78125,0.625,0.75,0.640625,0.75,0.265625,0.34375,0.265625,0.375,0.25,0.375,0.25,0.34375,0.390625,0.5,0.40625,0.5,0.40625,0.515625,0.390625,0.515625,0.484375,0.359375,0.484375,0.375,0.46875,0.375,0.46875,0.359375,0.296875,0.34375,0.296875,0.375,0.28125,0.375,0.28125,0.34375,0.328125,0.375,0.3125,0.375,0.3125,0.34375,0.328125,0.34375,0.359375,0.375,0.34375,0.375,0.34375,0.34375,0.359375,0.34375,0.390625,0.34375,0.390625,0.375,0.375,0.375,0.375,0.34375,0.5,0.359375,0.515625,0.359375,0.515625,0.375,0.5,0.375,0.546875,0.359375,0.546875,0.375,0.53125,0.375,0.53125,0.359375,0.421875,0.34375,0.421875,0.375,0.40625,0.375,0.40625,0.34375,0.453125,0.375,0.4375,0.375,0.4375,0.34375,0.453125,0.34375,0.25,0.515625,0,0.515625,0,0.5,0.25,0.5,0.25,0.46875,0.25,0.484375,0,0.484375,0,0.46875,0,0.75,0.21875,0.75,0.21875,1,0,1,0.453125,0.75,0.453125,1,0.234375,1,0.234375,0.75,0.484375,0.46875,0.484375,0.484375,0.265625,0.484375,0.265625,0.46875,0.71875,0.484375,0.5,0.484375,0.5,0.46875,0.71875,0.46875,0.015625,0.375,0,0.375,0,0.296875,0.015625,0.296875,0.046875,0.296875,0.046875,0.375,0.03125,0.375,0.03125,0.296875,0,0.4375,0.21875,0.4375,0.21875,0.453125,0,0.453125,0.453125,0.4375,0.453125,0.453125,0.234375,0.453125,0.234375,0.4375,0.65625,0.65625,0.65625,0.734375,0.4375,0.734375,0.4375,0.65625,0.65625,0.640625,0.4375,0.640625,0.4375,0.5625,0.65625,0.5625,0,0.96875,0,0.96875,0,1,0,1,0,0.96875,0,0.96875,0,1,0,1,0.5765625,0.8375,0.5765625,0.875,0.5015625,0.875,0.5015625,0.8375,0.5015625,0.765625,0.5765625,0.765625,0.5765625,0.803125,0.5015625,0.803125,0.078125,1,0.078125,1,0,1,0,1,0.078125,1,0.078125,1,0,1,0,1,0.5765625,0.875,0.5765625,0.8375,0.6041734375,0.8374999999999999,0.6041734375,0.875,0.47395156250000003,0.8375,0.5015625,0.8375,0.5015625,0.875,0.47395156250000003,0.875,0.1078125,0.40474062499999997,0.1078125,0.421875,0.0328125,0.421875,0.0328125,0.40474062499999997,0.4203125,0.421875,0.3453125,0.421875,0.3453125,0.40474062499999997,0.4203125,0.40474062499999997,0.6041734375,0.803125,0.5765625,0.803125,0.5765625,0.765625,0.6041734375,0.765625,0.5015625,0.765625,0.5015625,0.803125,0.47395156250000003,0.803125,0.47395156250000003,0.765625,0.1890625,0.390625,0.2640625,0.390625,0.2640625,0.40775937500000003,0.1890625,0.40775937500000003,0.5015625,0.40775937500000003,0.5015625,0.390625,0.5765625,0.390625,0.5765625,0.40775937500000003,0,1,0,1,0,0.96875,0,0.96875,0,1,0,1,0.03125,1,0.03125,1,0.03125,1,0.03125,1,0,1,0,1,0,0.96875,0,0.96875,0,1,0,1,0.2640625,0.390625,0.29048438787957764,0.39863961954952054,0.2640625,0.407759580463386,0.18906250000000002,0.390625,0.1890625,0.407759580463386,0.16264061212042233,0.39863961954952054,0.5015625,0.390625,0.5015625,0.407759580463386,0.47514208911119205,0.3986401293578338,0.5765625,0.390625,0.602982910888808,0.3986401293578338,0.5765625,0.407759580463386,0.10781249999999999,0.421875,0.1078125,0.404740419536614,0.13423438787957767,0.41386038045047946,0.03281250000000001,0.421875,0.006390612120422329,0.41386038045047946,0.0328125,0.404740419536614,0.3453125,0.421875,0.31889208911119205,0.4138598706421662,0.3453125,0.404740419536614,0.4203125,0.421875,0.4203125,0.404740419536614,0.44673291088880795,0.4138598706421662
}
}
LayerElementMaterial: 0 {
Version: 101
Name: ""
MappingInformationType: "AllSame"
ReferenceInformationType: "IndexToDirect"
Materials: *1 {
a: 0
}
}
Layer: 0 {
Version: 100
LayerElement: {
Type: "LayerElementNormal"
TypedIndex: 0
}
LayerElement: {
Type: "LayerElementMaterial"
TypedIndex: 0
}
LayerElement: {
Type: "LayerElementUV"
TypedIndex: 0
}
}
}
Material: 77933162, "Material::Bed_textures", "" {
Version: 102
ShadingModel: "lambert"
MultiLayer: 0
Properties70: {
P: "Emissive", "Vector3D", "Vector", "",0,0,0
P: "Ambient", "Vector3D", "Vector", "",0.2,0.2,0.2
P: "Diffuse", "Vector3D", "Vector", "",0.8,0.8,0.8
P: "Opacity", "double", "Number", "",1
}
}
Texture: 77056915, "Texture::Bed_textures", "" {
Type: "TextureVideoClip"
Version: 202
TextureName: "Texture::Bed_textures"
Media: "Video::Bed_textures"
FileName: ""
RelativeFilename: ""
ModelUVTranslation: 0,0
ModelUVScaling: 1,1
Texture_Alpha_Source: "None"
Cropping: 0,0,0,0
}
Video: 74972089, "Video::Bed_textures", "Clip" {
Type: "Clip"
Properties70: {
P: "Path", "KString", "XRefUrl", "", "Bed_textures"
}
UseMipMap: 0
Filename: ""
RelativeFilename: ""
Content: "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAACxElEQVR4AeyZPY4UMRCFZ1oiIweJgAtwFJIVARkSdyBAe4AVAXdAIiNAm3AULkCABDkZQbOfpbeydtfTrmr/MJZH81Q9dj3bVX62u3uWzx9erx4cbj4eHpwb6u3378XFmotbUsGLpWBbpqYUtIlUwblbAirE4mqyWwIeXV8frXBFuEHqlgAtAYvdiMVV3S0BrtFWIM0EVEhqVpPW9Y9/VsNGp24KWDM+xHL3voGykuiWgJJB7Gnrv0/Am/dfjnsC3OJ2S8Ax48Pgh10C8Rbw6u27VYjLSUBtdFNAHNizJ08PQlzO9bBLgOCEn79/HQSVyQ67BOIt4Ounj0chLlcSStq7bS1IzAMa8vDgwM2F1T+3Xfkt2nwsFrLlIUa+8JC0B3BrIGyC2oBybI1B9GwzJKDnAHr3PRPQewZ6999cAezqHtRKVPMEeE4AOMMkoFYg3nabK8A70Fq8mYBamT2XdpsrwHMCwKmV0JAAPYrm2L0D0XNBjt3bVw5/0WOoxdIwr6mtgGeBkmThWH3df49zNgM6/Pb9x4rliRKb+k1da2z1534fwLoEdPDyxfPw5hYVnfpNnVU1+MOrhSIKQAkeEJSHBwduCRRRAErwgAA8PDhwS8CtADpnzXsAVxucxcIrDbcCGAhr3gO4rG0r4JXGEv8RkbpWp6l6yuXDCbAF+Z5Sj3xq24XX0Kc6UX0c5EP+qteJ8JAPZaoneH6nsFWf4lnLw50gQaagBlP1cbl8CTIF+eQsHfnWtGETpAMdLVb75+oq3ARZefJnudB/qxmnrxhBAXFBq2sdZSiFPlEEtjXuJUADy7WPLy/DXWCuv/xaB5rq714CUo6jls8ESJKyo850Kq6pgFRmzrXcOu6pAGvGRvOfChhtRq3xTAVYMzaa/1TAaDNqjWcqwJqx0fynAkabUWs8UwHWjI3mPxVw7jO6d/z/AAAA//+eL7gsAAAABklEQVQDAFQy4r0Mepw6AAAAAElFTkSuQmCC"
}
}
; Object connections
;------------------------------------------------------------------
Connections: {
;Model::mesh, Model::RootNode
C: "OO",70061392,0
;Geometry::mesh, Model::mesh
C: "OO",79347176,70061392
;Material::Bed_textures, Model::mesh
C: "OO",77933162,70061392
;Texture::Bed_textures, Material::Bed_textures
C: "OP",77056915,77933162, "DiffuseColor"
;Video::Bed_textures, Texture::Bed_textures
C: "OO",74972089,77056915
}
; Takes section
;------------------------------------------------------------------
Takes: {
Current: ""
}

3
assets/models/Bed.mtl Normal file
View file

@ -0,0 +1,3 @@
newmtl BedMaterial
Kd 1.0 1.0 1.0
map_Kd Bed_textures.png

544
assets/models/Bed.obj Normal file
View file

@ -0,0 +1,544 @@
mtllib Bed.mtl
o Bed
v 40.625000 29.687500 40.625000
v 40.625000 21.875000 -40.625000
v 40.625000 14.062500 40.625000
v 40.625000 14.062500 -40.625000
v -40.625000 29.687500 40.625000
v -40.625000 21.875000 -40.625000
v -40.625000 14.062500 40.625000
v -40.625000 14.062500 -40.625000
v -37.500000 12.500000 -37.500000
v -37.500000 12.500000 -43.750000
v -37.500000 0.000000 -37.500000
v -37.500000 0.000000 -43.750000
v -43.750000 12.500000 -37.500000
v -43.750000 12.500000 -43.750000
v -43.750000 0.000000 -37.500000
v -43.750000 0.000000 -43.750000
v 43.750000 12.500000 -37.500000
v 43.750000 12.500000 -43.750000
v 43.750000 0.000000 -37.500000
v 43.750000 0.000000 -43.750000
v 37.500000 12.500000 -37.500000
v 37.500000 12.500000 -43.750000
v 37.500000 0.000000 -37.500000
v 37.500000 0.000000 -43.750000
v 43.750000 12.500000 56.250000
v 43.750000 12.500000 50.000000
v 43.750000 0.000000 56.250000
v 43.750000 0.000000 50.000000
v 37.500000 12.500000 56.250000
v 37.500000 12.500000 50.000000
v 37.500000 0.000000 56.250000
v 37.500000 0.000000 50.000000
v -37.500000 12.500000 56.250000
v -37.500000 12.500000 50.000000
v -37.500000 0.000000 56.250000
v -37.500000 0.000000 50.000000
v -43.750000 12.500000 56.250000
v -43.750000 12.500000 50.000000
v -43.750000 0.000000 56.250000
v -43.750000 0.000000 50.000000
v 43.750000 18.750000 56.250000
v 43.750000 18.750000 -43.750000
v 43.750000 12.500000 56.250000
v 43.750000 12.500000 -43.750000
v -43.750000 18.750000 56.250000
v -43.750000 18.750000 -43.750000
v -43.750000 12.500000 56.250000
v -43.750000 12.500000 -43.750000
v 43.750000 50.000000 56.250000
v 43.750000 50.000000 53.125000
v 43.750000 18.750000 56.250000
v 43.750000 18.750000 53.125000
v -43.750000 50.000000 56.250000
v -43.750000 50.000000 53.125000
v -43.750000 18.750000 56.250000
v -43.750000 18.750000 53.125000
v 15.000000 28.125000 46.562500
v 15.000000 28.125000 31.562500
v -15.000000 28.125000 46.562500
v -15.000000 28.125000 31.562500
v 15.000000 18.750000 46.562500
v 15.000000 18.750000 31.562500
v -15.000000 18.750000 46.562500
v -15.000000 18.750000 31.562500
v -25.000000 23.437500 46.562500
v -25.000000 23.437500 46.562500
v -25.000000 23.437500 31.562500
v -25.000000 23.437500 31.562500
v 25.000000 23.437500 46.562500
v 25.000000 23.437500 46.562500
v 25.000000 23.437500 31.562500
v 25.000000 23.437500 31.562500
v 15.000000 23.437500 26.562500
v 15.000000 23.437500 26.562500
v -15.000000 23.437500 26.562500
v -15.000000 23.437500 26.562500
v 15.000000 23.437500 51.562500
v 15.000000 23.437500 51.562500
v -15.000000 23.437500 51.562500
v -15.000000 23.437500 51.562500
vt 0.640625 0.527344
vt 0.437500 0.546875
vt 0.437500 0.500000
vt 0.640625 0.500000
vt 0.671875 0.953125
vt 0.671875 1.000000
vt 0.468750 0.980469
vt 0.468750 0.953125
vt 0.421875 0.734375
vt 0.218750 0.734375
vt 0.218750 0.531250
vt 0.421875 0.531250
vt 0.203125 0.531250
vt 0.203125 0.734375
vt 0.000000 0.734375
vt 0.000000 0.531250
vt 0.671875 0.890625
vt 0.671875 0.937500
vt 0.468750 0.937500
vt 0.468750 0.890625
vt 0.671875 0.453125
vt 0.468750 0.453125
vt 0.468750 0.437500
vt 0.671875 0.437500
vt 0.078125 0.375000
vt 0.062500 0.375000
vt 0.062500 0.343750
vt 0.078125 0.343750
vt 0.109375 0.343750
vt 0.109375 0.375000
vt 0.093750 0.375000
vt 0.093750 0.343750
vt 0.265625 0.500000
vt 0.281250 0.500000
vt 0.281250 0.515625
vt 0.265625 0.515625
vt 0.312500 0.500000
vt 0.312500 0.515625
vt 0.296875 0.515625
vt 0.296875 0.500000
vt 0.140625 0.343750
vt 0.140625 0.375000
vt 0.125000 0.375000
vt 0.125000 0.343750
vt 0.640625 0.875000
vt 0.625000 0.875000
vt 0.625000 0.843750
vt 0.640625 0.843750
vt 0.171875 0.375000
vt 0.156250 0.375000
vt 0.156250 0.343750
vt 0.171875 0.343750
vt 0.640625 0.796875
vt 0.640625 0.828125
vt 0.625000 0.828125
vt 0.625000 0.796875
vt 0.328125 0.500000
vt 0.343750 0.500000
vt 0.343750 0.515625
vt 0.328125 0.515625
vt 0.375000 0.500000
vt 0.375000 0.515625
vt 0.359375 0.515625
vt 0.359375 0.500000
vt 0.203125 0.343750
vt 0.203125 0.375000
vt 0.187500 0.375000
vt 0.187500 0.343750
vt 0.234375 0.375000
vt 0.218750 0.375000
vt 0.218750 0.343750
vt 0.234375 0.343750
vt 0.640625 0.781250
vt 0.625000 0.781250
vt 0.625000 0.750000
vt 0.640625 0.750000
vt 0.265625 0.343750
vt 0.265625 0.375000
vt 0.250000 0.375000
vt 0.250000 0.343750
vt 0.390625 0.500000
vt 0.406250 0.500000
vt 0.406250 0.515625
vt 0.390625 0.515625
vt 0.484375 0.359375
vt 0.484375 0.375000
vt 0.468750 0.375000
vt 0.468750 0.359375
vt 0.296875 0.343750
vt 0.296875 0.375000
vt 0.281250 0.375000
vt 0.281250 0.343750
vt 0.328125 0.375000
vt 0.312500 0.375000
vt 0.312500 0.343750
vt 0.328125 0.343750
vt 0.359375 0.375000
vt 0.343750 0.375000
vt 0.343750 0.343750
vt 0.359375 0.343750
vt 0.390625 0.343750
vt 0.390625 0.375000
vt 0.375000 0.375000
vt 0.375000 0.343750
vt 0.500000 0.359375
vt 0.515625 0.359375
vt 0.515625 0.375000
vt 0.500000 0.375000
vt 0.546875 0.359375
vt 0.546875 0.375000
vt 0.531250 0.375000
vt 0.531250 0.359375
vt 0.421875 0.343750
vt 0.421875 0.375000
vt 0.406250 0.375000
vt 0.406250 0.343750
vt 0.453125 0.375000
vt 0.437500 0.375000
vt 0.437500 0.343750
vt 0.453125 0.343750
vt 0.250000 0.515625
vt 0.000000 0.515625
vt 0.000000 0.500000
vt 0.250000 0.500000
vt 0.250000 0.468750
vt 0.250000 0.484375
vt 0.000000 0.484375
vt 0.000000 0.468750
vt 0.000000 0.750000
vt 0.218750 0.750000
vt 0.218750 1.000000
vt 0.000000 1.000000
vt 0.453125 0.750000
vt 0.453125 1.000000
vt 0.234375 1.000000
vt 0.234375 0.750000
vt 0.484375 0.468750
vt 0.484375 0.484375
vt 0.265625 0.484375
vt 0.265625 0.468750
vt 0.718750 0.484375
vt 0.500000 0.484375
vt 0.500000 0.468750
vt 0.718750 0.468750
vt 0.015625 0.375000
vt 0.000000 0.375000
vt 0.000000 0.296875
vt 0.015625 0.296875
vt 0.046875 0.296875
vt 0.046875 0.375000
vt 0.031250 0.375000
vt 0.031250 0.296875
vt 0.000000 0.437500
vt 0.218750 0.437500
vt 0.218750 0.453125
vt 0.000000 0.453125
vt 0.453125 0.437500
vt 0.453125 0.453125
vt 0.234375 0.453125
vt 0.234375 0.437500
vt 0.656250 0.656250
vt 0.656250 0.734375
vt 0.437500 0.734375
vt 0.437500 0.656250
vt 0.656250 0.640625
vt 0.437500 0.640625
vt 0.437500 0.562500
vt 0.656250 0.562500
vt 0.000000 0.968750
vt 0.000000 0.968750
vt 0.000000 1.000000
vt 0.000000 1.000000
vt 0.000000 0.968750
vt 0.000000 0.968750
vt 0.000000 1.000000
vt 0.000000 1.000000
vt 0.576562 0.837500
vt 0.576562 0.875000
vt 0.501563 0.875000
vt 0.501563 0.837500
vt 0.501563 0.765625
vt 0.576562 0.765625
vt 0.576562 0.803125
vt 0.501563 0.803125
vt 0.078125 1.000000
vt 0.078125 1.000000
vt 0.000000 1.000000
vt 0.000000 1.000000
vt 0.078125 1.000000
vt 0.078125 1.000000
vt 0.000000 1.000000
vt 0.000000 1.000000
vt 0.576562 0.875000
vt 0.576562 0.837500
vt 0.604173 0.837500
vt 0.604173 0.875000
vt 0.473952 0.837500
vt 0.501563 0.837500
vt 0.501563 0.875000
vt 0.473952 0.875000
vt 0.107813 0.404741
vt 0.107813 0.421875
vt 0.032813 0.421875
vt 0.032813 0.404741
vt 0.420312 0.421875
vt 0.345313 0.421875
vt 0.345313 0.404741
vt 0.420312 0.404741
vt 0.604173 0.803125
vt 0.576562 0.803125
vt 0.576562 0.765625
vt 0.604173 0.765625
vt 0.501563 0.765625
vt 0.501563 0.803125
vt 0.473952 0.803125
vt 0.473952 0.765625
vt 0.189062 0.390625
vt 0.264062 0.390625
vt 0.264062 0.407759
vt 0.189062 0.407759
vt 0.501563 0.407759
vt 0.501563 0.390625
vt 0.576562 0.390625
vt 0.576562 0.407759
vt 0.000000 1.000000
vt 0.000000 1.000000
vt 0.000000 0.968750
vt 0.000000 0.968750
vt 0.000000 1.000000
vt 0.000000 1.000000
vt 0.031250 1.000000
vt 0.031250 1.000000
vt 0.031250 1.000000
vt 0.031250 1.000000
vt 0.000000 1.000000
vt 0.000000 1.000000
vt 0.000000 0.968750
vt 0.000000 0.968750
vt 0.000000 1.000000
vt 0.000000 1.000000
vt 0.264062 0.390625
vt 0.290484 0.398640
vt 0.264062 0.407760
vt 0.189063 0.390625
vt 0.189062 0.407760
vt 0.162641 0.398640
vt 0.501563 0.390625
vt 0.501563 0.407760
vt 0.475142 0.398640
vt 0.576562 0.390625
vt 0.602983 0.398640
vt 0.576562 0.407760
vt 0.107812 0.421875
vt 0.107813 0.404740
vt 0.134234 0.413860
vt 0.032813 0.421875
vt 0.006391 0.413860
vt 0.032813 0.404740
vt 0.345313 0.421875
vt 0.318892 0.413860
vt 0.345313 0.404740
vt 0.420312 0.421875
vt 0.420312 0.404740
vt 0.446733 0.413860
vn 1.000000 0.000000 -0.000000
vn -1.000000 0.000000 0.000000
vn 0.000000 0.995409 -0.095712
vn 0.000000 -1.000000 0.000000
vn 0.000000 -0.000000 1.000000
vn 0.000000 0.000000 -1.000000
vn 1.000000 0.000000 -0.000000
vn -1.000000 0.000000 0.000000
vn -0.000000 1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 -0.000000 1.000000
vn 0.000000 0.000000 -1.000000
vn 1.000000 0.000000 -0.000000
vn -1.000000 0.000000 0.000000
vn -0.000000 1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 -0.000000 1.000000
vn 0.000000 0.000000 -1.000000
vn 1.000000 0.000000 -0.000000
vn -1.000000 0.000000 0.000000
vn -0.000000 1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 -0.000000 1.000000
vn 0.000000 0.000000 -1.000000
vn 1.000000 0.000000 -0.000000
vn -1.000000 0.000000 0.000000
vn -0.000000 1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 -0.000000 1.000000
vn 0.000000 0.000000 -1.000000
vn 1.000000 0.000000 -0.000000
vn -1.000000 0.000000 0.000000
vn -0.000000 1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 -0.000000 1.000000
vn 0.000000 0.000000 -1.000000
vn 1.000000 0.000000 -0.000000
vn -1.000000 0.000000 0.000000
vn -0.000000 1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 -0.000000 1.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 0.424434 0.905459 -0.000000
vn -0.424434 0.905459 0.000000
vn 0.000000 0.729537 0.683941
vn 0.000000 0.729537 -0.683941
vn 0.424434 -0.905459 0.000000
vn -0.424434 -0.905459 0.000000
vn 0.000000 -0.729537 0.683941
vn 0.000000 -0.729537 -0.683941
vn 0.000000 1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 0.323574 -0.690290 0.647147
vn -0.323574 -0.690290 0.647147
vn 0.323574 -0.690290 -0.647147
vn -0.323574 -0.690290 -0.647147
vn 0.323574 0.690290 0.647147
vn -0.323574 0.690290 0.647147
vn 0.323574 0.690290 -0.647147
vn -0.323574 0.690290 -0.647147
usemtl BedMaterial
s 1
f 2/1/1 1/2/1 3/3/1
f 2/1/1 3/3/1 4/4/1
f 7/5/2 5/6/2 6/7/2
f 7/5/2 6/7/2 8/8/2
f 5/9/3 1/10/3 2/11/3
f 5/9/3 2/11/3 6/12/3
f 4/13/4 3/14/4 7/15/4
f 4/13/4 7/15/4 8/16/4
f 3/17/5 1/18/5 5/19/5
f 3/17/5 5/19/5 7/20/5
f 6/21/6 2/22/6 4/23/6
f 6/21/6 4/23/6 8/24/6
f 10/25/7 9/26/7 11/27/7
f 10/25/7 11/27/7 12/28/7
f 15/29/8 13/30/8 14/31/8
f 15/29/8 14/31/8 16/32/8
f 13/33/9 9/34/9 10/35/9
f 13/33/9 10/35/9 14/36/9
f 12/37/10 11/38/10 15/39/10
f 12/37/10 15/39/10 16/40/10
f 11/41/11 9/42/11 13/43/11
f 11/41/11 13/43/11 15/44/11
f 14/45/12 10/46/12 12/47/12
f 14/45/12 12/47/12 16/48/12
f 18/49/13 17/50/13 19/51/13
f 18/49/13 19/51/13 20/52/13
f 23/53/14 21/54/14 22/55/14
f 23/53/14 22/55/14 24/56/14
f 21/57/15 17/58/15 18/59/15
f 21/57/15 18/59/15 22/60/15
f 20/61/16 19/62/16 23/63/16
f 20/61/16 23/63/16 24/64/16
f 19/65/17 17/66/17 21/67/17
f 19/65/17 21/67/17 23/68/17
f 22/69/18 18/70/18 20/71/18
f 22/69/18 20/71/18 24/72/18
f 26/73/19 25/74/19 27/75/19
f 26/73/19 27/75/19 28/76/19
f 31/77/20 29/78/20 30/79/20
f 31/77/20 30/79/20 32/80/20
f 29/81/21 25/82/21 26/83/21
f 29/81/21 26/83/21 30/84/21
f 28/85/22 27/86/22 31/87/22
f 28/85/22 31/87/22 32/88/22
f 27/89/23 25/90/23 29/91/23
f 27/89/23 29/91/23 31/92/23
f 30/93/24 26/94/24 28/95/24
f 30/93/24 28/95/24 32/96/24
f 34/97/25 33/98/25 35/99/25
f 34/97/25 35/99/25 36/100/25
f 39/101/26 37/102/26 38/103/26
f 39/101/26 38/103/26 40/104/26
f 37/105/27 33/106/27 34/107/27
f 37/105/27 34/107/27 38/108/27
f 36/109/28 35/110/28 39/111/28
f 36/109/28 39/111/28 40/112/28
f 35/113/29 33/114/29 37/115/29
f 35/113/29 37/115/29 39/116/29
f 38/117/30 34/118/30 36/119/30
f 38/117/30 36/119/30 40/120/30
f 42/121/31 41/122/31 43/123/31
f 42/121/31 43/123/31 44/124/31
f 47/125/32 45/126/32 46/127/32
f 47/125/32 46/127/32 48/128/32
f 45/129/33 41/130/33 42/131/33
f 45/129/33 42/131/33 46/132/33
f 44/133/34 43/134/34 47/135/34
f 44/133/34 47/135/34 48/136/34
f 43/137/35 41/138/35 45/139/35
f 43/137/35 45/139/35 47/140/35
f 46/141/36 42/142/36 44/143/36
f 46/141/36 44/143/36 48/144/36
f 50/145/37 49/146/37 51/147/37
f 50/145/37 51/147/37 52/148/37
f 55/149/38 53/150/38 54/151/38
f 55/149/38 54/151/38 56/152/38
f 53/153/39 49/154/39 50/155/39
f 53/153/39 50/155/39 54/156/39
f 52/157/40 51/158/40 55/159/40
f 52/157/40 55/159/40 56/160/40
f 51/161/41 49/162/41 53/163/41
f 51/161/41 53/163/41 55/164/41
f 54/165/42 50/166/42 52/167/42
f 54/165/42 52/167/42 56/168/42
f 70/169/43 69/170/43 72/171/43
f 70/169/43 72/171/43 71/172/43
f 65/173/44 66/174/44 68/175/44
f 65/173/44 68/175/44 67/176/44
f 57/177/45 58/178/45 60/179/45
f 57/177/45 60/179/45 59/180/45
f 64/181/46 62/182/46 61/183/46
f 64/181/46 61/183/46 63/184/46
f 77/185/47 78/186/47 80/187/47
f 77/185/47 80/187/47 79/188/47
f 74/189/48 73/190/48 76/191/48
f 74/189/48 76/191/48 75/192/48
f 58/193/49 57/194/49 70/195/49
f 58/193/49 70/195/49 72/196/49
f 66/197/50 59/198/50 60/199/50
f 66/197/50 60/199/50 68/200/50
f 78/201/51 57/202/51 59/203/51
f 78/201/51 59/203/51 80/204/51
f 60/205/52 58/206/52 74/207/52
f 60/205/52 74/207/52 76/208/52
f 69/209/53 61/210/53 62/211/53
f 69/209/53 62/211/53 71/212/53
f 64/213/54 63/214/54 65/215/54
f 64/213/54 65/215/54 67/216/54
f 63/217/55 61/218/55 77/219/55
f 63/217/55 77/219/55 79/220/55
f 73/221/56 62/222/56 64/223/56
f 73/221/56 64/223/56 75/224/56
f 73/225/57 74/226/57 71/227/57
f 73/225/57 71/227/57 72/228/57
f 78/229/58 77/230/58 69/231/58
f 78/229/58 69/231/58 70/232/58
f 76/233/59 75/234/59 67/235/59
f 76/233/59 67/235/59 68/236/59
f 79/237/60 80/238/60 65/239/60
f 79/237/60 65/239/60 66/240/60
f 61/241/61 69/242/61 77/243/61
f 63/244/62 79/245/62 65/246/62
f 62/247/63 73/248/63 71/249/63
f 64/250/64 67/251/64 75/252/64
f 57/253/65 78/254/65 70/255/65
f 59/256/66 66/257/66 80/258/66
f 58/259/67 72/260/67 74/261/67
f 60/262/68 76/263/68 68/264/68

Binary file not shown.

After

Width:  |  Height:  |  Size: 783 B

View file

@ -0,0 +1,78 @@
#version 330
// Input vertex attributes (from vertex shader)
in vec3 fragPosition;
in vec2 fragTexCoord;
in vec4 fragColor;
in vec3 fragNormal;
// Input uniform values
uniform sampler2D texture0;
uniform vec4 colDiffuse;
// Output fragment color
out vec4 finalColor;
#define MAX_LIGHTS 4
#define LIGHT_DIRECTIONAL 0
#define LIGHT_POINT 1
struct Light {
int enabled;
int type;
vec3 position;
vec3 target;
vec4 color;
};
uniform Light lights[MAX_LIGHTS];
uniform vec4 ambient;
uniform vec3 viewPos;
void main()
{
vec4 texelColor = texture(texture0, fragTexCoord);
if (texelColor.a < 0.1) discard;
vec3 normal = normalize(fragNormal);
vec3 viewD = normalize(viewPos - fragPosition);
vec3 lightTotal = ambient.rgb;
vec3 specularTotal = vec3(0.0);
for (int i = 0; i < MAX_LIGHTS; i++)
{
if (lights[i].enabled == 1)
{
vec3 lightDir = vec3(0.0);
float attenuation = 1.0;
if (lights[i].type == LIGHT_DIRECTIONAL)
{
lightDir = -normalize(lights[i].target - lights[i].position);
}
else // LIGHT_POINT
{
vec3 lightVec = lights[i].position - fragPosition;
float dist = length(lightVec);
lightDir = normalize(lightVec);
attenuation = 1.0 / (1.0 + 0.10 * dist + 0.03 * dist * dist);
}
float NdotL = max(dot(normal, lightDir), 0.0);
lightTotal += lights[i].color.rgb * NdotL * attenuation;
if (NdotL > 0.0)
{
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewD, reflectDir), 0.0), 16.0);
specularTotal += lights[i].color.rgb * spec * attenuation * 0.25;
}
}
}
vec4 tint = colDiffuse * fragColor;
vec3 result = (texelColor.rgb * tint.rgb) * lightTotal + specularTotal;
finalColor = vec4(result, texelColor.a * tint.a);
}

View file

@ -0,0 +1,30 @@
#version 330
// Input vertex attributes
in vec3 vertexPosition;
in vec2 vertexTexCoord;
in vec3 vertexNormal;
in vec4 vertexColor;
// Input uniform values
uniform mat4 mvp;
uniform mat4 matModel;
uniform mat4 matNormal;
// Output vertex attributes (to fragment shader)
out vec3 fragPosition;
out vec2 fragTexCoord;
out vec4 fragColor;
out vec3 fragNormal;
void main()
{
// Send vertex attributes to fragment shader
fragPosition = vec3(matModel*vec4(vertexPosition, 1.0));
fragTexCoord = vertexTexCoord;
fragColor = vertexColor;
fragNormal = normalize(vec3(matNormal*vec4(vertexNormal, 1.0)));
// Calculate final vertex position
gl_Position = mvp*vec4(vertexPosition, 1.0);
}

View file

@ -0,0 +1,119 @@
#!/usr/bin/env python3
"""
Convert Blockbench ASCII FBX to standard OBJ with normals, UVs, and triangulation for Raylib.
Usage: python scripts/convert_blockbench_fbx.py <input.fbx> [output.obj]
"""
import sys
import os
import re
def convert_fbx_to_obj(fbx_path, obj_path=None):
if obj_path is None:
obj_path = os.path.splitext(fbx_path)[0] + ".obj"
mtl_path = os.path.splitext(obj_path)[0] + ".mtl"
obj_name = os.path.splitext(os.path.basename(obj_path))[0]
tex_name = obj_name + "_textures.png"
with open(fbx_path, "r", encoding="utf-8", errors="ignore") as f:
text = f.read()
# Vertices
v_match = re.search(r"Vertices:\s*\*\d+\s*\{\s*a:\s*([^}]+)\}", text)
if not v_match:
print(f"Error: No vertices found in {fbx_path}")
return False
raw_v = [float(x.strip()) for x in v_match.group(1).split(",")]
min_y = min(raw_v[1::3])
# Ground the model so bottom Y is 0.0
vertices = [(raw_v[i], raw_v[i+1] - min_y, raw_v[i+2]) for i in range(0, len(raw_v), 3)]
# Indices
p_match = re.search(r"PolygonVertexIndex:\s*\*\d+\s*\{\s*a:\s*([^}]+)\}", text)
if not p_match:
print(f"Error: No indices found in {fbx_path}")
return False
raw_p = [int(x.strip()) for x in p_match.group(1).split(",")]
# UVs
uv_match = re.search(r"UV:\s*\*\d+\s*\{\s*a:\s*([^}]+)\}", text)
raw_uv = [float(x.strip()) for x in uv_match.group(1).split(",")] if uv_match else []
uvs = [(raw_uv[i], raw_uv[i+1]) for i in range(0, len(raw_uv), 2)]
# Polygons
polygons_v = []
polygons_uv = []
cur_v = []
cur_uv = []
for i, idx in enumerate(raw_p):
uv_idx = i
if idx < 0:
cur_v.append(~idx)
cur_uv.append(uv_idx)
polygons_v.append(cur_v)
polygons_uv.append(cur_uv)
cur_v = []
cur_uv = []
else:
cur_v.append(idx)
cur_uv.append(uv_idx)
# Compute face normals
def calc_normal(p0, p1, p2):
ax, ay, az = p1[0]-p0[0], p1[1]-p0[1], p1[2]-p0[2]
bx, by, bz = p2[0]-p0[0], p2[1]-p0[1], p2[2]-p0[2]
nx = ay*bz - az*by
ny = az*bx - ax*bz
nz = ax*by - ay*bx
l = (nx*nx + ny*ny + nz*nz)**0.5
if l < 1e-6:
return (0.0, 1.0, 0.0)
return (nx/l, ny/l, nz/l)
normals = []
triangles = []
for pv, puv in zip(polygons_v, polygons_uv):
p0 = vertices[pv[0]]
p1 = vertices[pv[1]]
p2 = vertices[pv[2]]
norm = calc_normal(p0, p1, p2)
normals.append(norm)
norm_idx = len(normals)
for k in range(1, len(pv) - 1):
tri = [
(pv[0], puv[0], norm_idx),
(pv[k], puv[k], norm_idx),
(pv[k+1], puv[k+1], norm_idx)
]
triangles.append(tri)
with open(obj_path, "w", encoding="utf-8") as out:
out.write(f"mtllib {os.path.basename(mtl_path)}\n")
out.write(f"o {obj_name}\n")
for v in vertices:
out.write(f"v {v[0]:.6f} {v[1]:.6f} {v[2]:.6f}\n")
for uv in uvs:
out.write(f"vt {uv[0]:.6f} {uv[1]:.6f}\n")
for n in normals:
out.write(f"vn {n[0]:.6f} {n[1]:.6f} {n[2]:.6f}\n")
out.write(f"usemtl {obj_name}Material\ns 1\n")
for tri in triangles:
face_str = " ".join(f"{vi+1}/{uvi+1}/{ni}" for vi, uvi, ni in tri)
out.write(f"f {face_str}\n")
with open(mtl_path, "w", encoding="utf-8") as out:
out.write(f"newmtl {obj_name}Material\n")
out.write("Kd 1.0 1.0 1.0\n")
out.write(f"map_Kd {tex_name}\n")
print(f"Successfully converted {fbx_path} -> {obj_path} & {mtl_path}")
return True
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: convert_blockbench_fbx.py <file.fbx> [output.obj]")
sys.exit(1)
convert_fbx_to_obj(sys.argv[1], sys.argv[2] if len(sys.argv) > 2 else None)

View file

@ -1,6 +1,7 @@
#include "Game.hpp" #include "Game.hpp"
#include "dungeon/DungeonGen.hpp" #include "dungeon/DungeonGen.hpp"
#include "core/SoundManager.hpp" #include "core/SoundManager.hpp"
#include "debug/DebugSystem.hpp"
#include <cstdlib> #include <cstdlib>
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
@ -19,6 +20,7 @@ void Game::init() {
m_renderer.setGraphicsQuality(SoundManager::get().getGraphicsQuality()); m_renderer.setGraphicsQuality(SoundManager::get().getGraphicsQuality());
m_state = GameState::MAIN_MENU; m_state = GameState::MAIN_MENU;
SoundManager::get().playTrack(MusicTrack::MENU_HUB); SoundManager::get().playTrack(MusicTrack::MENU_HUB);
DebugSystem::get().init(this);
} }
void Game::startNewRun() { void Game::startNewRun() {
@ -137,6 +139,8 @@ void Game::update(float dt) {
// Update dynamic music streaming engine every frame // Update dynamic music streaming engine every frame
SoundManager::get().updateMusic(dt); SoundManager::get().updateMusic(dt);
DebugSystem::get().update(dt);
switch (m_state) { switch (m_state) {
case GameState::MAIN_MENU: case GameState::MAIN_MENU:
updateMainMenu(dt); updateMainMenu(dt);
@ -1221,7 +1225,12 @@ void Game::draw() {
// Render screen transition wave on top of all 2D/3D elements // Render screen transition wave on top of all 2D/3D elements
m_transition.draw(); m_transition.draw();
// Render debug overlay if enabled
DebugSystem::get().drawOverlay();
EndDrawing(); EndDrawing();
DebugSystem::get().postDraw();
} }
} // namespace dnr } // namespace dnr

View file

@ -25,6 +25,9 @@ public:
void draw(); void draw();
bool shouldClose() const { return m_shouldQuit || WindowShouldClose(); } bool shouldClose() const { return m_shouldQuit || WindowShouldClose(); }
void requestQuit() { m_shouldQuit = true; }
friend class DebugSystem;
private: private:
void startNewRun(); void startNewRun();

749
src/debug/DebugSystem.cpp Normal file
View file

@ -0,0 +1,749 @@
#include "DebugSystem.hpp"
#include "core/Game.hpp"
#include "core/SoundManager.hpp"
#include "dungeon/DungeonGen.hpp"
#include "combat/Enemy.hpp"
#include <filesystem>
#include <fstream>
#include <iostream>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <queue>
#include <cmath>
#include <algorithm>
namespace dnr {
DebugSystem& DebugSystem::get() {
static DebugSystem instance;
return instance;
}
DebugSystem::DebugSystem() = default;
DebugSystem::~DebugSystem() = default;
void DebugSystem::parseArgs(int argc, char* argv[]) {
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i];
if (arg == "--bot") {
m_config.botEnabled = true;
} else if (arg == "--bot-duration" && i + 1 < argc) {
m_config.botDuration = std::stof(argv[++i]);
m_config.botEnabled = true;
} else if (arg == "--bot-max-steps" && i + 1 < argc) {
m_config.botMaxSteps = std::stoi(argv[++i]);
m_config.botEnabled = true;
} else if (arg == "--screenshot" && i + 1 < argc) {
m_config.singleScreenshotPath = argv[++i];
} else if (arg == "--screenshot-events") {
m_config.screenshotEvents = true;
} else if (arg == "--screenshot-dir" && i + 1 < argc) {
m_config.screenshotDir = argv[++i];
} else if (arg == "--state-dump" && i + 1 < argc) {
m_config.stateDumpPath = argv[++i];
} else if (arg == "--warp-state" && i + 1 < argc) {
m_config.warpState = argv[++i];
} else if (arg == "--floor" && i + 1 < argc) {
m_config.targetFloor = std::stoi(argv[++i]);
} else if (arg == "--god") {
m_config.godMode = true;
} else if (arg == "--turbo") {
m_config.turboMode = true;
} else if (arg == "--overlay") {
m_config.showOverlay = true;
}
}
}
void DebugSystem::init(Game* game) {
m_game = game;
try {
std::filesystem::create_directories(m_config.screenshotDir);
} catch (...) {}
if (m_config.turboMode) {
SetTargetFPS(0); // Unlocked framerate for ultra-fast simulation
}
}
void DebugSystem::toggleBot() {
m_config.botEnabled = !m_config.botEnabled;
SoundManager::get().playTumblerClick();
}
void DebugSystem::toggleGodMode() {
m_config.godMode = !m_config.godMode;
SoundManager::get().playPickup();
}
void DebugSystem::toggleOverlay() {
m_config.showOverlay = !m_config.showOverlay;
SoundManager::get().playTumblerClick();
}
void DebugSystem::handleHotkeys() {
if (IsKeyPressed(KEY_F1)) {
toggleOverlay();
}
if (IsKeyPressed(KEY_F2)) {
toggleGodMode();
}
if (IsKeyPressed(KEY_F3)) {
// Warp to next floor or instant win combat
if (m_game) {
if (m_game->m_state == GameState::COMBAT) {
m_game->m_combat.actionSpinAndShoot(m_game->m_player);
if (m_game->m_combat.getEnemy()) {
const_cast<Enemy*>(m_game->m_combat.getEnemy())->takeDamage(999, AmmoType::KINETIC);
}
} else if (m_game->m_state == GameState::EXPLORATION) {
m_game->nextFloor();
}
SoundManager::get().playSkillGreat();
}
}
if (IsKeyPressed(KEY_F4)) {
// Give resources
if (m_game) {
for (int t = 1; t <= 6; ++t) {
m_game->m_player.addAmmo(static_cast<AmmoType>(t), 5);
}
m_game->m_player.addTechScraps(100);
m_game->m_player.addPicks(5);
SoundManager::get().playPickup();
}
}
if (IsKeyPressed(KEY_F10)) {
toggleBot();
}
if (IsKeyPressed(KEY_F12)) {
captureScreenshot("");
}
}
void DebugSystem::applyWarp() {
if (!m_game || m_warpApplied || m_config.warpState.empty()) return;
m_warpApplied = true;
std::string w = m_config.warpState;
if (w == "HUB") {
m_game->startNewRun();
} else if (w == "HUB_BED") {
m_game->startNewRun();
m_game->m_player.setGridPosition(2, 4);
m_game->m_player.setFacing(Direction::EAST);
} else if (w == "HUB_BED_FRONT") {
m_game->startNewRun();
m_game->m_player.setGridPosition(4, 3);
m_game->m_player.setFacing(Direction::SOUTH);
} else if (w == "EXPLORATION") {
m_game->startNewRun();
if (m_config.targetFloor > 1) {
m_game->m_dungeon.setFloor(m_config.targetFloor);
}
m_game->enterCyberspace();
} else if (w == "REST_ROOM") {
m_game->startNewRun();
m_game->m_dungeon.setFloor(m_config.targetFloor);
m_game->enterRestRoom();
} else if (w == "COMBAT") {
m_game->startNewRun();
m_game->enterCyberspace();
auto enemy = std::make_unique<Enemy>(EnemyType::CYBER_DRONE, 1);
m_game->m_combat.startCombat(m_game->m_player, std::move(enemy));
m_game->m_state = GameState::COMBAT;
} else if (w == "HACKING") {
m_game->startNewRun();
m_game->enterCyberspace();
m_game->m_hacking.start(1);
m_game->m_state = GameState::HACKING;
}
}
void DebugSystem::update(float dt) {
if (!m_game) return;
m_frameCounter++;
if (m_frameCounter == 2 && !m_warpApplied) {
applyWarp();
}
handleHotkeys();
// God Mode replenishment
if (m_config.godMode) {
int maxH = m_game->m_player.getMaxHp();
int maxS = m_game->m_player.getMaxShield();
m_game->m_player.setStats(maxH, maxH, maxS, maxS, 99);
for (int t = 1; t <= 6; ++t) {
m_game->m_player.addAmmo(static_cast<AmmoType>(t), 2);
}
}
// State change detection for event screenshots
if (m_game->m_state != m_lastObservedState) {
if (m_config.screenshotEvents) {
std::string stateTag = "state_";
switch (m_game->m_state) {
case GameState::MAIN_MENU: stateTag += "menu"; break;
case GameState::HUB: stateTag += "hub"; break;
case GameState::EXPLORATION: stateTag += "explore"; break;
case GameState::COMBAT: stateTag += "combat"; break;
case GameState::HACKING: stateTag += "hacking"; break;
case GameState::REST_ROOM: stateTag += "rest"; break;
case GameState::VICTORY_LEVEL: stateTag += "victory"; break;
case GameState::GAME_OVER: stateTag += "gameover"; break;
default: stateTag += "other"; break;
}
captureEventScreenshot(stateTag);
}
m_lastObservedState = m_game->m_state;
}
// Bot automation
if (m_config.botEnabled) {
m_totalBotRunTime += dt;
updateBot(dt);
if (m_config.botDuration > 0.0f && m_totalBotRunTime >= m_config.botDuration) {
m_game->m_shouldQuit = true;
}
if (m_config.botMaxSteps > 0 && m_botStepCount >= m_config.botMaxSteps) {
m_game->m_shouldQuit = true;
}
}
// Continuous state dump
if (!m_config.stateDumpPath.empty() && (m_frameCounter % 15 == 0)) {
dumpStateToJson(m_config.stateDumpPath);
}
}
void DebugSystem::updateBot(float dt) {
if (!m_game || m_game->m_transition.isBusy()) return;
m_botActionTimer += dt;
float actionInterval = m_config.turboMode ? 0.02f : 0.15f;
if (m_botActionTimer < actionInterval) return;
m_botActionTimer = 0.0f;
m_botStepCount++;
switch (m_game->m_state) {
case GameState::MAIN_MENU:
m_game->startNewRun();
break;
case GameState::HUB:
executeBotHubStep();
break;
case GameState::EXPLORATION:
executeBotExplorationStep();
break;
case GameState::HACKING:
executeBotHackingStep();
break;
case GameState::COMBAT:
executeBotCombatStep();
break;
case GameState::REST_ROOM:
executeBotRestRoomStep();
break;
case GameState::VICTORY_LEVEL:
m_game->enterRestRoom();
break;
case GameState::GAME_OVER:
m_game->startNewRun();
break;
default:
break;
}
}
void DebugSystem::executeBotHubStep() {
if (!m_game) return;
if (m_game->m_hubRoom.isFacingPortal(m_game->m_player)) {
m_game->tryInteractHub();
return;
}
int px = m_game->m_player.getGridX();
int py = m_game->m_player.getGridY();
Direction facing = m_game->m_player.getFacing();
// Portal is at (3, 1) facing NORTH
if (py > 1) {
if (facing != Direction::NORTH) {
m_game->m_player.setFacing(Direction::NORTH);
} else {
m_game->tryMoveHub(0, -1);
}
} else if (px < 3) {
if (facing != Direction::EAST) {
m_game->m_player.setFacing(Direction::EAST);
} else {
m_game->tryMoveHub(1, 0);
}
} else if (px > 3) {
if (facing != Direction::WEST) {
m_game->m_player.setFacing(Direction::WEST);
} else {
m_game->tryMoveHub(-1, 0);
}
} else {
m_game->m_player.setFacing(Direction::NORTH);
}
}
void DebugSystem::executeBotRestRoomStep() {
if (!m_game) return;
if (m_game->m_restRoom.isFacingGateway(m_game->m_player)) {
m_game->tryInteractRestRoom();
return;
}
if (m_game->m_restRoom.isFacingRechargeMatrix(m_game->m_player) && !m_game->m_restRoom.isRecharged()) {
m_game->tryInteractRestRoom();
return;
}
int px = m_game->m_player.getGridX();
int py = m_game->m_player.getGridY();
// Path around fountain: (3, 4) -> (2, 4) -> (2, 1) -> (3, 1) facing NORTH
if (px == 3 && py == 4) {
m_game->tryMoveRestRoom(-1, 0); // Move to (2, 4)
} else if (px == 2 && py > 1) {
m_game->tryMoveRestRoom(0, -1); // Move north to (2, 1)
} else if (px == 2 && py == 1) {
m_game->tryMoveRestRoom(1, 0); // Move east to (3, 1)
} else if (px == 3 && py == 1) {
m_game->m_player.setFacing(Direction::NORTH);
m_game->tryInteractRestRoom();
}
}
void DebugSystem::executeBotExplorationStep() {
if (!m_game) return;
// Reset visited map on floor change
int curFloor = m_game->m_dungeon.getFloor();
if (m_lastDungeonFloor != curFloor) {
m_visitedDungeonTiles.clear();
m_lastDungeonFloor = curFloor;
}
int px = m_game->m_player.getGridX();
int py = m_game->m_player.getGridY();
m_visitedDungeonTiles.insert({ px, py });
// Handle implant / shrine modals
if (m_game->m_showImplantDraft) {
if (!m_game->m_draftChoices.empty()) {
m_game->m_player.addImplant(m_game->m_draftChoices[0]);
}
m_game->m_showImplantDraft = false;
return;
}
if (m_game->m_showShrineUI) {
m_game->m_showShrineUI = false;
return;
}
// If context prompt is available (door, mine, chest, exit), interact!
if (!m_game->m_contextPrompt.empty()) {
m_game->tryInteractDungeon();
return;
}
// BFS Pathfinding to find next actionable or unvisited target
int width = m_game->m_dungeon.getWidth();
int height = m_game->m_dungeon.getHeight();
std::vector<std::vector<int>> dist(height, std::vector<int>(width, -1));
std::vector<std::vector<std::pair<int, int>>> parent(height, std::vector<std::pair<int, int>>(width, { -1, -1 }));
std::queue<std::pair<int, int>> q;
q.push({ px, py });
dist[py][px] = 0;
int targetX = -1, targetY = -1;
bool foundTarget = false;
int dirs[4][2] = { {0, -1}, {1, 0}, {0, 1}, {-1, 0} };
while (!q.empty()) {
auto [cx, cy] = q.front();
q.pop();
// Priority 1: Unvisited walkable floor or door or exit portal
bool isCurrentStart = (cx == px && cy == py);
TileType tt = m_game->m_dungeon.getTile(cx, cy);
if (!isCurrentStart) {
if (tt == TileType::EXIT_PORTAL || tt == TileType::LOOT_CACHE ||
(tt == TileType::DOOR_LOCKED && m_visitedDungeonTiles.count({cx, cy}) == 0) ||
(m_game->m_dungeon.isWalkable(cx, cy) && m_visitedDungeonTiles.count({cx, cy}) == 0)) {
targetX = cx;
targetY = cy;
foundTarget = true;
break;
}
}
for (int i = 0; i < 4; ++i) {
int nx = cx + dirs[i][0];
int ny = cy + dirs[i][1];
if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
if (dist[ny][nx] == -1) {
bool canTraverse = m_game->m_dungeon.isWalkable(nx, ny) || m_game->m_dungeon.isDoor(nx, ny);
if (canTraverse) {
dist[ny][nx] = dist[cy][cx] + 1;
parent[ny][nx] = { cx, cy };
q.push({ nx, ny });
}
}
}
}
}
if (!foundTarget) {
// Fallback: search for exit portal anywhere
for (int y = 0; y < height && !foundTarget; ++y) {
for (int x = 0; x < width && !foundTarget; ++x) {
if (m_game->m_dungeon.getTile(x, y) == TileType::EXIT_PORTAL) {
targetX = x;
targetY = y;
foundTarget = true;
}
}
}
}
if (foundTarget && (targetX != px || targetY != py)) {
// Backtrack to get the immediate next step from (px, py)
int currX = targetX;
int currY = targetY;
while (parent[currY][currX].first != -1 &&
(parent[currY][currX].first != px || parent[currY][currX].second != py)) {
auto p = parent[currY][currX];
currX = p.first;
currY = p.second;
}
int stepX = currX;
int stepY = currY;
int dx = stepX - px;
int dy = stepY - py;
Direction desiredDir = Direction::NORTH;
if (dx == 1) desiredDir = Direction::EAST;
else if (dx == -1) desiredDir = Direction::WEST;
else if (dy == 1) desiredDir = Direction::SOUTH;
else if (dy == -1) desiredDir = Direction::NORTH;
if (m_game->m_player.getFacing() == desiredDir) {
if (m_game->m_dungeon.isDoorLocked(stepX, stepY)) {
m_game->tryInteractDungeon();
} else {
m_game->tryMoveDungeon(dx, dy);
}
} else {
m_game->m_player.setFacing(desiredDir);
}
} else {
// Random wandering if completely stuck
Vector2 dirVec = getDirectionOffset(m_game->m_player.getFacing());
int frontX = px + static_cast<int>(dirVec.x);
int frontY = py + static_cast<int>(dirVec.y);
if (m_game->m_dungeon.isWalkable(frontX, frontY)) {
m_game->tryMoveDungeon(static_cast<int>(dirVec.x), static_cast<int>(dirVec.y));
} else {
m_game->m_player.turnRight();
}
}
}
void DebugSystem::executeBotHackingStep() {
if (!m_game) return;
if (m_game->m_hacking.getPhase() == HackingPhase::PHASE_1_TUMBLER) {
auto& tumbler = const_cast<TumblerLock&>(m_game->m_hacking.getTumbler());
int pinIdx = tumbler.getSelectedPin();
const auto& pin = tumbler.getPin(pinIdx);
if (pin.isInSweetSpot()) {
tumbler.tryLockCurrentPin();
if (!tumbler.isAllPinsLocked()) {
tumbler.moveRight();
}
} else if (pin.currentHeight < pin.targetHeight) {
tumbler.adjustHeight(0.08f);
} else {
tumbler.adjustHeight(-0.08f);
}
} else if (m_game->m_hacking.getPhase() == HackingPhase::PHASE_2_SKILLCHECK) {
auto& sc = const_cast<DbdSkillCheck&>(m_game->m_hacking.getSkillCheck());
float angle = sc.getNeedleAngle();
float gStart = sc.getGreatStart();
float gEnd = sc.getGreatEnd();
if (angle >= gStart && angle <= gEnd) {
sc.strike();
}
}
}
void DebugSystem::executeBotCombatStep() {
if (!m_game) return;
CombatPhase phase = m_game->m_combat.getPhase();
if (phase == CombatPhase::PLAYER_TURN) {
int hp = m_game->m_player.getHp();
int shield = m_game->m_player.getShield();
if (hp < 40 && shield < 10) {
m_game->m_combat.actionShield(m_game->m_player);
} else if (m_game->m_combat.getTripleCooldown() == 0) {
m_game->m_combat.actionTripleShot(m_game->m_player);
} else {
m_game->m_combat.actionSpinAndShoot(m_game->m_player);
}
} else if (phase == CombatPhase::VICTORY) {
bool wasElite = m_game->m_combat.wasElite();
m_game->m_player.heal(25);
m_game->m_player.addAmmo(AmmoType::KINETIC, 1);
m_game->m_player.addAmmo(AmmoType::EMP, 1);
m_game->m_state = GameState::EXPLORATION;
SoundManager::get().playTrack(MusicTrack::EXPLORATION, true);
if (wasElite) {
m_game->triggerImplantDraft();
}
} else if (phase == CombatPhase::DEFEAT) {
if (m_config.godMode) {
m_game->m_player.heal(100);
} else {
m_game->m_state = GameState::GAME_OVER;
}
}
}
void DebugSystem::drawOverlay() {
if (!m_config.showOverlay || !m_game) return;
// Strict Zero-Rounding tactical industrial diagnostic HUD
int boxX = 16;
int boxY = 16;
int boxW = 390;
int boxH = 220;
DrawRectangle(boxX, boxY, boxW, boxH, Color{ 6, 10, 16, 235 });
DrawRectangleLines(boxX, boxY, boxW, boxH, Color{ 70, 150, 255, 255 }); // Cobalt Blue border
DrawRectangle(boxX, boxY, boxW, 24, Color{ 16, 26, 42, 255 });
DrawRectangleLines(boxX, boxY, boxW, 24, Color{ 70, 150, 255, 255 });
DrawText("/// TACTICAL AGENT TELEMETRY ///", boxX + 10, boxY + 6, 12, TacticalColors::PureWhite);
int textY = boxY + 34;
int lineStep = 18;
std::string stateStr = "STATE: ";
switch (m_game->m_state) {
case GameState::MAIN_MENU: stateStr += "MAIN_MENU"; break;
case GameState::HUB: stateStr += "HUB_ROOM"; break;
case GameState::EXPLORATION: stateStr += "EXPLORATION"; break;
case GameState::COMBAT: stateStr += "COMBAT"; break;
case GameState::HACKING: stateStr += "HACKING_ICE"; break;
case GameState::REST_ROOM: stateStr += "REST_ROOM"; break;
case GameState::SETTINGS: stateStr += "SETTINGS"; break;
case GameState::PAUSE_MENU: stateStr += "PAUSE"; break;
case GameState::VICTORY_LEVEL: stateStr += "VICTORY"; break;
case GameState::GAME_OVER: stateStr += "GAME_OVER"; break;
default: stateStr += "UNKNOWN"; break;
}
DrawText(stateStr.c_str(), boxX + 12, textY, 13, TacticalColors::PureWhite);
textY += lineStep;
int fps = GetFPS();
std::string perfStr = "FPS: " + std::to_string(fps) + " | FRAME: " + std::to_string(m_frameCounter);
DrawText(perfStr.c_str(), boxX + 12, textY, 13, Color{ 180, 200, 220, 255 });
textY += lineStep;
const auto& p = m_game->m_player;
std::string playerStr = "HP: " + std::to_string(p.getHp()) + "/" + std::to_string(p.getMaxHp()) +
" | SHIELD: " + std::to_string(p.getShield()) + "/" + std::to_string(p.getMaxShield()) +
" | PICKS: " + std::to_string(p.getCyberPicks());
DrawText(playerStr.c_str(), boxX + 12, textY, 13, TacticalColors::PureWhite);
textY += lineStep;
std::string posStr = "GRID: (" + std::to_string(p.getGridX()) + ", " + std::to_string(p.getGridY()) + ") | FLOOR: " +
std::to_string(m_game->m_dungeon.getFloor());
DrawText(posStr.c_str(), boxX + 12, textY, 13, Color{ 245, 130, 32, 255 }); // Overheat Amber
textY += lineStep;
std::string botStr = m_config.botEnabled ? "[BOT: AUTO-PILOT ON]" : "[BOT: MANUAL]";
Color botColor = m_config.botEnabled ? Color{ 95, 185, 75, 255 } : Color{ 140, 150, 160, 255 };
DrawText(botStr.c_str(), boxX + 12, textY, 13, botColor);
std::string godStr = m_config.godMode ? "[GOD: ACTIVE]" : "[GOD: OFF]";
Color godColor = m_config.godMode ? Color{ 225, 25, 55, 255 } : Color{ 140, 150, 160, 255 };
DrawText(godStr.c_str(), boxX + 210, textY, 13, godColor);
textY += lineStep;
DrawText("F1: HUD | F2: GOD | F3: SKIP | F4: AMMO | F10: BOT | F12: CAPTURE",
boxX + 12, textY + 6, 10, Color{ 120, 140, 165, 255 });
}
void DebugSystem::postDraw() {
// Single screenshot capture on designated frame
if (!m_config.singleScreenshotPath.empty() && !m_singleScreenshotTaken &&
m_frameCounter >= m_config.singleScreenshotFrame) {
std::filesystem::path p(m_config.singleScreenshotPath);
if (p.has_parent_path()) {
std::filesystem::create_directories(p.parent_path());
}
TakeScreenshot(p.make_preferred().string().c_str());
m_singleScreenshotTaken = true;
if (m_game) {
m_game->m_shouldQuit = true;
}
}
// Pending event screenshot capture
if (!m_pendingScreenshotPath.empty()) {
std::filesystem::path p(m_pendingScreenshotPath);
if (p.has_parent_path()) {
std::filesystem::create_directories(p.parent_path());
}
TakeScreenshot(p.make_preferred().string().c_str());
m_pendingScreenshotPath.clear();
}
}
void DebugSystem::captureScreenshot(const std::string& customPath) {
if (!customPath.empty()) {
m_pendingScreenshotPath = customPath;
} else {
auto now = std::chrono::system_clock::now();
auto timeT = std::chrono::system_clock::to_time_t(now);
std::tm tmNow;
#if defined(_WIN32)
localtime_s(&tmNow, &timeT);
#else
localtime_r(&timeT, &tmNow);
#endif
std::ostringstream ss;
ss << m_config.screenshotDir << "/shot_"
<< std::put_time(&tmNow, "%Y%m%d_%H%M%S") << ".png";
m_pendingScreenshotPath = ss.str();
}
}
void DebugSystem::captureEventScreenshot(const std::string& eventTag) {
auto now = std::chrono::system_clock::now();
auto timeT = std::chrono::system_clock::to_time_t(now);
std::tm tmNow;
#if defined(_WIN32)
localtime_s(&tmNow, &timeT);
#else
localtime_r(&timeT, &tmNow);
#endif
std::ostringstream ss;
ss << m_config.screenshotDir << "/event_"
<< std::put_time(&tmNow, "%H%M%S") << "_" << eventTag << ".png";
m_pendingScreenshotPath = ss.str();
}
std::string DebugSystem::serializeStateJson() {
if (!m_game) return "{}";
std::ostringstream ss;
ss << "{\n";
ss << " \"frame\": " << m_frameCounter << ",\n";
ss << " \"time\": " << m_totalBotRunTime << ",\n";
std::string st = "UNKNOWN";
switch (m_game->m_state) {
case GameState::MAIN_MENU: st = "MAIN_MENU"; break;
case GameState::HUB: st = "HUB"; break;
case GameState::EXPLORATION: st = "EXPLORATION"; break;
case GameState::COMBAT: st = "COMBAT"; break;
case GameState::HACKING: st = "HACKING"; break;
case GameState::REST_ROOM: st = "REST_ROOM"; break;
case GameState::SETTINGS: st = "SETTINGS"; break;
case GameState::PAUSE_MENU: st = "PAUSE_MENU"; break;
case GameState::VICTORY_LEVEL: st = "VICTORY_LEVEL"; break;
case GameState::GAME_OVER: st = "GAME_OVER"; break;
default: break;
}
ss << " \"state\": \"" << st << "\",\n";
const auto& p = m_game->m_player;
ss << " \"player\": {\n";
ss << " \"hp\": " << p.getHp() << ",\n";
ss << " \"max_hp\": " << p.getMaxHp() << ",\n";
ss << " \"shield\": " << p.getShield() << ",\n";
ss << " \"max_shield\": " << p.getMaxShield() << ",\n";
ss << " \"picks\": " << p.getCyberPicks() << ",\n";
ss << " \"scraps\": " << p.getTechScraps() << ",\n";
ss << " \"grid_x\": " << p.getGridX() << ",\n";
ss << " \"grid_y\": " << p.getGridY() << "\n";
ss << " },\n";
ss << " \"dungeon\": {\n";
ss << " \"floor\": " << m_game->m_dungeon.getFloor() << ",\n";
ss << " \"visited_tiles\": " << m_visitedDungeonTiles.size() << "\n";
ss << " },\n";
bool inCombat = (m_game->m_state == GameState::COMBAT);
ss << " \"combat\": {\n";
ss << " \"active\": " << (inCombat ? "true" : "false");
if (inCombat && m_game->m_combat.getEnemy()) {
const auto* e = m_game->m_combat.getEnemy();
ss << ",\n";
ss << " \"enemy_name\": \"" << e->getName() << "\",\n";
ss << " \"enemy_hp\": " << e->getHp() << ",\n";
ss << " \"enemy_max_hp\": " << e->getMaxHp() << ",\n";
ss << " \"enemy_shield\": " << e->getShield() << "\n";
} else {
ss << "\n";
}
ss << " },\n";
ss << " \"bot\": {\n";
ss << " \"enabled\": " << (m_config.botEnabled ? "true" : "false") << ",\n";
ss << " \"steps\": " << m_botStepCount << ",\n";
ss << " \"god_mode\": " << (m_config.godMode ? "true" : "false") << "\n";
ss << " }\n";
ss << "}\n";
return ss.str();
}
void DebugSystem::dumpStateToJson(const std::string& path) {
std::string json = serializeStateJson();
std::ofstream out(path, std::ios::trunc);
if (out.is_open()) {
out << json;
}
}
} // namespace dnr

91
src/debug/DebugSystem.hpp Normal file
View file

@ -0,0 +1,91 @@
#pragma once
#include "core/Common.hpp"
#include <string>
#include <vector>
#include <set>
#include <memory>
namespace dnr {
class Game;
struct DebugConfig {
bool botEnabled = false;
float botDuration = 0.0f; // Auto-quit after N seconds (0 = infinite)
int botMaxSteps = 0; // Step limit (0 = infinite)
bool godMode = false;
bool turboMode = false;
bool showOverlay = false;
bool screenshotEvents = false;
std::string singleScreenshotPath;
int singleScreenshotFrame = 8;
std::string screenshotDir = "screenshots";
std::string stateDumpPath;
std::string warpState; // "HUB", "EXPLORATION", "COMBAT", "REST_ROOM", "HACKING"
int targetFloor = 1;
};
class DebugSystem {
public:
static DebugSystem& get();
void parseArgs(int argc, char* argv[]);
const DebugConfig& getConfig() const { return m_config; }
DebugConfig& getConfig() { return m_config; }
void init(Game* game);
void update(float dt);
void drawOverlay();
void postDraw();
// Visual captures
void captureScreenshot(const std::string& customPath = "");
void captureEventScreenshot(const std::string& eventTag);
// State dump
void dumpStateToJson(const std::string& path);
// Controls
void toggleBot();
bool isBotEnabled() const { return m_config.botEnabled; }
void toggleGodMode();
bool isGodMode() const { return m_config.godMode; }
void toggleOverlay();
bool isOverlayVisible() const { return m_config.showOverlay; }
void handleHotkeys();
void applyWarp();
private:
DebugSystem();
~DebugSystem();
void updateBot(float dt);
void executeBotExplorationStep();
void executeBotCombatStep();
void executeBotHackingStep();
void executeBotHubStep();
void executeBotRestRoomStep();
std::string serializeStateJson();
Game* m_game = nullptr;
DebugConfig m_config;
// Bot telemetry
float m_botActionTimer = 0.0f;
float m_totalBotRunTime = 0.0f;
int m_botStepCount = 0;
GameState m_lastObservedState = GameState::MAIN_MENU;
std::set<std::pair<int, int>> m_visitedDungeonTiles;
int m_lastDungeonFloor = -1;
// Screenshot & state dump tracking
int m_frameCounter = 0;
bool m_singleScreenshotTaken = false;
bool m_warpApplied = false;
std::string m_pendingScreenshotPath;
};
} // namespace dnr

View file

@ -1,14 +1,25 @@
#include "raylib.h" #include "raylib.h"
#include "core/Game.hpp" #include "core/Game.hpp"
#include "debug/DebugSystem.hpp"
int main(int argc, char* argv[]) {
dnr::DebugSystem::get().parseArgs(argc, argv);
const auto& debugConfig = dnr::DebugSystem::get().getConfig();
int main() {
const int screenWidth = 1280; const int screenWidth = 1280;
const int screenHeight = 720; const int screenHeight = 720;
SetConfigFlags(FLAG_VSYNC_HINT); if (!debugConfig.turboMode) {
SetConfigFlags(FLAG_VSYNC_HINT);
}
InitWindow(screenWidth, screenHeight, "DoorNroll // Cyber-Revolver Cyberspace Crawler"); InitWindow(screenWidth, screenHeight, "DoorNroll // Cyber-Revolver Cyberspace Crawler");
SetExitKey(KEY_NULL); // Prevent ESC from closing the game window SetExitKey(KEY_NULL); // Prevent ESC from closing the game window
SetTargetFPS(60);
if (debugConfig.turboMode) {
SetTargetFPS(0);
} else {
SetTargetFPS(60);
}
dnr::Game game; dnr::Game game;
game.init(); game.init();

View file

@ -1228,12 +1228,92 @@ void CyberRenderer::generateTextures() {
m_texAncientCeil = createAncientCeilTexture(); m_texAncientCeil = createAncientCeilTexture();
m_texAncientWood = createAncientWoodTexture(); m_texAncientWood = createAncientWoodTexture();
// Load custom 3D model dynamic lighting shader
std::vector<std::pair<std::string, std::string>> lightingShaderPaths = {
{ "assets/shaders/lighting.vs", "assets/shaders/lighting.fs" },
{ "../assets/shaders/lighting.vs", "../assets/shaders/lighting.fs" },
{ "shaders/lighting.vs", "shaders/lighting.fs" }
};
m_modelLightingLoaded = false;
for (const auto& sp : lightingShaderPaths) {
if (FileExists(sp.first.c_str()) && FileExists(sp.second.c_str())) {
m_shaderModelLighting = LoadShader(sp.first.c_str(), sp.second.c_str());
if (m_shaderModelLighting.id != 0) {
m_shaderModelLighting.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(m_shaderModelLighting, "viewPos");
m_locLightViewPos = m_shaderModelLighting.locs[SHADER_LOC_VECTOR_VIEW];
m_locLightAmbient = GetShaderLocation(m_shaderModelLighting, "ambient");
for (int i = 0; i < 4; ++i) {
m_modelLights[i].enabled = GetShaderLocation(m_shaderModelLighting, TextFormat("lights[%d].enabled", i));
m_modelLights[i].type = GetShaderLocation(m_shaderModelLighting, TextFormat("lights[%d].type", i));
m_modelLights[i].position = GetShaderLocation(m_shaderModelLighting, TextFormat("lights[%d].position", i));
m_modelLights[i].target = GetShaderLocation(m_shaderModelLighting, TextFormat("lights[%d].target", i));
m_modelLights[i].color = GetShaderLocation(m_shaderModelLighting, TextFormat("lights[%d].color", i));
}
m_modelLightingLoaded = true;
break;
}
}
}
// Load custom imported 3D Bed model
std::vector<std::string> bedModelPaths = {
"assets/models/Bed.obj",
"../assets/models/Bed.obj",
"models/Bed.obj"
};
std::vector<std::string> bedTexPaths = {
"assets/models/Bed_textures.png",
"../assets/models/Bed_textures.png",
"models/Bed_textures.png"
};
m_bedModelLoaded = false;
for (const auto& mp : bedModelPaths) {
if (FileExists(mp.c_str())) {
m_bedModel = LoadModel(mp.c_str());
m_bedModelLoaded = true;
break;
}
}
if (m_bedModelLoaded) {
if (m_modelLightingLoaded) {
for (int i = 0; i < m_bedModel.materialCount; ++i) {
m_bedModel.materials[i].shader = m_shaderModelLighting;
}
}
for (const auto& tp : bedTexPaths) {
if (FileExists(tp.c_str())) {
m_texBedModel = LoadTexture(tp.c_str());
SetTextureFilter(m_texBedModel, TEXTURE_FILTER_POINT);
if (m_bedModel.materialCount > 0) {
m_bedModel.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = m_texBedModel;
}
break;
}
}
}
m_texturesLoaded = true; m_texturesLoaded = true;
} }
void CyberRenderer::unloadTextures() { void CyberRenderer::unloadTextures() {
if (!m_texturesLoaded) return; if (!m_texturesLoaded) return;
if (m_modelLightingLoaded) {
UnloadShader(m_shaderModelLighting);
m_modelLightingLoaded = false;
}
if (m_bedModelLoaded) {
UnloadModel(m_bedModel);
if (m_texBedModel.id != 0) {
UnloadTexture(m_texBedModel);
m_texBedModel.id = 0;
}
m_bedModelLoaded = false;
}
UnloadTexture(m_texDungeonWall); UnloadTexture(m_texDungeonWall);
UnloadTexture(m_texDungeonFloor); UnloadTexture(m_texDungeonFloor);
UnloadTexture(m_texDungeonCeil); UnloadTexture(m_texDungeonCeil);
@ -1904,6 +1984,60 @@ void CyberRenderer::renderCombat3D(const Dungeon& dungeon, const Player& player,
endPostProcess(animTime); endPostProcess(animTime);
} }
void CyberRenderer::updateHubModelLighting(const Camera3D& camera, float animTime) {
if (!m_modelLightingLoaded) return;
// Camera view position for specular highlights
float camPos[3] = { camera.position.x, camera.position.y, camera.position.z };
SetShaderValue(m_shaderModelLighting, m_locLightViewPos, camPos, SHADER_UNIFORM_VEC3);
// Cyber-noir ambient illumination (subtle slate blue-gray base)
float ambient[4] = { 0.18f, 0.18f, 0.22f, 1.0f };
SetShaderValue(m_shaderModelLighting, m_locLightAmbient, ambient, SHADER_UNIFORM_VEC4);
struct HubLight {
int enabled;
int type;
Vector3 pos;
Vector3 target;
float color[4];
};
float deskFlicker = 0.90f + 0.10f * std::sin(animTime * 6.0f);
float portalPulse = 0.75f + 0.25f * std::sin(animTime * 3.5f);
HubLight lights[4] = {
// Light 0: Megacity Skyline Window glow (East wall) - cool neon cobalt/violet
{ 1, 1, { 11.0f, 1.8f, 6.0f }, { 0, 0, 0 }, { 0.35f, 0.40f, 0.85f, 1.0f } },
// Light 1: Workstation CRT monitors (West desk) - cyan flicker
{ 1, 1, { 1.3f, 1.3f, 5.0f }, { 0, 0, 0 }, { 0.15f * deskFlicker, 0.55f * deskFlicker, 0.85f * deskFlicker, 1.0f } },
// Light 2: Cyberspace Gateway Portal (North wall) - emerald pulse
{ 1, 1, { 6.0f, 1.8f, 1.0f }, { 0, 0, 0 }, { 0.10f * portalPulse, 0.80f * portalPulse, 0.40f * portalPulse, 1.0f } },
// Light 3: Ceiling warm ambient lamp (Above room center/bed approach) - warm tungsten
{ 1, 1, { 8.5f, 2.7f, 8.5f }, { 0, 0, 0 }, { 0.70f, 0.58f, 0.42f, 1.0f } }
};
for (int i = 0; i < 4; ++i) {
if (m_modelLights[i].enabled != -1) {
SetShaderValue(m_shaderModelLighting, m_modelLights[i].enabled, &lights[i].enabled, SHADER_UNIFORM_INT);
}
if (m_modelLights[i].type != -1) {
SetShaderValue(m_shaderModelLighting, m_modelLights[i].type, &lights[i].type, SHADER_UNIFORM_INT);
}
if (m_modelLights[i].position != -1) {
float p[3] = { lights[i].pos.x, lights[i].pos.y, lights[i].pos.z };
SetShaderValue(m_shaderModelLighting, m_modelLights[i].position, p, SHADER_UNIFORM_VEC3);
}
if (m_modelLights[i].target != -1) {
float t[3] = { lights[i].target.x, lights[i].target.y, lights[i].target.z };
SetShaderValue(m_shaderModelLighting, m_modelLights[i].target, t, SHADER_UNIFORM_VEC3);
}
if (m_modelLights[i].color != -1) {
SetShaderValue(m_shaderModelLighting, m_modelLights[i].color, lights[i].color, SHADER_UNIFORM_VEC4);
}
}
}
void CyberRenderer::renderHub(const HubRoom& hub, const Player& player, float animTime) { void CyberRenderer::renderHub(const HubRoom& hub, const Player& player, float animTime) {
if (!m_texturesLoaded) { if (!m_texturesLoaded) {
generateTextures(); generateTextures();
@ -1920,6 +2054,8 @@ void CyberRenderer::renderHub(const HubRoom& hub, const Player& player, float an
m_camera.target = { eye.x + dirX, eye.y, eye.z + dirZ }; m_camera.target = { eye.x + dirX, eye.y, eye.z + dirZ };
updateHubModelLighting(m_camera, animTime);
BeginMode3D(m_camera); BeginMode3D(m_camera);
const float tileSize = 2.0f; const float tileSize = 2.0f;
@ -2091,19 +2227,27 @@ void CyberRenderer::renderHub(const HubRoom& hub, const Player& player, float an
Color{ 0, static_cast<unsigned char>(180 + ledPulse * 75), 255, 255 }); Color{ 0, static_cast<unsigned char>(180 + ledPulse * 75), 255, 255 });
// 8. BED (South-East corner: X in [8.5f, 11.2f], Z in [8.8f, 11.2f]) // 8. BED (South-East corner: X in [8.5f, 11.2f], Z in [8.8f, 11.2f])
Vector3 bedPos = { 9.8f, 0.30f, 10.0f }; if (m_bedModelLoaded) {
Vector3 bedSize = { 2.3f, 0.45f, 2.2f }; // Draw user custom imported 3D Bed model
DrawCube({ bedPos.x, 0.08f, bedPos.z }, bedSize.x + 0.1f, 0.16f, bedSize.z + 0.1f, Color{ 38, 26, 18, 255 }); Vector3 customBedPos = { 9.7f, 0.0f, 9.75f };
drawTexturedBox(m_texBedQuilt, bedPos, bedSize, 2.0f, 2.0f); float customBedScale = 0.024f;
DrawModelEx(m_bedModel, customBedPos, Vector3{ 0.0f, 1.0f, 0.0f }, 0.0f,
Vector3{ customBedScale, customBedScale, customBedScale }, WHITE);
} else {
Vector3 bedPos = { 9.8f, 0.30f, 10.0f };
Vector3 bedSize = { 2.3f, 0.45f, 2.2f };
DrawCube({ bedPos.x, 0.08f, bedPos.z }, bedSize.x + 0.1f, 0.16f, bedSize.z + 0.1f, Color{ 38, 26, 18, 255 });
drawTexturedBox(m_texBedQuilt, bedPos, bedSize, 2.0f, 2.0f);
// Bed Headboard against South wall // Bed Headboard against South wall
DrawCube({ bedPos.x, 0.75f, maxCoord - 0.08f }, bedSize.x + 0.1f, 1.1f, 0.10f, Color{ 38, 26, 18, 255 }); DrawCube({ bedPos.x, 0.75f, maxCoord - 0.08f }, bedSize.x + 0.1f, 1.1f, 0.10f, Color{ 38, 26, 18, 255 });
// Two soft pillows at head of bed // Two soft pillows at head of bed
Vector3 pillow1 = { 9.2f, 0.58f, 10.7f }; Vector3 pillow1 = { 9.2f, 0.58f, 10.7f };
Vector3 pillow2 = { 10.4f, 0.58f, 10.7f }; Vector3 pillow2 = { 10.4f, 0.58f, 10.7f };
DrawCube(pillow1, 0.9f, 0.12f, 0.50f, Color{ 160, 165, 180, 255 }); DrawCube(pillow1, 0.9f, 0.12f, 0.50f, Color{ 160, 165, 180, 255 });
DrawCube(pillow2, 0.9f, 0.12f, 0.50f, Color{ 160, 165, 180, 255 }); DrawCube(pillow2, 0.9f, 0.12f, 0.50f, Color{ 160, 165, 180, 255 });
}
// 9. CYBERSPACE GATEWAY DOOR (North wall at X = 6.0f, Z = minCoord) // 9. CYBERSPACE GATEWAY DOOR (North wall at X = 6.0f, Z = minCoord)
Vector3 gateFramePos = { 6.0f, roomHeight * 0.5f, minCoord + 0.05f }; Vector3 gateFramePos = { 6.0f, roomHeight * 0.5f, minCoord + 0.05f };

View file

@ -71,6 +71,23 @@ private:
Texture2D m_texWindowView{}; Texture2D m_texWindowView{};
Texture2D m_texCyberGate{}; Texture2D m_texCyberGate{};
// Custom 3D Models & Dynamic Lighting
Model m_bedModel{};
Texture2D m_texBedModel{};
bool m_bedModelLoaded = false;
Shader m_shaderModelLighting{};
bool m_modelLightingLoaded = false;
int m_locLightAmbient = -1;
int m_locLightViewPos = -1;
struct ModelLightLocs {
int enabled = -1;
int type = -1;
int position = -1;
int target = -1;
int color = -1;
} m_modelLights[4];
void updateHubModelLighting(const Camera3D& camera, float animTime);
// Ancient Palace Storeroom Procedural Textures // Ancient Palace Storeroom Procedural Textures
Texture2D m_texAncientWall{}; Texture2D m_texAncientWall{};
Texture2D m_texAncientFloor{}; Texture2D m_texAncientFloor{};