Compare commits

..

1 commit

18 changed files with 1071 additions and 2219 deletions

6
.gitignore vendored
View file

@ -22,12 +22,6 @@ 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

@ -33,11 +33,11 @@ add_executable(DoorNroll
src/hacking/DbdSkillCheck.cpp src/hacking/DbdSkillCheck.cpp
src/inventory/InventoryUI.cpp src/inventory/InventoryUI.cpp
src/render/CyberRenderer.cpp src/render/CyberRenderer.cpp
src/render/SoftwareRenderer.cpp
src/render/UIRenderer.cpp src/render/UIRenderer.cpp
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)

View file

@ -1,398 +0,0 @@
; 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: ""
}

View file

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

View file

@ -1,544 +0,0 @@
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.

Before

Width:  |  Height:  |  Size: 783 B

View file

@ -1,78 +0,0 @@
#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

@ -1,30 +0,0 @@
#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

@ -1,119 +0,0 @@
#!/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,7 +1,6 @@
#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>
@ -20,7 +19,6 @@ 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() {
@ -139,8 +137,6 @@ 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);
@ -1225,12 +1221,7 @@ 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

@ -6,7 +6,7 @@
#include "hacking/HackingGame.hpp" #include "hacking/HackingGame.hpp"
#include "combat/CombatSystem.hpp" #include "combat/CombatSystem.hpp"
#include "inventory/InventoryUI.hpp" #include "inventory/InventoryUI.hpp"
#include "render/CyberRenderer.hpp" #include "render/SoftwareRenderer.hpp"
#include "render/UIRenderer.hpp" #include "render/UIRenderer.hpp"
#include "render/ScreenTransition.hpp" #include "render/ScreenTransition.hpp"
#include "hub/HubRoom.hpp" #include "hub/HubRoom.hpp"
@ -25,9 +25,6 @@ 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();
@ -66,7 +63,7 @@ private:
CombatSystem m_combat; CombatSystem m_combat;
InventoryUI m_inventoryUI; InventoryUI m_inventoryUI;
CyberRenderer m_renderer; SoftwareRenderer m_renderer;
UIRenderer m_uiRenderer; UIRenderer m_uiRenderer;
ScreenTransition m_transition; ScreenTransition m_transition;

View file

@ -1,749 +0,0 @@
#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

View file

@ -1,91 +0,0 @@
#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,25 +1,14 @@
#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;
if (!debugConfig.turboMode) {
SetConfigFlags(FLAG_VSYNC_HINT); 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
if (debugConfig.turboMode) {
SetTargetFPS(0);
} else {
SetTargetFPS(60); SetTargetFPS(60);
}
dnr::Game game; dnr::Game game;
game.init(); game.init();

View file

@ -1228,92 +1228,12 @@ 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);
@ -1984,60 +1904,6 @@ 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();
@ -2054,8 +1920,6 @@ 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;
@ -2227,13 +2091,6 @@ 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])
if (m_bedModelLoaded) {
// Draw user custom imported 3D Bed model
Vector3 customBedPos = { 9.7f, 0.0f, 9.75f };
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 bedPos = { 9.8f, 0.30f, 10.0f };
Vector3 bedSize = { 2.3f, 0.45f, 2.2f }; 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 }); DrawCube({ bedPos.x, 0.08f, bedPos.z }, bedSize.x + 0.1f, 0.16f, bedSize.z + 0.1f, Color{ 38, 26, 18, 255 });
@ -2247,7 +2104,6 @@ void CyberRenderer::renderHub(const HubRoom& hub, const Player& player, float an
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,23 +71,6 @@ 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{};

View file

@ -0,0 +1,941 @@
#include "SoftwareRenderer.hpp"
#include "hub/HubRoom.hpp"
#include "hub/RestRoom.hpp"
#include "combat/CombatSystem.hpp"
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <iostream>
namespace dnr {
SoftwareRenderer::SoftwareRenderer() = default;
SoftwareRenderer::~SoftwareRenderer() {
cleanup();
}
void SoftwareRenderer::init() {
if (m_initialized) return;
m_framebuffer.resize(FB_WIDTH * FB_HEIGHT, Color{ 6, 8, 12, 255 });
m_depthBuffer.resize(FB_WIDTH, 1000.0f);
m_screenImage.data = m_framebuffer.data();
m_screenImage.width = FB_WIDTH;
m_screenImage.height = FB_HEIGHT;
m_screenImage.mipmaps = 1;
m_screenImage.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
m_screenTexture = LoadTextureFromImage(m_screenImage);
SetTextureFilter(m_screenTexture, TEXTURE_FILTER_POINT);
generateCpuTextures();
m_initialized = true;
}
void SoftwareRenderer::cleanup() {
if (m_initialized) {
UnloadTexture(m_screenTexture);
m_initialized = false;
}
}
void SoftwareRenderer::triggerGlitch(float intensity) {
m_glitchTimer = std::max(m_glitchTimer, intensity);
}
// ----------------------------------------------------------------------------
// Texture Generation & Loading
// ----------------------------------------------------------------------------
void SoftwareRenderer::generateCpuTextures() {
auto makeTex = [](CpuTexture& t, int w = TEX_SIZE, int h = TEX_SIZE) {
t.width = w;
t.height = h;
t.pixels.resize(w * h, Color{ 30, 30, 30, 255 });
};
// 1. Dungeon Wall: Dark tactical gunmetal panel with reinforced seams & rivets
makeTex(m_texDungeonWall);
for (int y = 0; y < TEX_SIZE; ++y) {
for (int x = 0; x < TEX_SIZE; ++x) {
bool isBorder = (x < 4 || x > 251 || y < 4 || y > 251 || x == 127 || x == 128 || y == 127 || y == 128);
bool isRivet = ((x == 12 || x == 243 || x == 118 || x == 137) && (y % 32 >= 14 && y % 32 <= 17));
int noise = ((x * 19 + y * 37) % 9) - 4;
int base = isBorder ? 18 : (isRivet ? 95 : 38 + noise);
m_texDungeonWall.pixels[y * TEX_SIZE + x] = Color{
(unsigned char)std::clamp(base - 4, 8, 255),
(unsigned char)std::clamp(base, 10, 255),
(unsigned char)std::clamp(base + 8, 14, 255),
255
};
}
}
// 2. Dungeon Floor: Industrial steel grating
makeTex(m_texDungeonFloor);
for (int y = 0; y < TEX_SIZE; ++y) {
for (int x = 0; x < TEX_SIZE; ++x) {
bool isGrate = (x % 16 <= 2 || y % 16 <= 2);
int base = isGrate ? 12 : 32 + ((x ^ y) % 7);
m_texDungeonFloor.pixels[y * TEX_SIZE + x] = Color{
(unsigned char)base,
(unsigned char)(base + 3),
(unsigned char)(base + 7),
255
};
}
}
// 3. Dungeon Ceiling: Metallic beam ribs with dim red emergency light strip
makeTex(m_texDungeonCeil);
for (int y = 0; y < TEX_SIZE; ++y) {
for (int x = 0; x < TEX_SIZE; ++x) {
bool isStrip = (x >= 124 && x <= 131 && y % 64 >= 20 && y % 64 <= 44);
int base = ((y % 32 < 4) ? 14 : 26) + ((x * 13) % 5);
if (isStrip) {
m_texDungeonCeil.pixels[y * TEX_SIZE + x] = Color{ 220, 25, 45, 255 };
} else {
m_texDungeonCeil.pixels[y * TEX_SIZE + x] = Color{
(unsigned char)base,
(unsigned char)(base + 2),
(unsigned char)(base + 6),
255
};
}
}
}
// 4. Dungeon Blast Door: Heavy pneumatic airlock door with yellow warning chevron
makeTex(m_texDungeonDoor);
for (int y = 0; y < TEX_SIZE; ++y) {
for (int x = 0; x < TEX_SIZE; ++x) {
bool isSeam = (x == 127 || x == 128 || y == 64 || y == 192);
bool isWarning = (y >= 115 && y <= 140 && ((x + y) / 16) % 2 == 0);
if (isWarning) {
m_texDungeonDoor.pixels[y * TEX_SIZE + x] = Color{ 235, 160, 20, 255 };
} else if (isSeam) {
m_texDungeonDoor.pixels[y * TEX_SIZE + x] = Color{ 14, 18, 24, 255 };
} else {
int base = 48 + ((x * 7 + y * 11) % 6);
m_texDungeonDoor.pixels[y * TEX_SIZE + x] = Color{
(unsigned char)base,
(unsigned char)(base + 2),
(unsigned char)(base + 6),
255
};
}
}
}
// 5. Parquet Floor: Dark smoked walnut planks
makeTex(m_texParquet);
for (int y = 0; y < TEX_SIZE; ++y) {
int row = y / 32;
int rowY = y % 32;
for (int x = 0; x < TEX_SIZE; ++x) {
int shift = (row % 2 == 0) ? 0 : 48;
int col = (x + shift) / 96;
int colX = (x + shift) % 96;
bool isSeam = (rowY == 0 || colX == 0);
if (isSeam) {
m_texParquet.pixels[y * TEX_SIZE + x] = Color{ 16, 12, 10, 255 };
} else {
int base = 42 + ((row * 13 + col * 23) % 11) - 5;
m_texParquet.pixels[y * TEX_SIZE + x] = Color{
(unsigned char)std::clamp(base + 8, 15, 120),
(unsigned char)std::clamp(base + 2, 12, 110),
(unsigned char)std::clamp(base - 2, 10, 95),
255
};
}
}
}
// 6. Rug: Luxury Persian / cyber antique rug
makeTex(m_texRug);
for (int y = 0; y < TEX_SIZE; ++y) {
for (int x = 0; x < TEX_SIZE; ++x) {
int mDist = std::max(std::abs(x - 128), std::abs(y - 128));
Color c;
if (mDist > 115) c = Color{ 115, 92, 48, 255 }; // Outer gold border
else if (mDist > 105) c = Color{ 22, 28, 42, 255 }; // Dark groove
else if (mDist > 98) c = Color{ 145, 110, 55, 255 }; // Inner accent
else c = Color{ 36, 44, 62, 255 }; // Midnight blue field
m_texRug.pixels[y * TEX_SIZE + x] = c;
}
}
// 7. Plaster Wall: Dark slate graphite
makeTex(m_texPlasterWall);
for (int y = 0; y < TEX_SIZE; ++y) {
for (int x = 0; x < TEX_SIZE; ++x) {
int grain = ((x * 23 + y * 41 + (x ^ y) * 17) % 13) - 6;
int base = 34 + grain;
m_texPlasterWall.pixels[y * TEX_SIZE + x] = Color{
(unsigned char)std::clamp(base, 12, 90),
(unsigned char)std::clamp(base + 4, 15, 95),
(unsigned char)std::clamp(base + 10, 18, 110),
255
};
}
}
// 8. Room Window: Try loading Room_window.jpg
makeTex(m_texWindowView);
bool loadedWindow = false;
const std::vector<std::string> winPaths = {
"assets/Pictures/Room_window.jpg",
"assets/Pictures/Room_window.png",
"../assets/Pictures/Room_window.jpg",
"assets/Room_window.jpg"
};
for (const auto& wp : winPaths) {
if (FileExists(wp.c_str())) {
Image img = LoadImage(wp.c_str());
if (img.data) {
ImageResize(&img, TEX_SIZE, TEX_SIZE);
ImageFormat(&img, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8);
Color* pix = (Color*)img.data;
m_texWindowView.pixels.assign(pix, pix + TEX_SIZE * TEX_SIZE);
UnloadImage(img);
loadedWindow = true;
break;
}
}
}
if (!loadedWindow) {
// Fallback procedural night skyline
for (int y = 0; y < TEX_SIZE; ++y) {
for (int x = 0; x < TEX_SIZE; ++x) {
float t = y / 256.0f;
int b = static_cast<int>(18 + (1.0f - t) * 75);
m_texWindowView.pixels[y * TEX_SIZE + x] = Color{
(unsigned char)(b * 0.7f),
(unsigned char)(b * 0.4f),
(unsigned char)b,
255
};
}
}
}
// 9. Cyber Gate & Other Textures
makeTex(m_texCyberGate);
for (int y = 0; y < TEX_SIZE; ++y) {
for (int x = 0; x < TEX_SIZE; ++x) {
bool isFrame = (x < 24 || x > 231 || y < 24);
bool isGateCore = !isFrame && (std::abs(x - 128) < 85);
if (isGateCore) {
m_texCyberGate.pixels[y * TEX_SIZE + x] = Color{ 0, 255, 128, 220 };
} else if (isFrame) {
m_texCyberGate.pixels[y * TEX_SIZE + x] = Color{ 24, 32, 44, 255 };
} else {
m_texCyberGate.pixels[y * TEX_SIZE + x] = Color{ 10, 14, 20, 255 };
}
}
}
// 10. Ancient Palace Textures (Rest Room)
makeTex(m_texAncientWall);
for (int y = 0; y < TEX_SIZE; ++y) {
for (int x = 0; x < TEX_SIZE; ++x) {
int brickY = y / 32;
int shift = (brickY % 2 == 0) ? 0 : 32;
int brickX = (x + shift) / 64;
bool isMortar = (y % 32 <= 2 || (x + shift) % 64 <= 2);
int base = isMortar ? 20 : 54 + ((brickX * 17 + brickY * 31) % 15) - 7;
m_texAncientWall.pixels[y * TEX_SIZE + x] = Color{
(unsigned char)(base + 6),
(unsigned char)base,
(unsigned char)(base - 4),
255
};
}
}
m_texAncientFloor = m_texAncientWall;
m_texAncientCeil = m_texAncientWall;
// 11. SPRITES: Drone Sprite
makeTex(m_texDrone);
for (int y = 0; y < TEX_SIZE; ++y) {
for (int x = 0; x < TEX_SIZE; ++x) {
int dx = x - 128;
int dy = y - 128;
float r = std::sqrt(float(dx * dx + dy * dy));
if (r < 75.0f) {
// Drone body chassis
if (r < 28.0f) {
m_texDrone.pixels[y * TEX_SIZE + x] = Color{ 240, 245, 255, 255 }; // Glowing core
} else if (std::abs(dy) < 14 && dx > -45 && dx < 45) {
m_texDrone.pixels[y * TEX_SIZE + x] = Color{ 230, 25, 45, 255 }; // Laser visor
} else {
int b = 35 + (int)(r * 0.4f);
m_texDrone.pixels[y * TEX_SIZE + x] = Color{ (unsigned char)b, (unsigned char)(b + 5), (unsigned char)(b + 12), 255 };
}
} else if (r < 82.0f && (std::abs(dx) > 55 || std::abs(dy) > 55)) {
m_texDrone.pixels[y * TEX_SIZE + x] = Color{ 60, 75, 95, 255 }; // Thruster pods
} else {
m_texDrone.pixels[y * TEX_SIZE + x] = Color{ 0, 0, 0, 0 }; // Transparent background
}
}
}
// 12. SPRITES: Elite Drone
makeTex(m_texDroneElite);
for (int y = 0; y < TEX_SIZE; ++y) {
for (int x = 0; x < TEX_SIZE; ++x) {
int dx = x - 128;
int dy = y - 128;
float r = std::sqrt(float(dx * dx + dy * dy));
if (r < 85.0f) {
if (r < 32.0f) {
m_texDroneElite.pixels[y * TEX_SIZE + x] = Color{ 255, 60, 75, 255 }; // Crimson reactor
} else if (r > 65.0f && r < 75.0f) {
m_texDroneElite.pixels[y * TEX_SIZE + x] = Color{ 220, 25, 45, 255 }; // Armor shroud
} else {
int b = 25 + (int)(r * 0.35f);
m_texDroneElite.pixels[y * TEX_SIZE + x] = Color{ (unsigned char)(b + 15), (unsigned char)b, (unsigned char)b, 255 };
}
} else {
m_texDroneElite.pixels[y * TEX_SIZE + x] = Color{ 0, 0, 0, 0 };
}
}
}
// 13. SPRITES: Desk with Dual Monitors
makeTex(m_texDesk);
for (int y = 0; y < TEX_SIZE; ++y) {
for (int x = 0; x < TEX_SIZE; ++x) {
bool isTable = (y >= 170 && y <= 230 && x >= 30 && x <= 226);
bool isLegs = (y > 230 && ((x >= 35 && x <= 50) || (x >= 206 && x <= 221)));
bool isMonLeft = (y >= 70 && y <= 165 && x >= 45 && x <= 125);
bool isMonRight = (y >= 70 && y <= 165 && x >= 135 && x <= 215);
if (isMonLeft) {
bool isCode = (y % 12 >= 8 && x % 20 < 15);
m_texDesk.pixels[y * TEX_SIZE + x] = isCode ? Color{ 220, 230, 245, 255 } : Color{ 14, 20, 32, 255 };
} else if (isMonRight) {
int rx = x - 175;
int ry = y - 117;
bool isRadar = (std::abs(rx * rx + ry * ry - 900) < 60 || rx == 0 || ry == 0);
m_texDesk.pixels[y * TEX_SIZE + x] = isRadar ? Color{ 0, 255, 180, 255 } : Color{ 8, 16, 26, 255 };
} else if (isTable) {
m_texDesk.pixels[y * TEX_SIZE + x] = Color{ 48, 38, 32, 255 };
} else if (isLegs) {
m_texDesk.pixels[y * TEX_SIZE + x] = Color{ 25, 28, 35, 255 };
} else {
m_texDesk.pixels[y * TEX_SIZE + x] = Color{ 0, 0, 0, 0 };
}
}
}
// 14. SPRITES: Cyber Bed
makeTex(m_texBed);
for (int y = 0; y < TEX_SIZE; ++y) {
for (int x = 0; x < TEX_SIZE; ++x) {
bool isBedFrame = (y >= 160 && y <= 245 && x >= 35 && x <= 220);
bool isPillow = (y >= 170 && y <= 200 && x >= 45 && x <= 95);
if (isPillow) {
m_texBed.pixels[y * TEX_SIZE + x] = Color{ 210, 218, 230, 255 };
} else if (isBedFrame) {
m_texBed.pixels[y * TEX_SIZE + x] = Color{ 26, 32, 48, 255 };
} else {
m_texBed.pixels[y * TEX_SIZE + x] = Color{ 0, 0, 0, 0 };
}
}
}
// 15. SPRITES: Loot Cache Crate
makeTex(m_texLootCache);
for (int y = 0; y < TEX_SIZE; ++y) {
for (int x = 0; x < TEX_SIZE; ++x) {
bool isBox = (y >= 140 && y <= 240 && x >= 55 && x <= 201);
if (isBox) {
bool isLed = (y >= 175 && y <= 195 && x >= 115 && x <= 141);
if (isLed) {
m_texLootCache.pixels[y * TEX_SIZE + x] = Color{ 255, 30, 50, 255 };
} else {
int b = 38 + ((x % 32 < 2 || y % 32 < 2) ? -12 : 0);
m_texLootCache.pixels[y * TEX_SIZE + x] = Color{ (unsigned char)b, (unsigned char)(b + 4), (unsigned char)(b + 8), 255 };
}
} else {
m_texLootCache.pixels[y * TEX_SIZE + x] = Color{ 0, 0, 0, 0 };
}
}
}
// 16. SPRITES: Exit Portal Beacon
makeTex(m_texExitPortal);
for (int y = 0; y < TEX_SIZE; ++y) {
for (int x = 0; x < TEX_SIZE; ++x) {
int dx = x - 128;
bool isCore = (std::abs(dx) < 22 && y >= 60 && y <= 235);
bool isBase = (y >= 235 && std::abs(dx) < 65);
if (isCore) {
m_texExitPortal.pixels[y * TEX_SIZE + x] = Color{ 240, 30, 60, 255 };
} else if (isBase) {
m_texExitPortal.pixels[y * TEX_SIZE + x] = Color{ 30, 38, 52, 255 };
} else {
m_texExitPortal.pixels[y * TEX_SIZE + x] = Color{ 0, 0, 0, 0 };
}
}
}
// 17. SPRITES: Proximity Mine
makeTex(m_texMine);
for (int y = 0; y < TEX_SIZE; ++y) {
for (int x = 0; x < TEX_SIZE; ++x) {
bool isPlate = (y >= 210 && y <= 245 && x >= 70 && x <= 186);
bool isBlinker = (y >= 218 && y <= 232 && x >= 118 && x <= 138);
if (isBlinker) {
m_texMine.pixels[y * TEX_SIZE + x] = Color{ 255, 40, 50, 255 };
} else if (isPlate) {
m_texMine.pixels[y * TEX_SIZE + x] = Color{ 36, 42, 54, 255 };
} else {
m_texMine.pixels[y * TEX_SIZE + x] = Color{ 0, 0, 0, 0 };
}
}
}
// 18. SPRITES: Cyber Shrine Terminal
makeTex(m_texShrine);
for (int y = 0; y < TEX_SIZE; ++y) {
for (int x = 0; x < TEX_SIZE; ++x) {
bool isMonolith = (y >= 70 && y <= 245 && x >= 85 && x <= 171);
bool isScreen = (y >= 90 && y <= 150 && x >= 95 && x <= 161);
if (isScreen) {
m_texShrine.pixels[y * TEX_SIZE + x] = Color{ 230, 30, 50, 255 };
} else if (isMonolith) {
m_texShrine.pixels[y * TEX_SIZE + x] = Color{ 20, 26, 36, 255 };
} else {
m_texShrine.pixels[y * TEX_SIZE + x] = Color{ 0, 0, 0, 0 };
}
}
}
// 19. SPRITES: Laser Beam Strip
makeTex(m_texLaserBeam);
for (int y = 0; y < TEX_SIZE; ++y) {
for (int x = 0; x < TEX_SIZE; ++x) {
bool isBeam = (y >= 120 && y <= 136);
if (isBeam) {
m_texLaserBeam.pixels[y * TEX_SIZE + x] = (y >= 126 && y <= 130) ? WHITE : Color{ 240, 30, 60, 255 };
} else {
m_texLaserBeam.pixels[y * TEX_SIZE + x] = Color{ 0, 0, 0, 0 };
}
}
}
}
const SoftwareRenderer::CpuTexture& SoftwareRenderer::getTexture(int index) const {
switch (index) {
case 1: return m_texDungeonWall;
case 2: return m_texDungeonFloor;
case 3: return m_texDungeonCeil;
case 4: return m_texDungeonDoor;
case 5: return m_texParquet;
case 6: return m_texRug;
case 7: return m_texPlasterWall;
case 8: return m_texWindowView;
case 9: return m_texCyberGate;
case 10: return m_texAncientWall;
case 11: return m_texAncientFloor;
case 12: return m_texAncientCeil;
case 13: return m_texDrone;
case 14: return m_texDroneElite;
case 15: return m_texDesk;
case 16: return m_texBed;
case 17: return m_texLootCache;
case 18: return m_texExitPortal;
case 19: return m_texMine;
case 20: return m_texShrine;
case 21: return m_texLaserBeam;
default: return m_texDungeonWall;
}
}
// ----------------------------------------------------------------------------
// Core DDA Raycaster with Diminishing Light & Horizontal Spans
// ----------------------------------------------------------------------------
void SoftwareRenderer::renderSceneRaycast(float posX, float posY, float dirX, float dirY,
int mapW, int mapH,
const std::vector<int>& mapGrid,
int wallTex, int floorTex, int ceilTex,
float animTime,
const std::vector<BillboardSprite>& sprites) {
float fov = 0.66f;
float planeX = -dirY * fov;
float planeY = dirX * fov;
std::vector<int> wallDrawStart(FB_WIDTH);
std::vector<int> wallDrawEnd(FB_WIDTH);
const auto& texWall = getTexture(wallTex);
const auto& texFloor = getTexture(floorTex);
const auto& texCeil = getTexture(ceilTex);
// 1. Raycast Wall Columns
for (int x = 0; x < FB_WIDTH; ++x) {
float cameraX = 2.0f * x / float(FB_WIDTH) - 1.0f;
float rayDirX = dirX + planeX * cameraX;
float rayDirY = dirY + planeY * cameraX;
int mapX = int(posX);
int mapY = int(posY);
float deltaDistX = (rayDirX == 0.0f) ? 1e30f : std::abs(1.0f / rayDirX);
float deltaDistY = (rayDirY == 0.0f) ? 1e30f : std::abs(1.0f / rayDirY);
float perpWallDist = 0.0f;
int stepX = 0;
int stepY = 0;
float sideDistX = 0.0f;
float sideDistY = 0.0f;
if (rayDirX < 0) {
stepX = -1;
sideDistX = (posX - mapX) * deltaDistX;
} else {
stepX = 1;
sideDistX = (mapX + 1.0f - posX) * deltaDistX;
}
if (rayDirY < 0) {
stepY = -1;
sideDistY = (posY - mapY) * deltaDistY;
} else {
stepY = 1;
sideDistY = (mapY + 1.0f - posY) * deltaDistY;
}
int hit = 0;
int side = 0; // 0 = X-axis, 1 = Y-axis
int hitTile = 1;
while (hit == 0) {
if (sideDistX < sideDistY) {
sideDistX += deltaDistX;
mapX += stepX;
side = 0;
} else {
sideDistY += deltaDistY;
mapY += stepY;
side = 1;
}
if (mapX < 0 || mapX >= mapW || mapY < 0 || mapY >= mapH) {
hit = 1;
hitTile = 1;
break;
}
int t = mapGrid[mapY * mapW + mapX];
if (t > 0) {
hit = 1;
hitTile = t;
}
}
if (side == 0) perpWallDist = (sideDistX - deltaDistX);
else perpWallDist = (sideDistY - deltaDistY);
if (perpWallDist < 0.1f) perpWallDist = 0.1f;
m_depthBuffer[x] = perpWallDist;
int lineHeight = int(FB_HEIGHT / perpWallDist);
int drawStart = -lineHeight / 2 + FB_HEIGHT / 2;
if (drawStart < 0) drawStart = 0;
int drawEnd = lineHeight / 2 + FB_HEIGHT / 2;
if (drawEnd >= FB_HEIGHT) drawEnd = FB_HEIGHT - 1;
wallDrawStart[x] = drawStart;
wallDrawEnd[x] = drawEnd;
// Texture coordinate calculation
float wallX = 0.0f;
if (side == 0) wallX = posY + perpWallDist * rayDirY;
else wallX = posX + perpWallDist * rayDirX;
wallX -= std::floor(wallX);
int texX = int(wallX * float(TEX_SIZE));
if (side == 0 && rayDirX > 0) texX = TEX_SIZE - texX - 1;
if (side == 1 && rayDirY < 0) texX = TEX_SIZE - texX - 1;
// Doom diminishing light formula
float shade = 1.0f / (1.0f + perpWallDist * 0.14f);
if (side == 1) shade *= 0.80f; // Ambient directional shadow
// Tactical flashlight cone focus in center
float angleDot = (rayDirX * dirX + rayDirY * dirY);
float cone = std::clamp((angleDot - 0.75f) / 0.25f, 0.0f, 1.0f);
shade = std::clamp(shade * (0.65f + cone * 0.60f), 0.08f, 1.0f);
// Select wall texture (doors use door texture, window wall uses window)
const CpuTexture* currWallTex = &texWall;
if (hitTile == 4) currWallTex = &m_texDungeonDoor;
else if (hitTile == 8) currWallTex = &m_texWindowView;
else if (hitTile == 9) currWallTex = &m_texCyberGate;
float step = 1.0f * TEX_SIZE / lineHeight;
float texPos = (drawStart - FB_HEIGHT / 2 + lineHeight / 2) * step;
for (int y = drawStart; y <= drawEnd; ++y) {
int texY = int(texPos) & (TEX_SIZE - 1);
texPos += step;
Color c = currWallTex->sample(texX, texY);
Color finalCol = {
(unsigned char)(c.r * shade),
(unsigned char)(c.g * shade),
(unsigned char)(c.b * shade),
255
};
m_framebuffer[y * FB_WIDTH + x] = finalCol;
}
}
// 2. Horizontal Floor and Ceiling Spans
float rayDirX0 = dirX - planeX;
float rayDirY0 = dirY - planeY;
float rayDirX1 = dirX + planeX;
float rayDirY1 = dirY + planeY;
for (int y = FB_HEIGHT / 2 + 1; y < FB_HEIGHT; ++y) {
int p = y - FB_HEIGHT / 2;
float posZ = 0.5f * FB_HEIGHT;
float rowDistance = posZ / p;
float floorStepX = rowDistance * (rayDirX1 - rayDirX0) / FB_WIDTH;
float floorStepY = rowDistance * (rayDirY1 - rayDirY0) / FB_WIDTH;
float floorX = posX + rowDistance * rayDirX0;
float floorY = posY + rowDistance * rayDirY0;
float rowShade = std::clamp(1.0f / (1.0f + rowDistance * 0.16f), 0.06f, 1.0f);
for (int x = 0; x < FB_WIDTH; ++x) {
int tx = int(TEX_SIZE * (floorX - std::floor(floorX))) & (TEX_SIZE - 1);
int ty = int(TEX_SIZE * (floorY - std::floor(floorY))) & (TEX_SIZE - 1);
floorX += floorStepX;
floorY += floorStepY;
// Draw floor pixel if below wall column
if (y > wallDrawEnd[x]) {
Color fc = texFloor.sample(tx, ty);
m_framebuffer[y * FB_WIDTH + x] = Color{
(unsigned char)(fc.r * rowShade),
(unsigned char)(fc.g * rowShade),
(unsigned char)(fc.b * rowShade),
255
};
}
// Draw ceiling pixel symmetrically
int ceilY = FB_HEIGHT - y - 1;
if (ceilY < wallDrawStart[x]) {
Color cc = texCeil.sample(tx, ty);
m_framebuffer[ceilY * FB_WIDTH + x] = Color{
(unsigned char)(cc.r * rowShade),
(unsigned char)(cc.g * rowShade),
(unsigned char)(cc.b * rowShade),
255
};
}
}
}
// 3. Depth-Sorted Billboard Sprites
if (!sprites.empty()) {
std::vector<BillboardSprite> sortedSprites = sprites;
for (auto& spr : sortedSprites) {
float dx = spr.x - posX;
float dy = spr.y - posY;
spr.distSq = dx * dx + dy * dy;
}
std::sort(sortedSprites.begin(), sortedSprites.end(), [](const BillboardSprite& a, const BillboardSprite& b) {
return a.distSq > b.distSq;
});
for (const auto& spr : sortedSprites) {
float spriteX = spr.x - posX;
float spriteY = spr.y - posY;
float invDet = 1.0f / (planeX * dirY - dirX * planeY);
float transformX = invDet * (dirY * spriteX - dirX * spriteY);
float transformY = invDet * (-planeY * spriteX + planeX * spriteY);
if (transformY <= 0.15f) continue; // Behind or right on top of camera
int spriteScreenX = int((FB_WIDTH / 2) * (1.0f + transformX / transformY));
int spriteHeight = std::abs(int(FB_HEIGHT / transformY * spr.scale));
int vShift = int(spr.vOffset * spriteHeight);
int drawStartY = -spriteHeight / 2 + FB_HEIGHT / 2 + vShift;
if (drawStartY < 0) drawStartY = 0;
int drawEndY = spriteHeight / 2 + FB_HEIGHT / 2 + vShift;
if (drawEndY >= FB_HEIGHT) drawEndY = FB_HEIGHT - 1;
int spriteWidth = std::abs(int(FB_HEIGHT / transformY * spr.scale));
int drawStartX = -spriteWidth / 2 + spriteScreenX;
if (drawStartX < 0) drawStartX = 0;
int drawEndX = spriteWidth / 2 + spriteScreenX;
if (drawEndX >= FB_WIDTH) drawEndX = FB_WIDTH - 1;
float sprShade = std::clamp(1.0f / (1.0f + transformY * 0.14f), 0.12f, 1.0f);
const auto& sprTex = getTexture(spr.texIndex);
for (int stripe = drawStartX; stripe <= drawEndX; ++stripe) {
int texX = int((stripe - (-spriteWidth / 2 + spriteScreenX)) * TEX_SIZE / spriteWidth);
if (texX < 0 || texX >= TEX_SIZE) continue;
if (transformY < m_depthBuffer[stripe]) {
for (int y = drawStartY; y <= drawEndY; ++y) {
int d = (y - vShift) * 256 - FB_HEIGHT * 128 + spriteHeight * 128;
int texY = ((d * TEX_SIZE) / spriteHeight) / 256;
texY = std::clamp(texY, 0, TEX_SIZE - 1);
Color c = sprTex.sample(texX, texY);
if (c.a > 20) {
Color finalCol;
if (spr.tint.r != 255 || spr.tint.g != 255 || spr.tint.b != 255) {
float tr = spr.tint.r / 255.0f;
float tg = spr.tint.g / 255.0f;
float tb = spr.tint.b / 255.0f;
finalCol = Color{
(unsigned char)(c.r * tr * sprShade),
(unsigned char)(c.g * tg * sprShade),
(unsigned char)(c.b * tb * sprShade),
255
};
} else {
finalCol = Color{
(unsigned char)(c.r * sprShade),
(unsigned char)(c.g * sprShade),
(unsigned char)(c.b * sprShade),
255
};
}
m_framebuffer[y * FB_WIDTH + stripe] = finalCol;
}
}
}
}
}
}
}
// ----------------------------------------------------------------------------
// Presentation with 2X Integer Upscaling
// ----------------------------------------------------------------------------
void SoftwareRenderer::presentFramebuffer() {
float dt = GetFrameTime();
if (m_glitchTimer > 0.0f) {
m_glitchTimer -= dt * 2.0f;
// CRT horizontal displacement slice artifact
int sliceY = (std::rand() % (FB_HEIGHT - 30));
int shift = (std::rand() % 15) - 7;
for (int y = sliceY; y < sliceY + 20 && y < FB_HEIGHT; ++y) {
for (int x = 0; x < FB_WIDTH; ++x) {
int srcX = std::clamp(x + shift, 0, FB_WIDTH - 1);
m_framebuffer[y * FB_WIDTH + x] = m_framebuffer[y * FB_WIDTH + srcX];
}
}
}
UpdateTexture(m_screenTexture, m_framebuffer.data());
// 2x Nearest-Neighbor Upscale to 1280x720 window
Rectangle src = { 0.0f, 0.0f, (float)FB_WIDTH, (float)FB_HEIGHT };
Rectangle dst = { 0.0f, 0.0f, (float)GetScreenWidth(), (float)GetScreenHeight() };
DrawTexturePro(m_screenTexture, src, dst, Vector2{ 0.0f, 0.0f }, 0.0f, WHITE);
}
// ----------------------------------------------------------------------------
// Exploration 3D Raycasting
// ----------------------------------------------------------------------------
void SoftwareRenderer::render3D(const Dungeon& dungeon, const Player& player, float animTime) {
if (!m_initialized) init();
int mapW = dungeon.getWidth();
int mapH = dungeon.getHeight();
std::vector<int> grid(mapW * mapH, 0);
for (int y = 0; y < mapH; ++y) {
for (int x = 0; x < mapW; ++x) {
TileType t = dungeon.getTile(x, y);
if (t == TileType::WALL) grid[y * mapW + x] = 1;
else if (t == TileType::DOOR_LOCKED || t == TileType::DOOR_UNLOCKED) grid[y * mapW + x] = 4;
else grid[y * mapW + x] = 0;
}
}
float posX = player.getVisualPosition().x / 2.0f;
float posY = player.getVisualPosition().z / 2.0f;
float rad = player.getVisualYaw() * (3.14159265f / 180.0f);
float dirX = std::sin(rad);
float dirY = -std::cos(rad);
// Build sprites list: Caches, Portals, Mines, Shrines, Lasers, Drone Patrols
std::vector<BillboardSprite> sprites;
for (int y = 0; y < mapH; ++y) {
for (int x = 0; x < mapW; ++x) {
TileType t = dungeon.getTile(x, y);
if (t == TileType::LOOT_CACHE) {
sprites.push_back({ x + 0.5f, y + 0.5f, 17, 0.75f, 0.15f, WHITE, false });
} else if (t == TileType::EXIT_PORTAL) {
sprites.push_back({ x + 0.5f, y + 0.5f, 18, 0.95f, 0.05f, WHITE, false });
}
}
}
for (const auto& l : dungeon.getLasers()) {
if (!l.disarmed) {
sprites.push_back({ l.x + 0.5f, l.y + 0.5f, 21, 0.85f, 0.0f, (l.active ? TacticalColors::BrightRed : Color{ 70, 70, 80, 255 }), false });
}
}
for (const auto& m : dungeon.getMines()) {
if (!m.defused && !m.triggered) {
sprites.push_back({ m.x + 0.5f, m.y + 0.5f, 19, 0.6f, 0.35f, WHITE, false });
}
}
for (const auto& s : dungeon.getShrines()) {
sprites.push_back({ s.x + 0.5f, s.y + 0.5f, 20, 0.9f, 0.05f, (s.used ? TacticalColors::SteelMuted : WHITE), false });
}
auto& enemies = const_cast<Dungeon&>(dungeon).getEnemies();
for (const auto& me : enemies) {
if (me.alive) {
sprites.push_back({ me.x + 0.5f, me.y + 0.5f, me.enemy.isElite() ? 14 : 13, 0.85f, 0.0f, me.enemy.getColor(), false });
}
}
renderSceneRaycast(posX, posY, dirX, dirY, mapW, mapH, grid, 1, 2, 3, animTime, sprites);
presentFramebuffer();
}
// ----------------------------------------------------------------------------
// Combat 3D Raycasting
// ----------------------------------------------------------------------------
void SoftwareRenderer::renderCombat3D(const Dungeon& dungeon, const Player& player, const CombatSystem& combat, float animTime) {
if (!m_initialized) init();
int mapW = dungeon.getWidth();
int mapH = dungeon.getHeight();
std::vector<int> grid(mapW * mapH, 0);
for (int y = 0; y < mapH; ++y) {
for (int x = 0; x < mapW; ++x) {
TileType t = dungeon.getTile(x, y);
grid[y * mapW + x] = (t == TileType::WALL) ? 1 : 0;
}
}
float posX = player.getVisualPosition().x / 2.0f;
float posY = player.getVisualPosition().z / 2.0f;
float rad = player.getVisualYaw() * (3.14159265f / 180.0f);
float dirX = std::sin(rad);
float dirY = -std::cos(rad);
std::vector<BillboardSprite> sprites;
const auto& enemy = combat.getEnemy();
if (enemy && enemy->isAlive()) {
float bob = std::sin(animTime * 3.5f) * 0.05f;
float distFront = 2.2f;
float droneX = posX + dirX * distFront;
float droneY = posY + dirY * distFront;
bool isHit = (combat.getPhase() == CombatPhase::PLAYER_ACTION_ANIM);
Color tint = isHit ? WHITE : enemy->getColor();
sprites.push_back({ droneX, droneY, enemy->isElite() ? 14 : 13, 1.05f, bob, tint, false });
}
renderSceneRaycast(posX, posY, dirX, dirY, mapW, mapH, grid, 1, 2, 3, animTime, sprites);
presentFramebuffer();
}
// ----------------------------------------------------------------------------
// Apartment Hub 2.5D Raycasting
// ----------------------------------------------------------------------------
void SoftwareRenderer::renderHub(const HubRoom& hub, const Player& player, float animTime) {
if (!m_initialized) init();
// 6x6 cozy apartment room
const int roomW = 6;
const int roomH = 6;
std::vector<int> grid(roomW * roomH, 0);
for (int y = 0; y < roomH; ++y) {
for (int x = 0; x < roomW; ++x) {
if (x == 0 || y == 5) grid[y * roomW + x] = 7; // Plaster Wall
else if (x == 5) grid[y * roomW + x] = 8; // Room Window (East wall with Room_window.jpg!)
else if (y == 0) grid[y * roomW + x] = 9; // Cyber Gate (North wall)
else grid[y * roomW + x] = 0;
}
}
float posX = std::clamp(player.getVisualPosition().x / 2.0f, 1.2f, 4.8f);
float posY = std::clamp(player.getVisualPosition().z / 2.0f, 1.2f, 4.8f);
float rad = player.getVisualYaw() * (3.14159265f / 180.0f);
float dirX = std::sin(rad);
float dirY = -std::cos(rad);
// Place apartment furniture as authentic 2.5D Doom-style sprites
std::vector<BillboardSprite> sprites = {
{ 3.0f, 1.3f, 15, 1.2f, 0.10f, WHITE, false }, // Workstation Desk with Dual Glowing Monitors
{ 1.4f, 4.0f, 16, 1.2f, 0.15f, WHITE, false } // Midnight Indigo Cyber Bed
};
renderSceneRaycast(posX, posY, dirX, dirY, roomW, roomH, grid, 7, 5, 7, animTime, sprites);
presentFramebuffer();
}
// ----------------------------------------------------------------------------
// Safe Haven Rest Room 2.5D Raycasting
// ----------------------------------------------------------------------------
void SoftwareRenderer::renderRestRoom(const RestRoom& restRoom, const Player& player, float animTime) {
if (!m_initialized) init();
const int roomW = 6;
const int roomH = 6;
std::vector<int> grid(roomW * roomH, 0);
for (int y = 0; y < roomH; ++y) {
for (int x = 0; x < roomW; ++x) {
if (x == 0 || x == 5 || y == 0 || y == 5) {
grid[y * roomW + x] = 10; // Ancient Palace Wall
} else {
grid[y * roomW + x] = 0;
}
}
}
float posX = std::clamp(player.getVisualPosition().x / 2.0f, 1.2f, 4.8f);
float posY = std::clamp(player.getVisualPosition().z / 2.0f, 1.2f, 4.8f);
float rad = player.getVisualYaw() * (3.14159265f / 180.0f);
float dirX = std::sin(rad);
float dirY = -std::cos(rad);
// Workbench and Cyber Portal
std::vector<BillboardSprite> sprites = {
{ 3.0f, 1.2f, 15, 1.1f, 0.10f, WHITE, false },
{ 3.0f, 4.6f, 18, 0.95f, 0.05f, WHITE, false }
};
renderSceneRaycast(posX, posY, dirX, dirY, roomW, roomH, grid, 10, 11, 12, animTime, sprites);
presentFramebuffer();
}
} // namespace dnr

View file

@ -0,0 +1,113 @@
#pragma once
#include "core/Common.hpp"
#include "dungeon/Dungeon.hpp"
#include "dungeon/Player.hpp"
#include <vector>
#include <memory>
#include <string>
namespace dnr {
class HubRoom;
class RestRoom;
class CombatSystem;
class SoftwareRenderer {
public:
SoftwareRenderer();
~SoftwareRenderer();
void init();
void cleanup();
void render3D(const Dungeon& dungeon, const Player& player, float animTime);
void renderCombat3D(const Dungeon& dungeon, const Player& player, const CombatSystem& combat, float animTime);
void renderHub(const HubRoom& hub, const Player& player, float animTime);
void renderRestRoom(const RestRoom& restRoom, const Player& player, float animTime);
void triggerGlitch(float intensity = 0.7f);
void setGraphicsQuality(GraphicsQuality q) { m_graphicsQuality = q; }
GraphicsQuality getGraphicsQuality() const { return m_graphicsQuality; }
private:
static constexpr int FB_WIDTH = 640;
static constexpr int FB_HEIGHT = 360;
static constexpr int TEX_SIZE = 256;
struct CpuTexture {
int width = TEX_SIZE;
int height = TEX_SIZE;
std::vector<Color> pixels;
Color sample(int x, int y) const {
if (pixels.empty()) return MAGENTA;
int px = (x >= 0 ? x : 0) % width;
int py = (y >= 0 ? y : 0) % height;
return pixels[py * width + px];
}
};
struct BillboardSprite {
float x = 0.0f;
float y = 0.0f;
int texIndex = 0;
float scale = 1.0f;
float vOffset = 0.0f; // vertical shift in height units
Color tint = WHITE;
bool additive = false;
float distSq = 0.0f;
};
void generateCpuTextures();
void renderSceneRaycast(float posX, float posY, float dirX, float dirY,
int mapW, int mapH,
const std::vector<int>& mapGrid,
int wallTex, int floorTex, int ceilTex,
float animTime,
const std::vector<BillboardSprite>& sprites);
void presentFramebuffer();
// Framebuffer & Raylib Texture
std::vector<Color> m_framebuffer;
std::vector<float> m_depthBuffer;
Image m_screenImage{};
Texture2D m_screenTexture{};
bool m_initialized = false;
GraphicsQuality m_graphicsQuality = GraphicsQuality::BALANCED;
float m_glitchTimer = 0.0f;
// CPU Textures
CpuTexture m_texDungeonWall;
CpuTexture m_texDungeonFloor;
CpuTexture m_texDungeonCeil;
CpuTexture m_texDungeonDoor;
CpuTexture m_texParquet;
CpuTexture m_texRug;
CpuTexture m_texPlasterWall;
CpuTexture m_texWindowView;
CpuTexture m_texPoster;
CpuTexture m_texBed;
CpuTexture m_texDesk;
CpuTexture m_texCyberGate;
CpuTexture m_texAncientWall;
CpuTexture m_texAncientFloor;
CpuTexture m_texAncientCeil;
CpuTexture m_texWorkbench;
// Sprite Textures
CpuTexture m_texDrone;
CpuTexture m_texDroneElite;
CpuTexture m_texLootCache;
CpuTexture m_texExitPortal;
CpuTexture m_texLaserBeam;
CpuTexture m_texMine;
CpuTexture m_texShrine;
const CpuTexture& getTexture(int index) const;
};
} // namespace dnr