├── data ├── sound_sequences.json ├── shaders │ ├── block_opaque_fragment.glsl │ ├── block_trans_fragment.glsl │ ├── sprite_fragment.glsl │ ├── block_vertex.glsl │ └── sprite_vertex.glsl ├── music_gta1.json ├── music_uk.json ├── music_gta2.json └── entitytypenames_gta1.json ├── .gitattributes ├── GTAViewer.sln ├── LICENSE ├── src ├── missionreader.d ├── obstacle.d ├── decoration.d ├── script.d ├── rectangle.d ├── gamestate.d ├── fasthash.d ├── mapprocessor.d ├── convert.d ├── buffer.d ├── typenames.d ├── mapreader.d ├── enums.d ├── uniformbuffer.d ├── powerup.d ├── stylereader.d ├── vector3.d ├── gta1initcommands.d ├── timer.d ├── missionlist.d ├── mapprocessorgta1.d ├── log.d ├── shader.d ├── animations.d ├── packnode.d ├── audiobank.d ├── cvars.d ├── bmp.d ├── program.d ├── rifffile.d ├── missionreadergta1.d ├── source.d ├── binaryfile.d ├── gta1commands.d ├── indexbuffer.d ├── gciparser.d ├── color.d ├── camera.d ├── missionreadergta2.d ├── block.d ├── renderer.d ├── vertexbuffer.d ├── texture.d ├── delta.d ├── main.d ├── strings.d ├── entity.d ├── mapprocessorgta2.d ├── gta1tokenizer.d ├── game.d └── vehicle.d ├── TODO.md ├── .gitignore └── README.md /data/sound_sequences.json: -------------------------------------------------------------------------------- 1 | { 2 | "BOWLING_ALLEY": { 3 | "index": 28, 4 | "type": "delay", 5 | "sounds": ["bowlingPins"], 6 | "delayMin": 2.5, 7 | "delayMax": 5.0 8 | }, 9 | "GANG_LOCKED_IN_BUS": { 10 | "index": 24, 11 | "type": "delay", 12 | "sounds": ["gangLocked1", "gangLocked2", "gangLocked3", "gangLocked4"], 13 | "delayMin": 1.5, 14 | "delayMax": 3.0 15 | } 16 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /data/shaders/block_opaque_fragment.glsl: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | precision mediump float; 4 | 5 | in mediump vec3 uvw; 6 | in lowp float brightness; 7 | 8 | out lowp vec4 color; 9 | 10 | layout(std140) uniform ShaderData { 11 | uniform mat4 mvp; 12 | vec4 ambientColor; 13 | uint remap[2048]; 14 | }; 15 | 16 | uniform sampler2DArray samplerBlockTextures; 17 | 18 | 19 | void main() { 20 | vec4 sample = texture(samplerBlockTextures, uvw); 21 | color = vec4(sample.r, sample.g, sample.b, 1.0) * vec4(brightness, brightness, brightness, 1.0) * ambientColor; 22 | } 23 | -------------------------------------------------------------------------------- /data/shaders/block_trans_fragment.glsl: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | precision mediump float; 4 | 5 | in mediump vec3 uvw; 6 | in lowp float brightness; 7 | 8 | out lowp vec4 color; 9 | 10 | layout(std140) uniform ShaderData { 11 | uniform mat4 mvp; 12 | vec4 ambientColor; 13 | uint remap[2048]; 14 | }; 15 | 16 | uniform sampler2DArray samplerBlockTextures; 17 | 18 | 19 | void main() { 20 | color = texture(samplerBlockTextures, uvw); 21 | if (color.a < 0.25) { 22 | discard; 23 | } 24 | color *= vec4(brightness, brightness, brightness, 1.0) * ambientColor; 25 | } 26 | -------------------------------------------------------------------------------- /data/shaders/sprite_fragment.glsl: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | precision mediump float; 4 | 5 | in mediump vec2 uv; 6 | in lowp float opacity; 7 | in lowp float darken; 8 | 9 | out lowp vec4 color; 10 | 11 | layout(std140) uniform ShaderData { 12 | uniform mat4 vp; 13 | vec4 ambientColor; 14 | uniform mat4 matrices[224]; 15 | }; 16 | 17 | uniform sampler2D samplerSpriteTexture; 18 | 19 | 20 | void main() { 21 | color = texture(samplerSpriteTexture, uv); 22 | if (color.a < 0.25) { 23 | discard; 24 | } 25 | color *= vec4(darken, darken, darken, opacity); 26 | color *= ambientColor; 27 | } 28 | -------------------------------------------------------------------------------- /data/music_gta1.json: -------------------------------------------------------------------------------- 1 | { 2 | "Radio01": { 3 | "file": "track1.wav" 4 | }, 5 | "Radio02": { 6 | "file": "track2.wav" 7 | }, 8 | "Radio03": { 9 | "file": "track3.wav" 10 | }, 11 | "Radio04": { 12 | "file": "track4.wav" 13 | }, 14 | "Radio05": { 15 | "file": "track5.wav" 16 | }, 17 | "Radio06": { 18 | "file": "track6.wav" 19 | }, 20 | "Radio07": { 21 | "file": "track7.wav" 22 | }, 23 | "Radio08": { 24 | "file": "track8.wav" 25 | }, 26 | "Ambient": { 27 | "file": "track9.wav" 28 | }, 29 | "PoliceRadio": { 30 | "file": "track10.wav" 31 | } 32 | } -------------------------------------------------------------------------------- /data/shaders/block_vertex.glsl: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | precision mediump float; 4 | 5 | layout(location = 0) in vec3 vertexPos; 6 | layout(location = 1) in vec2 vertexUV; 7 | layout(location = 2) in float vertexTexture; 8 | layout(location = 3) in float vertexBrightness; 9 | 10 | out mediump vec3 uvw; 11 | out lowp float brightness; 12 | 13 | layout(std140) uniform ShaderData { 14 | uniform mat4 mvp; 15 | vec4 ambientColor; 16 | uint remap[2048]; 17 | }; 18 | 19 | 20 | void main(void) { 21 | gl_Position = mvp * vec4(vertexPos, 1); 22 | float textureIndex = remap[int(vertexTexture)]; 23 | uvw = vec3(vertexUV.x, vertexUV.y, textureIndex); 24 | brightness = vertexBrightness; 25 | } 26 | -------------------------------------------------------------------------------- /data/shaders/sprite_vertex.glsl: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | precision mediump float; 4 | 5 | layout(location = 0) in vec3 vertexPos; 6 | layout(location = 1) in vec2 vertexUV; 7 | layout(location = 2) in float vertexOpacity; 8 | layout(location = 3) in float vertexDarken; 9 | layout(location = 4) in float vertexIndex; 10 | 11 | out mediump vec2 uv; 12 | out lowp float opacity; 13 | out lowp float darken; 14 | 15 | layout(std140) uniform ShaderData { 16 | uniform mat4 vp; 17 | vec4 ambientColor; 18 | uniform mat4 matrices[224]; 19 | }; 20 | 21 | 22 | void main(void) { 23 | gl_Position = vp * matrices[int(vertexIndex)] * vec4(vertexPos, 1); 24 | uv = vertexUV; 25 | opacity = vertexOpacity; 26 | darken = vertexDarken; 27 | } 28 | -------------------------------------------------------------------------------- /data/music_uk.json: -------------------------------------------------------------------------------- 1 | { 2 | "Radio01": { 3 | "file": "track1.wav" 4 | }, 5 | "Radio02": { 6 | "file": "track2.wav" 7 | }, 8 | "Radio03": { 9 | "file": "track3.wav" 10 | }, 11 | "Radio04": { 12 | "file": "track4.wav" 13 | }, 14 | "Radio05": { 15 | "file": "track5.wav" 16 | }, 17 | "Radio06": { 18 | "file": "track6.wav" 19 | }, 20 | "Radio07": { 21 | "file": "track7.wav" 22 | }, 23 | "Radio08": { 24 | "file": "track8.wav" 25 | }, 26 | "Radio09": { 27 | "file": "track9.wav" 28 | }, 29 | "Radio10": { 30 | "file": "track10.wav" 31 | }, 32 | "PoliceRadio": { 33 | "file": "track11.wav" 34 | }, 35 | "Menu1": { 36 | "file": "track12.wav" 37 | }, 38 | "Menu2": { 39 | "file": "track13.wav" 40 | }, 41 | "Menu3": { 42 | "file": "track14.wav" 43 | }, 44 | "Ambient": { 45 | "file": "track15.wav" 46 | } 47 | } -------------------------------------------------------------------------------- /GTAViewer.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{002A2DE9-8BB6-484D-9802-7E4AD4084715}") = "GTAViewer", "src\GTAViewer.visualdproj", "{41AF8791-F965-4C3F-872E-D9EF8298B2C1}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Profile|Win32 = Profile|Win32 10 | Release|Win32 = Release|Win32 11 | EndGlobalSection 12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 13 | {41AF8791-F965-4C3F-872E-D9EF8298B2C1}.Debug|Win32.ActiveCfg = Debug|Win32 14 | {41AF8791-F965-4C3F-872E-D9EF8298B2C1}.Debug|Win32.Build.0 = Debug|Win32 15 | {41AF8791-F965-4C3F-872E-D9EF8298B2C1}.Profile|Win32.ActiveCfg = Profile|Win32 16 | {41AF8791-F965-4C3F-872E-D9EF8298B2C1}.Profile|Win32.Build.0 = Profile|Win32 17 | {41AF8791-F965-4C3F-872E-D9EF8298B2C1}.Release|Win32.ActiveCfg = Release|Win32 18 | {41AF8791-F965-4C3F-872E-D9EF8298B2C1}.Release|Win32.Build.0 = Release|Win32 19 | EndGlobalSection 20 | GlobalSection(SolutionProperties) = preSolution 21 | HideSolutionNode = FALSE 22 | EndGlobalSection 23 | EndGlobal 24 | -------------------------------------------------------------------------------- /data/music_gta2.json: -------------------------------------------------------------------------------- 1 | { 2 | "Credits": { 3 | "file": "A.wav" 4 | }, 5 | "Menu": { 6 | "file": "D.wav" 7 | }, 8 | "HeadRadio": { 9 | "file": "1.wav" 10 | }, 11 | "RockstarRadio": { 12 | "file": "2.wav" 13 | }, 14 | "KREZ": { 15 | "file": "3.wav" 16 | }, 17 | "LoFiFM": { 18 | "file": "4.wav" 19 | }, 20 | "FuturoFM": { 21 | "file": "5.wav" 22 | }, 23 | "FutoroFMAM": { 24 | "file": "5a.wav" 25 | }, 26 | "FunamiFM": { 27 | "file": "6.wav" 28 | }, 29 | "FnuamiFMAM": { 30 | "file": "6a.wav" 31 | }, 32 | "LithiumFM": { 33 | "file": "7.wav" 34 | }, 35 | "LithiumFMAM": { 36 | "file": "7a.wav" 37 | }, 38 | "King1307FM": { 39 | "file": "8.wav" 40 | }, 41 | "King1307FMAM": { 42 | "file": "8a.wav" 43 | }, 44 | "OsmosisRadio": { 45 | "file": "9.wav" 46 | }, 47 | "OsmosisRadioAM": { 48 | "file": "9a.wav" 49 | }, 50 | "HeavenlyRadio": { 51 | "file": "10.wav" 52 | }, 53 | "HeavenlyRadioAM": { 54 | "file": "10a.wav" 55 | }, 56 | "KGBH": { 57 | "file": "11.wav" 58 | }, 59 | "KGBHAM": { 60 | "file": "11a.wav" 61 | }, 62 | "Ambient": { 63 | "file": "12.wav" 64 | }, 65 | "PoliceRadio": { 66 | "file": "101a.wav" 67 | } 68 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Dennis Meuwissen 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | License does not apply to code and information found in the ./info directory. -------------------------------------------------------------------------------- /src/missionreader.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.script.missionreader; 29 | 30 | import game.script.missionlist; 31 | 32 | 33 | public abstract class MissionReader { 34 | 35 | protected Mission[] _missions; 36 | 37 | 38 | public Mission[] getMissions() { 39 | return _missions; 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /src/obstacle.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.entities.obstacle; 29 | 30 | import game.entities.entity; 31 | 32 | import game.style.style; 33 | 34 | 35 | public final class EntityObstacle : Entity { 36 | this(EntityType type) { 37 | super(); 38 | 39 | this.initializeFromType(type); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/decoration.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.entities.decoration; 29 | 30 | import game.entities.entity; 31 | 32 | import game.style.style; 33 | 34 | 35 | public final class EntityDecoration : Entity { 36 | this(EntityType type) { 37 | super(); 38 | 39 | this.initializeFromType(type); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/script.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.script.script; 29 | 30 | import game.gamestate_level; 31 | 32 | import game.style.style; 33 | 34 | import game.map.map; 35 | 36 | 37 | public abstract class Script { 38 | public abstract void init(GameStateLevel state, Style style, Map map); 39 | public abstract void run(); 40 | } -------------------------------------------------------------------------------- /src/rectangle.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module util.rectangle; 29 | 30 | 31 | public struct Rectangle { 32 | int x1; 33 | int y1; 34 | int x2; 35 | int y2; 36 | 37 | @property public int width() { 38 | return x2 - x1; 39 | } 40 | 41 | @property public int height() { 42 | return y2 - y1; 43 | } 44 | } -------------------------------------------------------------------------------- /src/gamestate.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.gamestate; 29 | 30 | import game.game; 31 | 32 | 33 | public abstract class GameState { 34 | 35 | private bool _isEnabled = true; 36 | 37 | public abstract void update(const double delta); 38 | public abstract void render(const double lerp); 39 | public abstract void input(ubyte* keys); 40 | 41 | @property 42 | public bool isEnabled() { 43 | return _isEnabled; 44 | } 45 | } -------------------------------------------------------------------------------- /src/fasthash.d: -------------------------------------------------------------------------------- 1 | /* 2 | https://code.google.com/p/fast-hash 3 | */ 4 | 5 | module util.fasthash; 6 | 7 | /** 8 | * fasthash64 - 64-bit implementation of fasthash 9 | * @buf: data buffer 10 | * @len: data size 11 | * @seed: the seed 12 | */ 13 | public ulong fasthash64(const void* buf, const size_t len, const ulong seed) { 14 | const ulong m = 0x880355f21e6d1965UL; 15 | 16 | ulong* pos = cast(ulong*)buf; 17 | const ulong* end = pos + (len / 8); 18 | ulong h = seed ^ (len * m); 19 | ulong v; 20 | 21 | while (pos != end) { 22 | v = *pos++; 23 | h ^= mix(v); 24 | h *= m; 25 | } 26 | 27 | switch (len & 7) { 28 | case 7: v ^= cast(ulong)pos[6] << 48; goto case; 29 | case 6: v ^= cast(ulong)pos[5] << 40; goto case; 30 | case 5: v ^= cast(ulong)pos[4] << 32; goto case; 31 | case 4: v ^= cast(ulong)pos[3] << 24; goto case; 32 | case 3: v ^= cast(ulong)pos[2] << 16; goto case; 33 | case 2: v ^= cast(ulong)pos[1] << 8; goto case; 34 | case 1: 35 | v ^= cast(ulong)pos[0]; 36 | h ^= mix(v); 37 | h *= m; 38 | goto default; 39 | default: 40 | } 41 | 42 | return mix(h); 43 | } 44 | 45 | /** 46 | * fasthash32 - 32-bit implementation of fasthash 47 | * @buf: data buffer 48 | * @len: data size 49 | * @seed: the seed 50 | */ 51 | public uint fasthash32(const void *buf, const size_t len, const uint seed) { 52 | const ulong h = fasthash64(buf, len, seed); 53 | 54 | // the following trick converts the 64-bit hashcode to Fermat 55 | // residue, which shall retain information from both the higher 56 | // and lower parts of hashcode. 57 | return cast(uint)(h - (h >> 32)); 58 | } 59 | 60 | // Compression function for Merkle-Damgard construction. 61 | // This function is generated using the framework provided. 62 | private ulong mix(ulong h) { 63 | h ^= h >> 23; 64 | h *= 0x2127599bf4325c37UL; 65 | h ^= h >> 47; 66 | 67 | return h; 68 | } -------------------------------------------------------------------------------- /src/mapprocessor.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.map.mapprocessor; 29 | 30 | import game.map.map; 31 | 32 | import game.style.style; 33 | 34 | 35 | public abstract class MapProcessor { 36 | 37 | protected Map _map; 38 | protected ubyte _shadingLevel; 39 | 40 | 41 | this(Map map) { 42 | _map = map; 43 | } 44 | 45 | public void setShadingLevel(const ubyte shadingLevel) { 46 | _shadingLevel = shadingLevel; 47 | } 48 | 49 | public abstract void process(Style style); 50 | 51 | } -------------------------------------------------------------------------------- /src/convert.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module util.convert; 29 | 30 | import util.vector3; 31 | 32 | 33 | public float convertAngle(int angle) { 34 | return (360.0 - (angle / 1024.0) * 360.0) % 360.0; 35 | } 36 | 37 | public Vector3 convertToPixels(const Vector3 pos) { 38 | return Vector3(pos.x * 64 + 32, pos.y * 64 + 32, 384 - (pos.z * 64)); 39 | } 40 | 41 | public Vector3 flipZ(const Vector3 pos) { 42 | return Vector3(pos.x, pos.y, 384 - pos.z); 43 | } 44 | 45 | public float flipRotation(const float rotation) { 46 | return (360 - rotation) % 360; 47 | } -------------------------------------------------------------------------------- /src/buffer.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module audio.buffer; 29 | 30 | import std.stdio; 31 | 32 | import derelict.openal.al; 33 | 34 | 35 | public final class AudioBuffer { 36 | 37 | private ALuint _id; 38 | 39 | 40 | this(const ubyte[] data, const ALenum format, const uint sampleRate) { 41 | alGenBuffers(1, &_id); 42 | if (data.length) { 43 | alBufferData(_id, format, &data[0], data.length, sampleRate); 44 | } 45 | } 46 | 47 | ~this() { 48 | alDeleteBuffers(1, &_id); 49 | } 50 | 51 | @property public ALuint id() { 52 | return _id; 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | Milestone I 2 | ----------- 3 | * Fix sloped side lid texture rotation 4 | * Sound sequences? 5 | * Rework sound playback so that a single entity can play any number of sounds. 6 | * Play music 7 | * WAV reader 8 | * IMA(1\2?) ADPCM decoder 9 | 10 | Milestone II 11 | ------------ 12 | * Light rendering 13 | * Font rendering 14 | * Timing debug text 15 | * Debug primitive rendering 16 | * Toggle rendering of invisible (map) data 17 | * Road direction arrows 18 | * Map zones 19 | * Trigger radiuses 20 | * Sounds positions 21 | * Light positions 22 | * Junction system 23 | * Entity names\data 24 | 25 | Milestone III 26 | ------------- 27 | * Generators (that don't just spawn the item they generate) 28 | * Conveyors 29 | * Destructors 30 | * Cranes 31 | * Traffic lights 32 | 33 | Milestone IV 34 | ------------ 35 | * Ped spawning, rendering 36 | * Player ped 37 | * Map controls to commands that can be serialized to a demo 38 | * Use regular ped state machine for movement 39 | * Basic entity vs. map collision detection 40 | * Ambient peds 41 | * Visible border block iterator 42 | * Basic ped AI 43 | * Ped state machine 44 | * Simple ped collision detection 45 | * Slope\special block ped collision detection 46 | 47 | Milestone V 48 | ----------- 49 | * Proper script execution 50 | * GTA2 functions\gosubs 51 | * Init\run scripts 52 | * To EXEC or not to EXEC? 53 | * Map zones 54 | * Radio stations 55 | 56 | Milestone VI 57 | ------------ 58 | * Weapons 59 | * HUD 60 | 61 | Unlikely if ever 62 | ---------------- 63 | * Extended ped AI 64 | * Fleeing 65 | * Fistfights 66 | * Cops! 67 | * Controller support 68 | * Peds can enter cars 69 | * Peds can drive cars 70 | * Car vs. map collision detection 71 | * Car physics 72 | * Prototype first 73 | * Separate for GTA1\UK and GTA2 74 | * Special cars (tanks, firetrucks) 75 | * Ambient cars 76 | * Junctions 77 | * GTA1\UK 78 | * GTA2 79 | * Car weapons 80 | * Police AI 81 | * Car chasing 82 | * SWAT 83 | * FBI 84 | * Army 85 | * Roadblocks 86 | * Service AI 87 | * Paramedics 88 | * Fire dept. 89 | * Map-wide pathfinding 90 | * Death, restart 91 | * Script triggers 92 | * Complete scripting 93 | * GTA1\UK 94 | * GTA2 95 | * Main menu -------------------------------------------------------------------------------- /src/typenames.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.entities.typenames; 29 | 30 | import std.stdio; 31 | import std.json; 32 | import std.file; 33 | import std.conv; 34 | import std.string; 35 | 36 | import game.style.style; 37 | 38 | 39 | public final class TypeNames(T) { 40 | 41 | private string[T] _typeNames; 42 | 43 | 44 | this(const string fileName) { 45 | const JSONValue json = parseJSON(readText(fileName)); 46 | 47 | foreach (string key, JSONValue val; json.object) { 48 | _typeNames[to!T(key)] = val.str; 49 | } 50 | } 51 | 52 | public string getName(const T modelId) { 53 | if (modelId !in _typeNames) { 54 | throw new Exception(format("Entity type model id '%d' is not known.", modelId)); 55 | } 56 | 57 | return _typeNames[modelId]; 58 | } 59 | } -------------------------------------------------------------------------------- /src/mapreader.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.map.mapreader; 29 | 30 | import std.stdio; 31 | import std.string; 32 | 33 | import game.map.block; 34 | import game.map.map; 35 | 36 | import game.style.style; 37 | 38 | import game.entities.entity; 39 | import game.entities.typenames; 40 | 41 | import util.binaryfile; 42 | import util.vector3; 43 | 44 | 45 | public abstract class MapReader { 46 | 47 | protected BinaryFile _file; 48 | 49 | 50 | this(BinaryFile file) { 51 | _file = file; 52 | } 53 | 54 | public abstract void readHeader(); 55 | 56 | public abstract Block[][][] readBlockData(); 57 | public abstract MapEntity[] readMapEntities(TypeNames!EntityTypeIndex typeNames); 58 | public abstract Route[] readRoutes(); 59 | public abstract Location[] readLocations(); 60 | public abstract Zone[] readZones(); 61 | public abstract Light[] readLights(); 62 | public abstract Animation[] readAnimations(); 63 | public abstract JunctionNetwork readJunctionNetwork(); 64 | 65 | @property public abstract MapCoord width(); 66 | @property public abstract MapCoord height(); 67 | @property public abstract MapCoord depth(); 68 | } 69 | -------------------------------------------------------------------------------- /src/enums.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module render.enums; 29 | 30 | import derelict.opengl; 31 | 32 | 33 | public enum BufferUsage : GLenum { 34 | STREAM_DRAW = GL_STREAM_DRAW, 35 | STREAM_READ = GL_STREAM_READ, 36 | STREAM_COPY = GL_STREAM_COPY, 37 | STATIC_DRAW = GL_STATIC_DRAW, 38 | STATIC_READ = GL_STATIC_READ, 39 | STATIC_COPY = GL_STATIC_COPY, 40 | DYNAMIC_DRAW = GL_DYNAMIC_DRAW, 41 | DYNAMIC_READ = GL_DYNAMIC_READ, 42 | DYNAMIC_COPY = GL_DYNAMIC_COPY, 43 | } 44 | 45 | public enum PrimitiveType : GLenum { 46 | TRIANGLES = GL_TRIANGLES, 47 | TRIANGLE_STRIPS = GL_TRIANGLE_STRIP, 48 | TRIANGLE_FANS = GL_TRIANGLE_FAN 49 | } 50 | 51 | public enum TextureFormat : GLenum { 52 | RED = GL_RED, 53 | RGBA = GL_RGBA, 54 | BGRA = GL_BGRA 55 | } 56 | 57 | public enum InternalTextureFormat : GLenum { 58 | R8 = GL_R8, 59 | RGBA = GL_RGBA 60 | } 61 | 62 | public enum TextureArrayTarget : GLenum { 63 | TEXTURE_1D, 64 | TEXTURE_2D 65 | } 66 | 67 | public enum TextureFilter : GLenum { 68 | NEAREST = GL_NEAREST, 69 | LINEAR = GL_LINEAR 70 | } 71 | 72 | public enum TextureWrapMode : GLenum { 73 | CLAMP = GL_CLAMP_TO_EDGE, 74 | WRAP = GL_REPEAT 75 | } -------------------------------------------------------------------------------- /src/uniformbuffer.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module render.uniformbuffer; 29 | 30 | import std.stdio; 31 | 32 | import derelict.opengl; 33 | 34 | 35 | public final class UniformBuffer(T) { 36 | 37 | private GLuint[BUFFER_COUNT] _id; 38 | private int _currentBuffer; 39 | 40 | private static immutable BUFFER_COUNT = 1; 41 | 42 | 43 | public this() { 44 | glGenBuffers(BUFFER_COUNT, &_id[0]); 45 | 46 | for (int buffer = 0; buffer < BUFFER_COUNT; buffer++) { 47 | glBindBuffer(GL_UNIFORM_BUFFER, _id[buffer]); 48 | glBufferData(GL_UNIFORM_BUFFER, T.sizeof, null, GL_STREAM_DRAW); 49 | } 50 | } 51 | 52 | public void update(const T data) { 53 | _currentBuffer = (_currentBuffer + 1) % BUFFER_COUNT; 54 | 55 | glBindBuffer(GL_UNIFORM_BUFFER, _id[_currentBuffer]); 56 | T* dest = cast(T*)glMapBufferRange(GL_UNIFORM_BUFFER, 0, T.sizeof, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); 57 | *dest = data; 58 | glUnmapBuffer(GL_UNIFORM_BUFFER); 59 | } 60 | 61 | public void bindBase(const GLint blockIndex) { 62 | glBindBufferBase(GL_UNIFORM_BUFFER, blockIndex, _id[_currentBuffer]); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/powerup.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.entities.powerup; 29 | 30 | import game.entities.entity; 31 | 32 | import game.style.style; 33 | 34 | 35 | public final class EntityPowerup : Entity { 36 | 37 | private uint _value; 38 | private double _animTime = 0.0; 39 | 40 | 41 | this(EntityType type, const uint value) { 42 | super(); 43 | 44 | this.initializeFromType(type); 45 | 46 | _value = value; 47 | // TODO: Value == 0 means use a default value. 48 | // >= 100 for weapons means its a kill frenzy. Number indicates amount of frames the kill frenzy lasts, 25 FPS. 100 = infinite kill frenzy. 49 | // 1 - 32 for info shields are fxt text references for help 50 | } 51 | 52 | override public void update(const double delta) { 53 | super.update(delta); 54 | 55 | if (_spriteTexture !is null && _spriteTexture.subTextures.length) { 56 | _animTime += delta; 57 | if (_animTime >= 0.12) { 58 | _frame += 1; 59 | if (_frame >= _spriteTexture.subTextures.length) { 60 | _frame = 0; 61 | } 62 | _animTime = 0; 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/stylereader.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.style.stylereader; 29 | 30 | import game.map.block; 31 | 32 | import game.style.style; 33 | 34 | import game.entities.entity; 35 | import game.entities.vehicle; 36 | import game.entities.typenames; 37 | 38 | import util.binaryfile; 39 | import util.color; 40 | 41 | 42 | public abstract class StyleReader { 43 | 44 | protected BinaryFile _file; 45 | 46 | 47 | this(BinaryFile file) { 48 | _file = file; 49 | } 50 | 51 | public abstract void readHeader(); 52 | public abstract BlockTexture[] readBlockTextures(); 53 | public abstract Animation[] readAnimations(); 54 | public abstract Palette[] readPalettes(); 55 | public abstract VirtualPalette[] readVirtualPalettes(); 56 | public abstract void readEntityTypes(ref EntityType[string] entityTypes, TypeNames!EntityTypeIndex entityTypeNames); 57 | public abstract VehicleType[] readVehicleTypes(); 58 | public abstract SpriteFrame[] readSpriteFrames(); 59 | public abstract Font[] readFonts(); 60 | public abstract ushort[] readPaletteBases(); 61 | 62 | @property public abstract ushort blockTexturePaletteCount(); 63 | @property public abstract BlockTextureIndex sideTextureCount(); 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/vector3.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module util.vector3; 29 | 30 | import util.binaryfile; 31 | 32 | 33 | public struct Vector3 { 34 | float x = 0.0; 35 | float y = 0.0; 36 | float z = 0.0; 37 | 38 | public void readUByteFrom(BinaryFile file) { 39 | x = cast(float)file.readUByte(); 40 | y = cast(float)file.readUByte(); 41 | z = cast(float)file.readUByte(); 42 | } 43 | 44 | public void readUShortFrom(BinaryFile file) { 45 | x = cast(float)file.readUShort(); 46 | y = cast(float)file.readUShort(); 47 | z = cast(float)file.readUShort(); 48 | } 49 | 50 | bool opEquals(Vector3 other) { 51 | return (x == other.x && y == other.y && z == other.z); 52 | } 53 | 54 | void opOpAssign(string op)(Vector3 other) { 55 | mixin("x " ~ op ~ "= other.x;"); 56 | mixin("y " ~ op ~ "= other.y;"); 57 | mixin("z " ~ op ~ "= other.z;"); 58 | } 59 | 60 | public Vector3 opBinary(string op)(const Vector3 l) if (op == "+" || op == "-") { 61 | mixin("return Vector3(x " ~ op ~ " l.x, y " ~ op ~ " l.y, z " ~ op ~ " l.z);"); 62 | } 63 | 64 | public Vector3 opBinary(string op)(const real l) if (op == "*" || op == "/") { 65 | mixin("return Vector3(x " ~ op ~ " l, y " ~ op ~ " l, z " ~ op ~ " l);"); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /data/entitytypenames_gta1.json: -------------------------------------------------------------------------------- 1 | { 2 | "0": "G1Tire1", 3 | "1": "G1Tire2", 4 | "2": "G1BinLid", 5 | "3": "G1Bin", 6 | "4": "G1Cone", 7 | "5": "G1Flowers", 8 | "6": "G1Barrier", 9 | "7": "G1Cross", 10 | "8": "G1Bench", 11 | "9": "G1Gravestone", 12 | "10": "G1Smoke1", 13 | "11": "G1Skid", 14 | "12": "G1SkidBlood", 15 | "13": "G1Spark", 16 | "14": "G1TrainDoors", 17 | "15": "G1Blood", 18 | "16": "G1Junction1", 19 | "17": "G1Junction2", 20 | "18": "G1Fire1", 21 | "19": "G1Fire2", 22 | "20": "G1Water", 23 | "21": "G1SkidDirt1", 24 | "22": "G1SkidDirt2", 25 | "23": "G1SkidDirt3", 26 | "24": "G1Barrier3", 27 | "25": "G1Bumper", 28 | "26": "G1Tree1", 29 | "27": "G1Tree2", 30 | "28": "G1Tree3", 31 | "29": "G1Unknown29", 32 | "30": "G1Crane", 33 | "31": "G1Missile", 34 | "32": "G1Turret", 35 | "33": "G1TankGun", 36 | "34": "G1TankGunBarrel", 37 | "35": "G1Coffin", 38 | "36": "G1XCrate", 39 | "37": "G1MissileCrate", 40 | "38": "G1TankGunWrecked", 41 | "39": "G1TankGunBarrelWrecked", 42 | "40": "G1Phone", 43 | "41": "G1Briefcase", 44 | "42": "G1Unknown42", 45 | "43": "G1Rubble3", 46 | "44": "G1Rubble2", 47 | "45": "G1Rubble1", 48 | "46": "G1LFire", 49 | "47": "G1DropoffPoint", 50 | "48": "G1WaterJet", 51 | "49": "G1WaterJetEnd", 52 | "50": "G1Firehose", 53 | "51": "G1BigSmoke", 54 | "52": "G1Unknown52", 55 | "53": "G1Exhaust", 56 | "54": "G1Splash", 57 | "55": "G1FireTruckArm1", 58 | "56": "G1FireTruckArm2", 59 | "57": "G1FireTruckArm3", 60 | "58": "G1FireTruckArm4", 61 | "59": "G1SwatGun", 62 | "60": "G1CheckFlag", 63 | "61": "G1Unknown61", 64 | "62": "G1Unknown62", 65 | "63": "G1FirstBlood", 66 | "64": "G1Body", 67 | "65": "G1Unknown65", 68 | "66": "G1Unknown66", 69 | "67": "G1Smoke2", 70 | "68": "G1Unknown68", 71 | "69": "G1Barrel2", 72 | "70": "G1TVCamera", 73 | "71": "G1Unknown71", 74 | "72": "G1Unknown72", 75 | "73": "G1Unknown73", 76 | "74": "G1Bullet", 77 | "75": "G1Fireball", 78 | "76": "G1GasCan", 79 | "77": "G1BloodSmear", 80 | "78": "G1PowerupPistol", 81 | "79": "G1PowerupMachinegun", 82 | "80": "G1PowerupRocketLauncher", 83 | "81": "G1PowerupFlamethrower", 84 | "82": "G1PowerupGasCan", 85 | "83": "G1PowerupQuestionMark", 86 | "84": "G1Crate", 87 | "85": "G1CrateExploding", 88 | "86": "G1Beachball", 89 | "87": "G1Bollard", 90 | "88": "G1FlagWaving", 91 | "89": "G1Rubbish", 92 | "90": "G1Unknown90", 93 | "91": "G1Unknown91", 94 | "92": "G1HelicopterBlade", 95 | "93": "G1RubbishExploding", 96 | "94": "G1Helipad", 97 | "95": "G1PowerupInfo", 98 | "96": "G1PowerupShield", 99 | "97": "G1PowerupLightning", 100 | "98": "G1PowerupKey", 101 | "99": "G1PowerupHeart", 102 | "100": "G1PowerupExtra", 103 | "101": "G1PowerupCoin" 104 | } -------------------------------------------------------------------------------- /src/gta1initcommands.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.script.gta1initcommands; 29 | 30 | 31 | public enum GTA1InitCommandIndex : ubyte { 32 | ALT_DAMAGE_TRIG, 33 | BARRIER, 34 | BASIC_BARRIER, 35 | BLOCK_INFO, 36 | BOMBSHOP, 37 | BOMBSHOP_COST, 38 | CANNON_START, 39 | CAR, 40 | CARBOMB, 41 | CARBOMB_TRIG, 42 | CARDESTROY_TRIG, 43 | CARSTUCK_TRIG, 44 | CARTRIGGER, 45 | CARWAIT_TRIG, 46 | CHOPPER_ENDPOINT, 47 | CORRECT_CAR_TRIG, 48 | CORRECT_MOD_TRIG, 49 | COUNTER, 50 | CRANE, 51 | DAMAGE_TRIG, 52 | DOOR, 53 | DRIVER, 54 | DRIVER_PARK, 55 | DUM_MISSION_TRIG, 56 | DUM_PED_BLOCK_TRIG, 57 | DUMMY, 58 | FINAL_MULTI, 59 | FUTURE, 60 | FUTURECAR, 61 | FUTUREPED, 62 | GTA_DEMAND, 63 | GUN_SCREEN, 64 | GUN_SCREEN_TRIG, 65 | GUN_TRIG, 66 | HELLS, 67 | MID_MULTI_SETUP, 68 | MIDPOINT_MULTI, 69 | MISSION_COUNTER, 70 | MISSION_TOTAL, 71 | MODEL_BARRIER, 72 | MODEL_DOOR, 73 | MOVING_TRIG, 74 | MOVING_TRIG_HIRED, 75 | MPHONES, 76 | OBJECT, 77 | ONETRIGGER, 78 | PARKED, 79 | PARKED_PIXELS, 80 | PED, 81 | PEDCAR_TRIG, 82 | PHONE_TOGG, 83 | PLAYER, 84 | POWERUP, 85 | SECRET_MISSION_COUNTER, 86 | SETUP_SPEED, 87 | SPECIFIC_BARR, 88 | SPECIFIC_DOOR, 89 | SPRAY, 90 | STOPPED, 91 | TARGET, 92 | TARGET_SCORE, 93 | TELEPHONE, 94 | TRIGGER, 95 | } 96 | -------------------------------------------------------------------------------- /src/timer.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module util.timer; 29 | 30 | import std.stdio; 31 | 32 | import derelict.sdl2.sdl; 33 | 34 | 35 | private ulong startTime; 36 | private ulong endTime; 37 | 38 | private double performanceFrequency; 39 | 40 | 41 | public void timerInit() { 42 | performanceFrequency = cast(double)SDL_GetPerformanceFrequency(); 43 | } 44 | 45 | public void timerStart() { 46 | startTime = timerGetCounter(); 47 | } 48 | 49 | public ulong timerStop() { 50 | endTime = timerGetCounter(); 51 | return endTime - startTime; 52 | } 53 | 54 | public void timerWait(const ulong delay) { 55 | static long fudge; 56 | 57 | // Suspend thread and measure how long the suspension lasted. 58 | ulong delayStart = timerGetCounter(); 59 | if (cast(int)(delay - fudge) / 1000 < 0) { 60 | fudge -= 2; 61 | return; 62 | } 63 | SDL_Delay(cast(int)(delay - fudge) / 1000); 64 | long delayTime = timerGetCounter() - delayStart; 65 | 66 | // If the thread was suspended too long, wait less next time. 67 | if (delayTime > delay) { 68 | fudge += 8; 69 | 70 | // Busywait the remaining period. 71 | } else if (delayTime < delay) { 72 | delayStart = timerGetCounter(); 73 | while(timerGetCounter() - delayStart < delay - delayTime) { 74 | SDL_Delay(0); 75 | } 76 | fudge -= 4; 77 | } 78 | } 79 | 80 | public ulong timerGetCounter() { 81 | return cast(ulong)((SDL_GetPerformanceCounter() / performanceFrequency) * 1000000); 82 | } -------------------------------------------------------------------------------- /src/missionlist.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.script.missionlist; 29 | 30 | import std.path; 31 | import std.string; 32 | 33 | import game.game; 34 | 35 | import game.script.missionreader; 36 | import game.script.missionreadergta1; 37 | import game.script.missionreadergta2; 38 | import game.script.script; 39 | 40 | import util.console; 41 | 42 | 43 | public enum MissionType : ubyte { 44 | UNKNOWN, 45 | MAIN, 46 | BONUS, 47 | MULTIPLAYER 48 | } 49 | 50 | 51 | public struct Mission { 52 | string name; 53 | string cityName; 54 | MissionType type; 55 | 56 | string mapName; 57 | string styleName; 58 | string audioName; 59 | 60 | Script script; 61 | } 62 | 63 | 64 | public final class MissionList { 65 | 66 | private Mission[] _missions; 67 | 68 | 69 | this() { 70 | MissionReader reader; 71 | CVar cvarBasePath = CVars.get("g_basedir"); 72 | 73 | const string path = buildPath("base", cvarBasePath.strVal); 74 | if (gameMode == GameMode.GTA1) { 75 | reader = new MissionReaderGTA1(path); 76 | } else if (gameMode == GameMode.GTA2) { 77 | reader = new MissionReaderGTA2(path); 78 | } 79 | 80 | _missions = reader.getMissions(); 81 | } 82 | 83 | public Mission getMission(const uint index) { 84 | if (index >= _missions.length) { 85 | throw new Exception(format("Mission index '%d' does not exist.", index)); 86 | } 87 | return _missions[index]; 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /src/mapprocessorgta1.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.map.mapprocessorgta1; 29 | 30 | import game.map.map; 31 | import game.map.mapprocessor; 32 | import game.map.block; 33 | 34 | import game.style.style; 35 | 36 | 37 | public final class MapProcessorGTA1 : MapProcessor { 38 | 39 | this(Map map) { 40 | super(map); 41 | } 42 | 43 | override public void process(Style style) { 44 | remapBlockTextures(style); 45 | } 46 | 47 | private void remapBlockTextures(Style style) { 48 | Block[][][] blocks = _map.getBlocks(); 49 | 50 | const uint normalTextureCount = style.getBlockTextures().length / 4; 51 | for (MapCoord y = 0; y < _map.height; y++) { 52 | for (MapCoord x = 0; x < _map.width; x++) { 53 | foreach (ref Block block; blocks[x][y]) { 54 | // Lid texture offset and remap; 55 | if (block.faces[FaceIndex.LID].texture) { 56 | block.faces[FaceIndex.LID].texture += style.sideTextureCount; 57 | block.faces[FaceIndex.LID].texture += block.lidTextureRemap * normalTextureCount; 58 | } 59 | 60 | // Shadowing. 61 | if (block.faces[FaceIndex.BOTTOM].texture) { 62 | block.faces[FaceIndex.BOTTOM].texture += normalTextureCount * 1; 63 | } 64 | if (block.faces[FaceIndex.LEFT].texture) { 65 | block.faces[FaceIndex.LEFT].texture += normalTextureCount * 2; 66 | } 67 | if (block.faces[FaceIndex.RIGHT].texture) { 68 | block.faces[FaceIndex.RIGHT].texture += normalTextureCount * 3; 69 | } 70 | } 71 | } 72 | } 73 | } 74 | 75 | } -------------------------------------------------------------------------------- /src/log.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module util.log; 29 | 30 | import std.stdio; 31 | 32 | import core.sys.windows.windows; 33 | 34 | 35 | /** 36 | * Color bits for character attributes. 37 | * See MSDN for 38 | * more information about these attributes. 39 | */ 40 | public enum ColorBits : ushort { 41 | FOREGROUND_BLUE = 1, 42 | FOREGROUND_GREEN = 2, 43 | FOREGROUND_RED = 4, 44 | FOREGROUND_INTENSE = 8, 45 | BACKGROUND_BLUE = 16, 46 | BACKGROUND_GREEN = 32, 47 | BACKGROUND_RED = 64, 48 | BACKGROUND_INTENSE = 128 49 | } 50 | 51 | /// Predefined colors. 52 | public enum Color : ushort { 53 | NORMAL = 0xFFFF, 54 | ERROR = ColorBits.FOREGROUND_RED | ColorBits.FOREGROUND_INTENSE, 55 | WARNING = ColorBits.FOREGROUND_RED | ColorBits.FOREGROUND_GREEN | ColorBits.FOREGROUND_INTENSE, 56 | DEBUG = ColorBits.FOREGROUND_INTENSE, 57 | } 58 | 59 | 60 | public final class Log { 61 | 62 | /// A handle to the current console instance. 63 | private static HANDLE _consoleHandle; 64 | 65 | /// Stored console buffer configuration. 66 | private static CONSOLE_SCREEN_BUFFER_INFO _bufferInfo; 67 | 68 | 69 | static this() { 70 | _consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); 71 | 72 | // Store the console buffer configuration for later restoration. 73 | GetConsoleScreenBufferInfo(_consoleHandle, &_bufferInfo); 74 | } 75 | 76 | public static void write(Ushort, Char, A...)(in Ushort color, in Char[] fmt, A args) { 77 | if (color != 0xFFFF) { 78 | SetConsoleTextAttribute(_consoleHandle, color); 79 | } 80 | writefln(fmt, args); 81 | if (color != 0xFFFF) { 82 | SetConsoleTextAttribute(_consoleHandle, _bufferInfo.wAttributes); 83 | } 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/shader.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module render.shader; 29 | 30 | import std.stdio; 31 | import std.file; 32 | import std.string; 33 | import std.path; 34 | import std.conv; 35 | 36 | import derelict.opengl; 37 | 38 | import util.log; 39 | 40 | 41 | public enum ShaderType { 42 | VERTEX = GL_VERTEX_SHADER, 43 | FRAGMENT = GL_FRAGMENT_SHADER 44 | } 45 | 46 | public final class Shader { 47 | 48 | private ShaderType _type; 49 | private GLuint _id; 50 | private string _name; 51 | 52 | 53 | this(const string fileName, const ShaderType type) { 54 | _name = baseName(fileName, ".glsl"); 55 | Log.write(Color.NORMAL, "Compiling %s shader %s...", to!string(type), _name); 56 | 57 | _id = glCreateShader(type); 58 | 59 | const(char)* code = readText(fileName).toStringz(); 60 | glShaderSource(_id, 1, &code, null); 61 | glCompileShader(_id); 62 | 63 | GLint status; 64 | glGetShaderiv(_id, GL_COMPILE_STATUS, &status); 65 | if (status == GL_FALSE) { 66 | GLint logLength; 67 | glGetShaderiv(_id, GL_INFO_LOG_LENGTH, &logLength); 68 | 69 | char[] errorLog; 70 | if (logLength > 0) { 71 | errorLog = new char[logLength]; 72 | glGetShaderInfoLog(_id, logLength, &logLength, &errorLog[0]); 73 | } else { 74 | errorLog = cast(char[])"Unknown error, no log information given."; 75 | } 76 | 77 | glDeleteShader(_id); 78 | 79 | throw new Exception(format("Cannot compile shader. %s", cast(char[])errorLog)); 80 | } 81 | } 82 | 83 | ~this() { 84 | if (_id) { 85 | glDeleteShader(_id); 86 | } 87 | } 88 | 89 | @property 90 | public GLuint id() { 91 | return _id; 92 | } 93 | 94 | @property 95 | public string name() { 96 | return _name; 97 | } 98 | 99 | } -------------------------------------------------------------------------------- /src/animations.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.animations; 29 | 30 | import std.stdio; 31 | import std.string; 32 | 33 | import derelict.opengl; 34 | 35 | import game.style.style; 36 | 37 | import game.map.block; 38 | 39 | 40 | private struct LoadedAnim { 41 | BlockTextureIndex textureIndex; 42 | double delay; 43 | BlockTextureIndex[] frames; 44 | double counter; 45 | ushort frameIndex; 46 | 47 | this(Animation anim) { 48 | textureIndex = anim.textureIndex; 49 | delay = anim.delay; 50 | frames = anim.frames; 51 | frameIndex = 0; 52 | counter = delay; 53 | } 54 | } 55 | 56 | 57 | public final class Animations { 58 | 59 | private LoadedAnim[] _animations; 60 | private GLuint[MAX_BLOCKTEXTURE_INDICES * 4] _indices; 61 | 62 | 63 | public this(Style style) { 64 | foreach (Animation anim; style.getAnimations()) { 65 | _animations ~= LoadedAnim(anim); 66 | } 67 | 68 | // Build initial list of block texture indices. 69 | const int textureCount = style.getBlockTextures().length; 70 | if (textureCount > MAX_BLOCKTEXTURE_INDICES) { 71 | throw new Exception(format("Too many textures for animating. Maximum is %d.", MAX_BLOCKTEXTURE_INDICES)); 72 | } 73 | for (uint index = 0; index < textureCount; index++) { 74 | _indices[index * 4] = index; 75 | } 76 | } 77 | 78 | public void update(const double delta) { 79 | foreach(ref LoadedAnim anim; _animations) { 80 | anim.counter -= delta; 81 | while (anim.counter <= 0.0) { 82 | anim.counter += anim.delay; 83 | 84 | anim.frameIndex++; 85 | if (anim.frameIndex >= anim.frames.length) { 86 | anim.frameIndex = 0; 87 | } 88 | 89 | _indices[anim.textureIndex * 4] = anim.frames[anim.frameIndex]; 90 | } 91 | } 92 | } 93 | 94 | public GLuint[] getIndices() { 95 | return _indices; 96 | } 97 | 98 | } -------------------------------------------------------------------------------- /src/packnode.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module util.packnode; 29 | 30 | import util.rectangle; 31 | 32 | 33 | // Binary partition based bin packing for arbitrarily sized nodes. 34 | public struct PackNode { 35 | Rectangle rectangle; 36 | PackNode* right; 37 | PackNode* bottom; 38 | 39 | 40 | // Creates a new node from absolute coordinates. 41 | this(const Rectangle size) { 42 | rectangle = size; 43 | } 44 | 45 | // Creates a new node from a width and height. X and Y are assumed to be 0. 46 | this(const int width, const int height) { 47 | rectangle = Rectangle(0, 0, width, height); 48 | } 49 | 50 | // Attempts to find space for a new node. Returns the new node if a spot was found, or none if there is no room. 51 | public PackNode* insert(const int width, const int height) { 52 | 53 | // If this node is not a leaf, try to find room in one of it's children. 54 | if (right || bottom) { 55 | PackNode* node = right.insert(width, height); 56 | if (node) { 57 | return node; 58 | } 59 | return bottom.insert(width, height); 60 | } 61 | 62 | // If this node is a leaf and there is room, create child partitions and return this node's rectangle. 63 | if (width <= rectangle.width && height <= rectangle.height) { 64 | right = new PackNode( 65 | Rectangle( 66 | rectangle.x1 + width, rectangle.y1, 67 | rectangle.x2, rectangle.y1 + height 68 | ) 69 | ); 70 | bottom = new PackNode( 71 | Rectangle( 72 | rectangle.x1, rectangle.y1 + height, 73 | rectangle.x2, rectangle.y2 74 | ) 75 | ); 76 | 77 | return new PackNode( 78 | Rectangle( 79 | rectangle.x1, rectangle.y1, 80 | rectangle.x1 + width, rectangle.y1 + height 81 | ) 82 | ); 83 | } 84 | 85 | return null; 86 | } 87 | } -------------------------------------------------------------------------------- /src/audiobank.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.audiobank; 29 | 30 | import std.stdio; 31 | 32 | import derelict.openal.al; 33 | 34 | import audio.buffer; 35 | 36 | import game.game; 37 | 38 | import util.binaryfile; 39 | import util.log; 40 | 41 | 42 | public struct AudioChunk { 43 | int sampleRateVariation; 44 | int loopStart; 45 | int loopEnd; 46 | 47 | AudioBuffer buffer; 48 | } 49 | 50 | 51 | public enum SoundType : ubyte { 52 | NONE, 53 | LOOP, 54 | ONCE 55 | } 56 | 57 | 58 | public alias SoundIndex = ushort; 59 | 60 | 61 | public final class AudioBank { 62 | 63 | private AudioChunk[] _audioChunks; 64 | 65 | 66 | this(const string baseName) { 67 | Log.write(Color.NORMAL, "Reading audio bank '%s'...", baseName); 68 | 69 | BinaryFile headerFile = new BinaryFile(baseName ~ ".sdt"); 70 | BinaryFile dataFile = new BinaryFile(baseName ~ ".raw"); 71 | 72 | AudioChunk chunk; 73 | while (!headerFile.eof) { 74 | int offset = headerFile.readInt(); 75 | int length = headerFile.readInt(); 76 | int sampleRate = headerFile.readInt(); 77 | 78 | if (gameMode == GameMode.GTA2) { 79 | chunk.sampleRateVariation = headerFile.readInt(); 80 | chunk.loopStart = headerFile.readInt() / 2; 81 | chunk.loopEnd = headerFile.readInt(); 82 | if (chunk.loopEnd == -1) { 83 | chunk.loopEnd = length / 2; 84 | } 85 | } 86 | 87 | dataFile.seek(offset); 88 | ubyte[] data = dataFile.readBytes(length); 89 | const ALenum format = (data.length % 2) ? AL_FORMAT_MONO8 : AL_FORMAT_MONO16; 90 | chunk.buffer = new AudioBuffer(data, format, sampleRate); 91 | 92 | _audioChunks ~= chunk; 93 | } 94 | 95 | Log.write(Color.NORMAL, "%d audio chunks.", _audioChunks.length); 96 | } 97 | 98 | public AudioChunk* getChunk(const uint index) { 99 | return &_audioChunks[index]; 100 | } 101 | } -------------------------------------------------------------------------------- /src/cvars.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module cvars; 29 | 30 | import util.console; 31 | 32 | 33 | public void registerCVars() { 34 | // Game 35 | CVars.register("g_fov", new CVar(35.0, CVarFlags.ARCHIVE)); 36 | CVars.register("g_basedir", new CVar("gta1", CVarFlags.ARCHIVE)); 37 | CVars.register("g_mission", new CVar(0, CVarFlags.ARCHIVE)); 38 | CVars.register("g_language", new CVar("en", CVarFlags.ARCHIVE)); 39 | CVars.register("g_dusk", new CVar(0, CVarFlags.ARCHIVE)); 40 | 41 | // Render 42 | CVars.register("r_blockmipmaps", new CVar(3, CVarFlags.ARCHIVE)); 43 | CVars.register("r_spritemipmaps", new CVar(3, CVarFlags.ARCHIVE)); 44 | CVars.register("r_width", new CVar(1280, CVarFlags.ARCHIVE)); 45 | CVars.register("r_height", new CVar(720, CVarFlags.ARCHIVE)); 46 | CVars.register("r_filter", new CVar(1, CVarFlags.ARCHIVE)); 47 | CVars.register("r_anisotropy", new CVar(16, CVarFlags.ARCHIVE)); 48 | CVars.register("r_aasamples", new CVar(0, CVarFlags.ARCHIVE)); 49 | CVars.register("r_shadows", new CVar(1, CVarFlags.ARCHIVE)); 50 | CVars.register("r_dumpblocktextures", new CVar(0, CVarFlags.ARCHIVE)); 51 | CVars.register("r_dumpsprites", new CVar(0, CVarFlags.ARCHIVE)); 52 | CVars.register("r_dumpspritetextures", new CVar(0, CVarFlags.ARCHIVE)); 53 | CVars.register("r_fullscreen", new CVar(0, CVarFlags.ARCHIVE)); 54 | CVars.register("r_vsync", new CVar(1, CVarFlags.ARCHIVE)); 55 | CVars.register("r_frameratelimit", new CVar(60, CVarFlags.ARCHIVE)); 56 | 57 | // Audio 58 | CVars.register("a_maxsources", new CVar(40, CVarFlags.ARCHIVE)); 59 | CVars.register("a_loopsourcesratio", new CVar(0.5, CVarFlags.ARCHIVE)); 60 | CVars.register("a_singlesourcesratio", new CVar(0.5, CVarFlags.ARCHIVE)); 61 | CVars.register("a_dopplerfactor", new CVar(1.0, CVarFlags.ARCHIVE)); 62 | 63 | // Map 64 | CVars.register("m_border", new CVar(2, CVarFlags.ARCHIVE)); 65 | CVars.register("m_sectorsize", new CVar(32, CVarFlags.ARCHIVE)); 66 | } 67 | -------------------------------------------------------------------------------- /src/bmp.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module util.bmp; 29 | 30 | import std.stdio; 31 | import std.file; 32 | 33 | 34 | private align(1) struct BMPHeader { 35 | align(1): 36 | ubyte[2] magic; 37 | uint fileSize; 38 | ubyte[4] reserved; 39 | uint imageOffset; 40 | } 41 | 42 | private align(1) struct DIBHeader { 43 | align(1): 44 | uint headerSize; 45 | int imageWidth; 46 | int imageHeight; 47 | ushort colorPlanes; 48 | ushort bpp; 49 | uint compression; 50 | uint imageSize; 51 | int dpiX; 52 | int dpiY; 53 | uint colorsUsed; 54 | uint importantColorsUsed; 55 | } 56 | 57 | 58 | public void writeBMP32(const string fileName, const uint width, const uint height, ubyte[] image) { 59 | 60 | // Flip image data vertically. 61 | ubyte[] imageData = new ubyte[image.length]; 62 | int destOffset = 0; 63 | int srcOffset = width * (height - 1) * 4; 64 | for (int y = height - 1; y >= 0; y--) { 65 | for (int x = 0; x < width; x++) { 66 | imageData[destOffset + 0] = image[srcOffset + 0]; 67 | imageData[destOffset + 1] = image[srcOffset + 1]; 68 | imageData[destOffset + 2] = image[srcOffset + 2]; 69 | imageData[destOffset + 3] = image[srcOffset + 3]; 70 | destOffset += 4; 71 | srcOffset += 4; 72 | } 73 | srcOffset -= width * 2 * 4; 74 | } 75 | 76 | BMPHeader header; 77 | header.magic = [0x42, 0x4D]; 78 | header.fileSize = BMPHeader.sizeof + DIBHeader.sizeof + imageData.length; 79 | header.imageOffset = BMPHeader.sizeof + DIBHeader.sizeof; 80 | 81 | DIBHeader dib; 82 | dib.headerSize = DIBHeader.sizeof; 83 | dib.imageWidth = width; 84 | dib.imageHeight = height; 85 | dib.colorPlanes = 1; 86 | dib.bpp = 32; 87 | dib.compression = 0; 88 | dib.imageSize = 0; 89 | dib.dpiX = 96; 90 | dib.dpiY = 96; 91 | dib.colorsUsed = 0; 92 | dib.importantColorsUsed = 0; 93 | 94 | File output = File(fileName, "wb"); 95 | fwrite(&header, header.sizeof, 1, output.getFP()); 96 | fwrite(&dib, dib.sizeof, 1, output.getFP()); 97 | fwrite(&imageData[0], imageData.length, 1, output.getFP()); 98 | output.close(); 99 | } -------------------------------------------------------------------------------- /src/program.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module render.program; 29 | 30 | import std.stdio; 31 | import std.string; 32 | 33 | import derelict.opengl; 34 | 35 | import render.shader; 36 | 37 | import util.log; 38 | 39 | 40 | public final class Program { 41 | 42 | private GLuint _id; 43 | private Shader _shaderVertex; 44 | private Shader _shaderFragment; 45 | 46 | this(Shader shaderVertex, Shader shaderFragment) { 47 | Log.write(Color.NORMAL, "Linking '%s' & '%s' into shader program...", shaderVertex.name, shaderFragment.name); 48 | 49 | _id = glCreateProgram(); 50 | _shaderVertex = shaderVertex; 51 | _shaderFragment = shaderFragment; 52 | 53 | glAttachShader(_id, shaderVertex.id); 54 | glAttachShader(_id, shaderFragment.id); 55 | glLinkProgram(_id); 56 | 57 | GLint status; 58 | GLint logLength; 59 | glGetProgramiv(_id, GL_LINK_STATUS, &status); 60 | if (status == GL_FALSE) { 61 | glGetProgramiv(_id, GL_INFO_LOG_LENGTH, &logLength); 62 | 63 | char[] errorLog; 64 | if (logLength > 0) { 65 | errorLog = new char[logLength]; 66 | glGetProgramInfoLog(_id, logLength, null, &errorLog[0]); 67 | } else { 68 | errorLog = cast(char[])"Unknown error message. No log info given."; 69 | } 70 | 71 | glDeleteProgram(_id); 72 | 73 | throw new Exception(format("Cannot link program. %s", cast(char[])errorLog)); 74 | } 75 | } 76 | 77 | ~this() { 78 | if (_id) { 79 | glDeleteProgram(_id); 80 | } 81 | } 82 | 83 | public void use() { 84 | glUseProgram(_id); 85 | } 86 | 87 | public GLint getUniformLocation(const string name) { 88 | const(char)* n = name.toStringz(); 89 | return glGetUniformLocation(_id, n); 90 | } 91 | 92 | public GLint getUniformBlockIndex(const string name) { 93 | const(char)* n = name.toStringz(); 94 | return glGetUniformBlockIndex(_id, n); 95 | } 96 | 97 | public void uniformBlockBinding(const GLint blockIndex, const GLint bindingPoint) { 98 | glUniformBlockBinding(_id, blockIndex, bindingPoint); 99 | } 100 | 101 | } -------------------------------------------------------------------------------- /src/rifffile.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module util.rifffile; 29 | 30 | import std.stdio; 31 | import std.string; 32 | import std.conv; 33 | 34 | import util.binaryfile; 35 | import util.log; 36 | 37 | 38 | public struct Chunk { 39 | char[4] name; 40 | uint size; 41 | uint offset; 42 | } 43 | 44 | 45 | public final class RIFFFile { 46 | 47 | private BinaryFile _file; 48 | private char[4] _type; 49 | private ushort _versionNum; 50 | private Chunk[] _chunks; 51 | private uint[string] _chunkNames; 52 | 53 | 54 | this(BinaryFile file) { 55 | _file = file; 56 | _type = cast(char[])_file.readBytes(4); 57 | _versionNum = _file.readUShort(); 58 | 59 | while (_file.offset < _file.size) { 60 | Chunk chunk; 61 | chunk.name = cast(char[])_file.readBytes(4); 62 | chunk.size = _file.readUInt(); 63 | chunk.offset = _file.offset; 64 | 65 | _chunks ~= chunk; 66 | _chunkNames[to!string(chunk.name)] = _chunks.length - 1; 67 | 68 | _file.seek(chunk.offset + chunk.size); 69 | } 70 | } 71 | 72 | public Chunk getChunk(const string name) { 73 | if (name !in _chunkNames) { 74 | throw new Exception(format("RIFF file '%s' does not contain chunk '%s'.", _file.name, name)); 75 | } 76 | 77 | const Chunk chunk = _chunks[_chunkNames[name]]; 78 | _file.seek(chunk.offset); 79 | 80 | return chunk; 81 | } 82 | 83 | public void listChunks() { 84 | Log.write(Color.DEBUG, "Listing chunks for '%s':", _file.name); 85 | foreach (ref Chunk chunk; _chunks) { 86 | Log.write(Color.DEBUG, "%s %d %d", chunk.name, chunk.size, chunk.offset); 87 | } 88 | } 89 | 90 | public bool contains(const string name) { 91 | return (name in _chunkNames) !is null; 92 | } 93 | 94 | public void close() { 95 | _file.close(); 96 | _file = null; 97 | } 98 | 99 | @property public char[4] type() { 100 | return _type; 101 | } 102 | 103 | @property public ushort versionNum() { 104 | return _versionNum; 105 | } 106 | 107 | @property public BinaryFile file() { 108 | return _file; 109 | } 110 | 111 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | tmp/ 9 | *.tmp 10 | *.bak 11 | *.swp 12 | *~.nib 13 | local.properties 14 | .classpath 15 | .settings/ 16 | .loadpath 17 | 18 | # External tool builders 19 | .externalToolBuilders/ 20 | 21 | # Locally stored "Eclipse launch configurations" 22 | *.launch 23 | 24 | # CDT-specific 25 | .cproject 26 | 27 | # PDT-specific 28 | .buildpath 29 | 30 | 31 | ################# 32 | ## Visual Studio 33 | ################# 34 | 35 | ## Ignore Visual Studio temporary files, build results, and 36 | ## files generated by popular Visual Studio add-ons. 37 | 38 | # User-specific files 39 | *.suo 40 | *.user 41 | *.sln.docstates 42 | 43 | # Build results 44 | 45 | [Dd]ebug/ 46 | [Rr]elease/ 47 | x64/ 48 | build/ 49 | [Oo]bj/ 50 | 51 | # MSTest test Results 52 | [Tt]est[Rr]esult*/ 53 | [Bb]uild[Ll]og.* 54 | 55 | *_i.c 56 | *_p.c 57 | *.ilk 58 | *.meta 59 | *.obj 60 | *.pch 61 | *.pdb 62 | *.pgc 63 | *.pgd 64 | *.rsp 65 | *.sbr 66 | *.tlb 67 | *.tli 68 | *.tlh 69 | *.tmp 70 | *.tmp_proj 71 | *.log 72 | *.vspscc 73 | *.vssscc 74 | .builds 75 | *.pidb 76 | *.log 77 | *.scc 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opensdf 84 | *.sdf 85 | *.cachefile 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | 92 | # Guidance Automation Toolkit 93 | *.gpState 94 | 95 | # ReSharper is a .NET coding add-in 96 | _ReSharper*/ 97 | *.[Rr]e[Ss]harper 98 | 99 | # TeamCity is a build add-in 100 | _TeamCity* 101 | 102 | # DotCover is a Code Coverage Tool 103 | *.dotCover 104 | 105 | # NCrunch 106 | *.ncrunch* 107 | .*crunch*.local.xml 108 | 109 | # Installshield output folder 110 | [Ee]xpress/ 111 | 112 | # DocProject is a documentation generator add-in 113 | DocProject/buildhelp/ 114 | DocProject/Help/*.HxT 115 | DocProject/Help/*.HxC 116 | DocProject/Help/*.hhc 117 | DocProject/Help/*.hhk 118 | DocProject/Help/*.hhp 119 | DocProject/Help/Html2 120 | DocProject/Help/html 121 | 122 | # Click-Once directory 123 | publish/ 124 | 125 | # Publish Web Output 126 | *.Publish.xml 127 | *.pubxml 128 | 129 | # NuGet Packages Directory 130 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 131 | #packages/ 132 | 133 | # Windows Azure Build Output 134 | csx 135 | *.build.csdef 136 | 137 | # Windows Store app package directory 138 | AppPackages/ 139 | 140 | # Others 141 | sql/ 142 | *.Cache 143 | ClientBin/ 144 | [Ss]tyle[Cc]op.* 145 | ~$* 146 | *~ 147 | *.dbmdl 148 | *.[Pp]ublish.xml 149 | *.pfx 150 | *.publishsettings 151 | 152 | # RIA/Silverlight projects 153 | Generated_Code/ 154 | 155 | # Backup & report files from converting an old project file to a newer 156 | # Visual Studio version. Backup files are not needed, because we have git ;-) 157 | _UpgradeReport_Files/ 158 | Backup*/ 159 | UpgradeLog*.XML 160 | UpgradeLog*.htm 161 | 162 | # SQL Server files 163 | App_Data/*.mdf 164 | App_Data/*.ldf 165 | 166 | ############# 167 | ## Windows detritus 168 | ############# 169 | 170 | # Windows image file caches 171 | Thumbs.db 172 | ehthumbs.db 173 | 174 | # Folder config file 175 | Desktop.ini 176 | 177 | # Recycle Bin used on file shares 178 | $RECYCLE.BIN/ 179 | 180 | # Mac crap 181 | .DS_Store 182 | 183 | 184 | ############# 185 | ## Python 186 | ############# 187 | 188 | *.py[co] 189 | 190 | # Packages 191 | *.egg 192 | *.egg-info 193 | dist/ 194 | build/ 195 | eggs/ 196 | parts/ 197 | var/ 198 | sdist/ 199 | develop-eggs/ 200 | .installed.cfg 201 | 202 | # Installer logs 203 | pip-log.txt 204 | 205 | # Unit test / coverage reports 206 | .coverage 207 | .tox 208 | 209 | #Translations 210 | *.mo 211 | 212 | #Mr Developer 213 | .mr.developer.cfg 214 | 215 | data/config.json 216 | blocktextures 217 | spritetextures 218 | sprites 219 | base/* 220 | basesrc 221 | info 222 | *.dll 223 | *.exe 224 | releases 225 | *_cv 226 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | GTA Viewer 2 | ========== 3 | This is an engine\viewer for the top-down Grand Theft Auto games. 4 | 5 | It supports the following GTA games: 6 | 7 | * GTA 1 8 | * GTA 1 demos (regular, 3DFX, ECTS) 9 | * GTA London (1969 and 1961) 10 | * GTA 2 11 | * GTA 2 demo 12 | 13 | It is not feature complete, lacking the following abilities: 14 | * GTA 2 sound sequences (causing wrong ambient sounds to play). 15 | * Map meta-data like routes, spawn points and drive arrows are not displayed. 16 | * GTA 2 point lights are not rendered. 17 | 18 | Usage 19 | ----- 20 | In order to run GTA Viewer you will need to place files from a valid GTA game in an appropriately named subdirectory of the /base directory. You can use one of the following subdirectory names: 21 | 22 | * gta1: GTA 1 23 | * gta1demo: GTA 1 Demo 24 | * gta1demo3dfx: GTA 1 3DFX Demo 25 | * gta1demoects: GTA 1 ECTS Demo 26 | * uk: GTA London 1969 27 | * uk1961: GTA London 1961 28 | * gta2: GTA 2 29 | * gta2demo: GTA 2 Demo 30 | 31 | For the GTA 1 and GTA: London games, copy all data inside the /gtadata directory into the corresponding /base/gta1* or /base/uk* directories. For GTA 2 games, copy all data inside the /data directory into the corresponding /base/gta2* directory. Any version of the game's data files should work, including versions released through Steam. 32 | 33 | To configure what game directory should be used, edit the /data/config.json file, and modify the g_basedir value to the directory name containing the game data you want to use. 34 | 35 | Configuration 36 | ------------- 37 | The /data/config.json file contains a number of variables that can be modified. The most useful ones are: 38 | 39 | * g_basedir: sets the directory inside the /base directory that contains the files to run GTA Viewer with. 40 | * r_width & r_height: these values set the width and height of the window to diusplay the viewer in, or the resolution of the fullscreen mode (see r_fullscreen). 41 | * g_dusk: set to 1 if GTA 2 levels should be played in dusk lighting mode. 42 | * g_fov: sets the field of view of the camera. 43 | * g_mission: sets the mission index to view. This will load the appropriate map, script and style for the specified mission. 44 | * r_aasamples: sets the number of anti-aliasing samples to use to smooth out edges. 45 | * r_anisotropy: sets the number of anisotropy samples to use to produce more accurate texture filtering at steep angles. 46 | * r_dumpblocktextures: if set to 1, all of the loaded style's block textures will be written to the /blocktextures directory. 47 | * r_dumpsprites: if set to 1, all of the loaded style's sprites will be written to the /sprites directory. 48 | * r_dumpspritetextures: if set to 1, all of the loaded style's sprites will be written to the /spritetextures directory as sprite sheets. 49 | * r_filter: if set to 1, texture filtering will be applied. To emulate GTA 1's lack of texture filtering, set this to 0. 50 | * r_frameratelimit: if r_vsync is 0, this will dictate the maximum framerate to render at. 51 | * r_fullscreen: if set to 1, will display the viewer in fullscreen mode instead of in a window. 52 | * r_shadows: if set to 1, draws faux shadows under sprites. This also works in GTA 1 games. 53 | * r_vsync: if set to 1, will enable vsync to synchronize screen refreshes with the display refresh rate, leading to smoother motion. 54 | 55 | Compiling 56 | --------- 57 | GTA Viewer can be compiled using the included Visual Studio 2013 project, as well as an installation of D and Visual D. The following libraries are also required: 58 | 59 | * Derelict SDL2: https://github.com/DerelictOrg/DerelictSDL2 60 | * Derelict OpenGL 3: https://github.com/DerelictOrg/DerelictGL3 61 | * Derelict OpenAL: https://github.com/DerelictOrg/DerelictAL 62 | 63 | Notices 64 | ------- 65 | * Contains code based on Epic GTA2 Script Decompiler by T.M. 66 | * fast-hash implementation converted from https://code.google.com/p/fast-hash/ 67 | * Matrix math functions adapted from https://github.com/coreh/gl-matrix.c by Marco Aurélio 68 | * Bin packing functionality adapted from https://github.com/jakesgordon/bin-packing by Jake Gordon 69 | * Grand Theft Auto, Grand Theft Auto 2, Grand Theft Auto London 1969 and Grand Theft Auto London 1961 are registered trademarks from Rockstar Games, Inc. 70 | -------------------------------------------------------------------------------- /src/missionreadergta1.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.script.missionreadergta1; 29 | 30 | import std.stdio; 31 | import std.file; 32 | import std.path; 33 | import std.string; 34 | import std.conv; 35 | 36 | import game.script.missionlist; 37 | import game.script.missionreader; 38 | import game.script.gta1script; 39 | import game.script.gta1parser; 40 | import game.script.gta1tokenizer; 41 | 42 | import game.strings; 43 | 44 | import util.binaryfile; 45 | import util.log; 46 | 47 | 48 | public final class MissionReaderGTA1 : MissionReader { 49 | 50 | this(const string baseDir) { 51 | string[] missionPaths = [ 52 | buildPath(baseDir, "mission.ini"), 53 | buildPath(baseDir, "missuke.ini"), 54 | buildPath(baseDir, "missuk.ini") 55 | ]; 56 | 57 | string fileName; 58 | foreach (string path; missionPaths) { 59 | if (exists(path)) { 60 | fileName = path; 61 | break; 62 | } 63 | } 64 | if (!fileName.length) { 65 | throw new Exception(format("Cannot find a mission script in GTA1 base directory '%s'.", baseDir)); 66 | } 67 | 68 | Log.write(Color.NORMAL, "Reading GTA 1 mission script '%s'...", fileName); 69 | GTA1Tokenizer tokenizer = new GTA1Tokenizer(fileName); 70 | GTA1Parser parser = new GTA1Parser(tokenizer.tokens); 71 | 72 | // Iterate over the parsed scripts and fill in missing information. 73 | foreach (int index, GTA1Script script; parser.scripts) { 74 | Mission mission; 75 | 76 | mission.name = Strings.get(format("mission%s", to!string(script.textIndex))); 77 | 78 | // Hackish way to detect multiplayer maps. 79 | if (script.textIndex > 999) { 80 | mission.type = MissionType.MULTIPLAYER; 81 | } else { 82 | mission.type = MissionType.MAIN; 83 | } 84 | 85 | // Read map header. 86 | BinaryFile map = new BinaryFile(buildPath(baseDir, script.mapName)); 87 | map.skip(4); 88 | ubyte styleIndex = map.readUByte(); 89 | const ubyte audioBankIndex = map.readUByte(); 90 | map.close(); 91 | 92 | // FOr uk1961, detect if the differently named style file exists. 93 | string expansionStyle = format("sty%.3d.g24", styleIndex); 94 | if (exists(buildPath(baseDir, expansionStyle))) { 95 | mission.styleName = expansionStyle; 96 | } else { 97 | mission.styleName = format("style%.3d.g24", styleIndex); 98 | } 99 | 100 | mission.audioName = format("audio/level%.3d", audioBankIndex + 1); 101 | mission.mapName = script.mapName; 102 | mission.cityName = Strings.get(format("city%d", styleIndex - 1)); 103 | 104 | mission.script = script; 105 | 106 | _missions ~= mission; 107 | } 108 | } 109 | 110 | } -------------------------------------------------------------------------------- /src/source.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module audio.source; 29 | 30 | import std.stdio; 31 | import std.string; 32 | 33 | import derelict.openal.al; 34 | 35 | import audio.audio; 36 | import audio.buffer; 37 | 38 | import util.vector3; 39 | 40 | 41 | public enum AudioSourceType : ubyte { 42 | SINGLE, 43 | LOOPING 44 | } 45 | 46 | public enum AudioSourceState : ubyte { 47 | PLAYING, 48 | STOPPED, 49 | PAUSED, 50 | INITIAL 51 | } 52 | 53 | 54 | public final class AudioSource { 55 | 56 | private ALuint _id; 57 | 58 | private AudioSourceType _type; 59 | 60 | 61 | this(const AudioSourceType type) { 62 | _type = type; 63 | 64 | alGetError(); 65 | alGenSources(1, &_id); 66 | const ALint error = alGetError(); 67 | if (error != AL_NO_ERROR) { 68 | throw new Exception(format("Cannot allocate OpenAL source. Error %d.", error)); 69 | } 70 | 71 | setPitch(1.0); 72 | setGain(1.0); 73 | setPosition(Vector3(0, 0, 0)); 74 | setVelocity(Vector3(0, 0, 0)); 75 | setLooping(false); 76 | 77 | alSourcef(_id, AL_MAX_DISTANCE, MAX_AUDIO_DISTANCE); 78 | alSourcef(_id, AL_REFERENCE_DISTANCE, MIN_AUDIO_DISTANCE); 79 | } 80 | 81 | ~this() { 82 | alSourcei(_id, AL_BUFFER, 0); 83 | alDeleteSources(1, &_id); 84 | } 85 | 86 | public void setPitch(const float pitch) { 87 | alSourcef(_id, AL_PITCH, pitch); 88 | } 89 | 90 | public void setGain(const float gain) { 91 | alSourcef(_id, AL_GAIN, gain); 92 | } 93 | 94 | public void setPosition(const Vector3 pos) { 95 | alSource3f(_id, AL_POSITION, pos.x, pos.y, pos.z); 96 | } 97 | 98 | public void setVelocity(const Vector3 vel) { 99 | alSource3f(_id, AL_VELOCITY, vel.x, vel.y, vel.z); 100 | } 101 | 102 | public void setLooping(const bool looping) { 103 | alSourcei(_id, AL_LOOPING, looping ? AL_TRUE : AL_FALSE); 104 | } 105 | 106 | public AudioSourceState getState() { 107 | ALenum state; 108 | alGetSourcei(_id, AL_SOURCE_STATE, &state); 109 | 110 | switch (state) { 111 | case AL_PLAYING: return AudioSourceState.PLAYING; 112 | case AL_STOPPED: return AudioSourceState.STOPPED; 113 | case AL_INITIAL: return AudioSourceState.INITIAL; 114 | case AL_PAUSED: return AudioSourceState.PAUSED; 115 | default: 116 | throw new Exception(format("Unknown audio source state '%d'.", state)); 117 | } 118 | } 119 | 120 | public void bindBuffer(AudioBuffer buffer) { 121 | alSourcei(_id, AL_BUFFER, buffer.id); 122 | } 123 | 124 | public void play() { 125 | alSourcePlay(_id); 126 | } 127 | 128 | public void pause() { 129 | alSourcePause(_id); 130 | } 131 | 132 | public void stop() { 133 | alSourceStop(_id); 134 | } 135 | 136 | @property public AudioSourceType type() { 137 | return _type; 138 | } 139 | 140 | } -------------------------------------------------------------------------------- /src/binaryfile.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module util.binaryfile; 29 | 30 | import std.stdio; 31 | import std.string; 32 | import std.bitmanip; 33 | import std.file; 34 | 35 | 36 | public final class BinaryFile { 37 | 38 | private string _name; 39 | private ubyte[] _data; 40 | private uint _offset; 41 | 42 | 43 | this(const string fileName) { 44 | _name = fileName; 45 | _data = cast(ubyte[])read(fileName); 46 | } 47 | 48 | public void seek(const uint offset) { 49 | _offset = offset; 50 | } 51 | 52 | public void skip(const uint bytes) { 53 | _offset += bytes; 54 | } 55 | 56 | public string readNullString(const uint length) { 57 | ubyte[] buffer = _data[_offset.._offset + length]; 58 | _offset += length; 59 | 60 | int nullIndex = length; 61 | for (int index = 0; index < length; index++) { 62 | if (buffer[index] == 0) { 63 | nullIndex = index; 64 | break; 65 | } 66 | } 67 | 68 | return cast(string)buffer[0..nullIndex]; 69 | } 70 | 71 | public string readString(const uint length) { 72 | ubyte[] buffer = _data[_offset.._offset + length]; 73 | _offset += length; 74 | 75 | return cast(string)buffer[0..length]; 76 | } 77 | 78 | public ubyte[] readBytes(const uint amount) { 79 | ubyte[] buffer = _data[_offset.._offset + amount]; 80 | _offset += amount; 81 | return buffer; 82 | } 83 | 84 | public ubyte readUByte() { 85 | ubyte[1] buffer = _data[_offset]; 86 | _offset += 1; 87 | return buffer[0]; 88 | } 89 | 90 | public ushort readUShort() { 91 | ubyte[2] buffer = _data[_offset.._offset + 2]; 92 | _offset += 2; 93 | return littleEndianToNative!ushort(buffer[0..2]); 94 | } 95 | 96 | public uint readUInt() { 97 | ubyte[4] buffer = _data[_offset.._offset + 4]; 98 | _offset += 4; 99 | return littleEndianToNative!uint(buffer[0..4]); 100 | } 101 | 102 | public byte readByte() { 103 | byte[1] buffer = _data[_offset]; 104 | _offset += 1; 105 | return buffer[0]; 106 | } 107 | 108 | public short readShort() { 109 | ubyte[2] buffer = _data[_offset.._offset + 2]; 110 | _offset += 2; 111 | return littleEndianToNative!short(buffer[0..2]); 112 | } 113 | 114 | public int readInt() { 115 | ubyte[4] buffer = _data[_offset.._offset + 4]; 116 | _offset += 4; 117 | return littleEndianToNative!int(buffer[0..4]); 118 | } 119 | 120 | public void close() { 121 | _data.length = 0; 122 | _offset = 0; 123 | } 124 | 125 | @property public bool eof() { 126 | return (_offset >= _data.length); 127 | } 128 | 129 | @property public uint offset() { 130 | return _offset; 131 | } 132 | 133 | @property public uint size() { 134 | return _data.length; 135 | } 136 | 137 | @property public string name() { 138 | return _name; 139 | } 140 | } -------------------------------------------------------------------------------- /src/gta1commands.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.script.gta1commands; 29 | 30 | 31 | public enum GTA1CommandIndex : ubyte { 32 | ADD_A_LIFE, 33 | ANSWER, 34 | ARE_BOTH_ONSCREEN, 35 | ARMEDMESS, 36 | ARROW, 37 | ARROW_OFF, 38 | ARROWCAR, 39 | ARROWPED, 40 | BANK_ALARM_OFF, 41 | BANK_ALARM_ON, 42 | BANK_ROBBERY, 43 | BRIEF, 44 | CANCEL_BRIEFING, 45 | CAR_ON, 46 | CHANGE_BLOCK, 47 | CHANGE_PED_TYPE, 48 | CHANGE_TYPE, 49 | CHECK_CAR, 50 | CLOSE_DOOR, 51 | COMPARE, 52 | CRANE, 53 | DEAD_ARRESTED, 54 | DECCOUNT, 55 | DELAY_CRIME, 56 | DESTROY, 57 | DISABLE, 58 | DISARMMESS, 59 | DO_GTA, 60 | DO_MODEL, 61 | DO_REPO, 62 | DONOWT, 63 | DOOR_OFF, 64 | DOOR_ON, 65 | DROP_ON, 66 | DROP_WANTED_LEVEL, 67 | DUMMYON, 68 | DUMMY_DRIVE_ON, 69 | ENABLE, 70 | END, 71 | EXPL_LAST, 72 | EXPL_NO_FIRE, 73 | EXPL_PED, 74 | EXPLODE, 75 | EXPLODE_CAR, 76 | FREEUP_CAR, 77 | FREEZE_ENTER, 78 | FREEZE_TIMED, 79 | FRENZY_BRIEF, 80 | FRENZY_CHECK, 81 | FRENZY_SET, 82 | GARAGE_SEND, 83 | GENERAL_ONSCREEN, 84 | GET_CAR_INFO, 85 | GET_DRIVER_INFO, 86 | GOTO, 87 | GOTO_DROPOFF, 88 | HELL_ON, 89 | HUNTOFF, 90 | HUNTON, 91 | INC_HEADS, 92 | INCCOUNT, 93 | IS_A_TRAIN_WRECKED, 94 | IS_GOAL_DEAD, 95 | IS_PED_ARRESTED, 96 | IS_PED_IN_CAR, 97 | IS_PED_STUNNED, 98 | IS_PLAYER_ON_TRAIN, 99 | IS_POWERUP_DONE, 100 | KEEP_THIS_PROC, 101 | KF_BRIEF_GENERAL, 102 | KF_BRIEF_TIMED, 103 | KF_CANCEL_BRIEFING, 104 | KF_CANCEL_GENERAL, 105 | KF_PROCESS, 106 | KICKSTART, 107 | KILL_CAR, 108 | KILL_DROP, 109 | KILL_OBJ, 110 | KILL_PED, 111 | KILL_PROCESS, 112 | KILL_SIDE_PROC, 113 | KILL_SPEC_PROC, 114 | LOCATE, 115 | LOCATE_STOPPED, 116 | LOCK_DOOR, 117 | MAKEOBJ, 118 | MESSAGE_BRIEF, 119 | MISSION_END, 120 | MOBILE_BRIEF, 121 | MODEL_FUTURE, 122 | MODEL_HUNT, 123 | MPHONE, 124 | NEXT_KICK, 125 | OBTAIN, 126 | OPEN_DOOR, 127 | P_BRIEF, 128 | P_BRIEF_TIMED, 129 | PARK, 130 | PARKED_ON, 131 | PARKED_PIXELS_ON, 132 | PED_BACK, 133 | PED_ON, 134 | PED_OUT_OF_CAR, 135 | PED_POLICE, 136 | PED_SENDTO, 137 | PED_WEAPON, 138 | PIXEL_CAR_ON, 139 | PLAIN_EXPL_BUILDING, 140 | PLAYER_ARE_BOTH_ONSCREEN, 141 | PLAYER_DRIVE_BY, 142 | PLAYER_DRIVE_BY_OFF, 143 | POWERUP_OFF, 144 | POWERUP_ON, 145 | RED_ARROW, 146 | RED_ARROWCAR, 147 | RED_ARROW_OFF, 148 | REMAP_CAR, 149 | REMAP_PED, 150 | RESET, 151 | RESET_KF, 152 | RESET_WITH_BRIEFS, 153 | RETURN_CONTROL, 154 | SCORE_CHECK, 155 | SENDTO, 156 | SET_COLLIDE, 157 | SET_KILLTRIG, 158 | SET_NO_COLLIDE, 159 | SET_PED_SPEED, 160 | SETBOMB, 161 | SETUP_REPO, 162 | SPEECH_BRIEF, 163 | START_CHOPPER, 164 | START_DRIVER, 165 | START_HIRED_ESC, 166 | START_MODEL, 167 | STARTUP, 168 | STEAL, 169 | STOP_FRENZY, 170 | SURVIVE, 171 | THROW, 172 | THROW_TO_POINT, 173 | UNFREEZE_ENTER, 174 | UNLOCK_DOOR, 175 | WAIT_FOR_PED, 176 | WAIT_FOR_PLAYER, 177 | WAIT_FOR_PLAYERS, 178 | WRECK_A_TRAIN, 179 | WRECK_CURR_TRAIN, 180 | } -------------------------------------------------------------------------------- /src/indexbuffer.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module render.indexbuffer; 29 | 30 | import std.algorithm; 31 | 32 | import derelict.opengl; 33 | 34 | import render.enums; 35 | 36 | 37 | public final class IndexBuffer { 38 | 39 | private GLuint _id; 40 | 41 | private PrimitiveType _primitiveType; 42 | private BufferUsage _usage; 43 | private int _subBufferCount; 44 | 45 | private GLushort[][] _data; 46 | private size_t[] _offsets; 47 | private size_t[] _sizes; 48 | private size_t[] _startIndices; 49 | 50 | 51 | this(const PrimitiveType primitiveType, const int subBufferCount, const BufferUsage usage) { 52 | _primitiveType = primitiveType; 53 | _usage = usage; 54 | _subBufferCount = subBufferCount; 55 | 56 | _offsets = new size_t[_subBufferCount]; 57 | _sizes = new size_t[_subBufferCount]; 58 | _data = new GLushort[][](_subBufferCount); 59 | _startIndices = new size_t[_subBufferCount]; 60 | } 61 | 62 | ~this() { 63 | glDeleteBuffers(1, &_id); 64 | } 65 | 66 | public void add(const GLushort[] data, const int subBufferIndex, const size_t startIndex) { 67 | foreach (GLushort value; data) { 68 | _data[subBufferIndex] ~= cast(GLushort)(value + startIndex); 69 | } 70 | } 71 | 72 | public void setStartIndex(const int subBufferIndex, const size_t index) { 73 | _startIndices[subBufferIndex] = index; 74 | } 75 | 76 | public void generate() { 77 | if (_id != 0) { 78 | throw new Exception("Attempted to generate an already generated index buffer."); 79 | } 80 | 81 | glGenBuffers(1, &_id); 82 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _id); 83 | 84 | // Calculate the total size of the buffer. 85 | size_t size; 86 | foreach (GLushort[] data; _data) { 87 | size += data.length * GLushort.sizeof; 88 | } 89 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, null, _usage); 90 | 91 | // Remap sub-buffer indices to start at the proper index. 92 | foreach (int subBuffer, GLushort[] data; _data) { 93 | foreach (ref GLushort index; data) { 94 | index += _startIndices[subBuffer]; 95 | } 96 | } 97 | 98 | // Compute subbuffer offsets and sizes, and store data into the buffer object. 99 | size_t offset; 100 | foreach (int index, GLushort[] data; _data) { 101 | _offsets[index] = offset; 102 | _sizes[index] = data.length; 103 | 104 | if (data.length) { 105 | glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, data.length * GLushort.sizeof, &data[0]); 106 | } 107 | 108 | offset += data.length * GLushort.sizeof; 109 | } 110 | } 111 | 112 | public void clear() { 113 | _data.length = 0; 114 | } 115 | 116 | public void bind() { 117 | if (_id == 0) { 118 | throw new Exception("Attempted to bind index buffer before generating."); 119 | } 120 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _id); 121 | } 122 | 123 | public void draw(const int subBufferIndex) { 124 | glDrawElements(_primitiveType, _sizes[subBufferIndex], GL_UNSIGNED_SHORT, cast(void*)_offsets[subBufferIndex]); 125 | } 126 | } -------------------------------------------------------------------------------- /src/gciparser.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module util.gciparser; 29 | 30 | import std.stdio; 31 | import std.file; 32 | import std.conv; 33 | 34 | 35 | public enum GCIType : ubyte { 36 | INTEGER, 37 | FLOATING, 38 | STRING, 39 | EOF 40 | } 41 | 42 | public union GCIValueData { 43 | int integer; 44 | double floating; 45 | string str; 46 | } 47 | 48 | public struct GCIValue { 49 | GCIType type; 50 | GCIValueData value; 51 | } 52 | 53 | private enum CType : char { 54 | STRING_START = '{', 55 | STRING_END = '}', 56 | FLOATING = 'f', 57 | SPACE = ' ', 58 | CR = '\r', 59 | LF = '\n', 60 | TAB = '\t', 61 | } 62 | 63 | 64 | public final class GCIParser { 65 | 66 | private string _name; 67 | private char[] _text; 68 | private uint _index; 69 | 70 | 71 | this(string fileName) { 72 | _name = fileName; 73 | _text = cast(char[])read(_name); 74 | } 75 | 76 | public GCIValue parse() { 77 | GCIValue value; 78 | 79 | while (_index < _text.length) { 80 | const char c = _text[_index]; 81 | 82 | // Strings. 83 | if (c == CType.STRING_START) { 84 | value.type = GCIType.STRING; 85 | value.value.str = parseString(); 86 | return value; 87 | 88 | // Floating point values. 89 | } else if (c == CType.FLOATING) { 90 | value.type = GCIType.FLOATING; 91 | value.value.floating = parseFloat(); 92 | return value; 93 | 94 | // Integer values. 95 | } else if (c >= 48 && c <= 57) { 96 | value.type = GCIType.INTEGER; 97 | value.value.integer = parseInteger(); 98 | return value; 99 | } 100 | 101 | _index++; 102 | } 103 | 104 | value.type = GCIType.EOF; 105 | return value; 106 | } 107 | 108 | private string parseString() { 109 | string str; 110 | 111 | _index++; 112 | while (_index < _text.length) { 113 | const char c = _text[_index]; 114 | 115 | if (c == CType.STRING_END) { 116 | return str; 117 | } 118 | 119 | str ~= c; 120 | _index++; 121 | } 122 | 123 | return str; 124 | } 125 | 126 | private float parseFloat() { 127 | string str; 128 | 129 | _index++; 130 | while (_index < _text.length) { 131 | const char c = _text[_index]; 132 | 133 | if (isWhitespace(c)) { 134 | return to!float(str); 135 | } 136 | 137 | str ~= c; 138 | _index++; 139 | } 140 | 141 | return to!float(str); 142 | } 143 | 144 | private int parseInteger() { 145 | string str; 146 | 147 | while (_index < _text.length) { 148 | const char c = _text[_index]; 149 | 150 | if (isWhitespace(c)) { 151 | return to!int(str); 152 | } 153 | 154 | str ~= c; 155 | _index++; 156 | } 157 | 158 | return to!int(str); 159 | } 160 | 161 | private bool isWhitespace(const char c) { 162 | return (c == CType.SPACE || c == CType.CR || c == CType.LF || c == CType.TAB); 163 | } 164 | 165 | } -------------------------------------------------------------------------------- /src/color.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module util.color; 29 | 30 | import std.stdio; 31 | import std.algorithm; 32 | 33 | 34 | public struct RGBA { 35 | ubyte r; 36 | ubyte g; 37 | ubyte b; 38 | ubyte a; 39 | 40 | this(const ubyte vr, const ubyte vg, const ubyte vb, const ubyte va) { 41 | r = vr; 42 | g = vg; 43 | b = vb; 44 | a = va; 45 | } 46 | 47 | this(const float vr, const float vg, const float vb, const float va) { 48 | r = cast(ubyte)(vr * 255.0); 49 | g = cast(ubyte)(vg * 255.0); 50 | b = cast(ubyte)(vb * 255.0); 51 | a = cast(ubyte)(va * 255.0); 52 | } 53 | } 54 | 55 | public struct HSL { 56 | float h = 0.0; 57 | float s = 0.0; 58 | float l = 0.0; 59 | 60 | this(const float vh, const float vs, const float vl) { 61 | h = vh; 62 | s = vs; 63 | l = vl; 64 | } 65 | 66 | this(const short vh, const short vs, const short vl) { 67 | h = cast(float)vh; 68 | s = cast(float)vs; 69 | l = cast(float)vl; 70 | } 71 | } 72 | 73 | public struct Palette { 74 | RGBA[256] colors; 75 | 76 | public void adjust(const HSL hsl) { 77 | foreach (int index, ref RGBA color; colors) { 78 | HSL c = RGBAtoHSL(color); 79 | 80 | c.h = (c.h + hsl.h) % 360.0; 81 | c.s = (c.s + hsl.s) % 100.0; 82 | c.l = (c.l + hsl.l) % 100.0; 83 | 84 | colors[index] = HSLtoRGBA(c); 85 | } 86 | } 87 | } 88 | 89 | 90 | public HSL RGBAtoHSL(const RGBA color) { 91 | const float r = color.r / 255.0; 92 | const float g = color.g / 255.0; 93 | const float b = color.b / 255.0; 94 | 95 | const float cmax = max(r, g, b); 96 | const float cmin = min(r, g, b); 97 | 98 | float h = 0.0; 99 | float s = 0.0; 100 | const float l = (cmax + cmin) / 2.0; 101 | 102 | if (cmax == cmin) { 103 | h = 0.0; 104 | s = 0.0; 105 | } else { 106 | float d = cmax - cmin; 107 | s = l > 0.5 ? d / (2.0 - cmax - cmin) : d / (cmax + cmin); 108 | 109 | if (cmax == r) { 110 | h = (g - b) / d + (g < b ? 6.0 : 0.0); 111 | } 112 | if (cmax == g) { 113 | h = (b - r) / d + 2.0; 114 | } 115 | if (cmax == b) { 116 | h = (r - g) / d + 4.0; 117 | } 118 | h /= 6.0; 119 | } 120 | 121 | return HSL(h * 360.0, s * 100.0, l * 100.0); 122 | } 123 | 124 | private float hue2rgb(const float p, const float q, float t) pure { 125 | if (t < 0.0) { 126 | t += 1.0; 127 | } 128 | if (t > 1.0) { 129 | t -= 1.0; 130 | } 131 | 132 | if (t < 1.0 / 6.0) { 133 | return p + (q - p) * 6.0 * t; 134 | } 135 | if (t < 1.0 / 2.0) { 136 | return q; 137 | } 138 | if (t < 2.0 / 3.0) { 139 | return p + (q - p) * (2.0 / 3.0 - t) * 6.0; 140 | } 141 | 142 | return p; 143 | } 144 | 145 | public RGBA HSLtoRGBA(const HSL color) { 146 | const float h = color.h / 360.0; 147 | const float s = color.s / 100.0; 148 | const float l = color.l / 100.0; 149 | 150 | if (s == 0.0) { 151 | return RGBA(l, l, l, 0.0); 152 | } 153 | 154 | const float q = l < 0.5 ? l * (1.0 + s) : l + s - l * s; 155 | const float p = 2.0 * l - q; 156 | 157 | return RGBA( 158 | hue2rgb(p, q, h + 1.0 / 3.0), 159 | hue2rgb(p, q, h), 160 | hue2rgb(p, q, h - 1.0 / 3.0), 161 | 0.0 162 | ); 163 | } 164 | -------------------------------------------------------------------------------- /src/camera.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module render.camera; 29 | 30 | import std.stdio; 31 | import std.algorithm; 32 | import std.math; 33 | 34 | import util.vector3; 35 | import util.matrix4; 36 | import util.rectangle; 37 | 38 | 39 | public final class Camera { 40 | private Vector3 _position; 41 | private Vector3 _positionLast; 42 | private Vector3 _positionLerp; 43 | 44 | private Vector3 _velocity; 45 | 46 | private float _fov; 47 | private float _width; 48 | private float _height; 49 | private float _aspectRatio; 50 | 51 | private float _nearPlane; 52 | private float _farPlane; 53 | 54 | private Matrix4 _projection; 55 | private Matrix4 _view; 56 | private Matrix4 _projectionView; 57 | 58 | 59 | private static immutable Vector3 UP = Vector3(0, 1, 0); 60 | 61 | private static immutable float FOV_MIN = 10.0; 62 | private static immutable float FOV_MAX = 179.0; 63 | 64 | 65 | this(const float width, const float height, const float fov, const float nearPlane, const float farPlane) { 66 | _width = width; 67 | _height = height; 68 | _aspectRatio = _width / _height; 69 | 70 | _fov = min(max(fov, FOV_MIN), FOV_MAX); 71 | _nearPlane = nearPlane; 72 | _farPlane = farPlane; 73 | 74 | setProjection(); 75 | } 76 | 77 | private void setProjection() { 78 | _projection = matrix4Perspective(_fov, _aspectRatio, _nearPlane, _farPlane); 79 | } 80 | 81 | public void update(const double delta) { 82 | _velocity = _velocity * 0.8; 83 | _position += _velocity; 84 | } 85 | 86 | public void setThrust(const Vector3 thrust) { 87 | _velocity = thrust; 88 | } 89 | 90 | public void setVelocity(const Vector3 velocity) { 91 | _velocity = velocity; 92 | } 93 | 94 | public void move(const Vector3 amount) { 95 | _position += amount; 96 | } 97 | 98 | public void set(const Vector3 position) { 99 | _position = position; 100 | } 101 | 102 | public void interpolateStart() { 103 | _positionLast = _position; 104 | } 105 | 106 | public void interpolateEnd(const double t) { 107 | _positionLerp = _positionLast + (_position - _positionLast) * t; 108 | 109 | Vector3 lookAt = _positionLerp; 110 | lookAt.z = 0; 111 | _view = matrix4LookAt(_positionLerp, lookAt, UP); 112 | _projectionView = matrix4Multiply(_projection, _view); 113 | } 114 | 115 | public Rectangle unproject() { 116 | const float Hfar = tan(_fov * (PI / 180.0) / 2.0) * _position.z; 117 | const float Wfar = Hfar * _aspectRatio; 118 | 119 | return Rectangle( 120 | cast(int)(_position.x - Wfar), 121 | cast(int)(_position.y - Hfar), 122 | cast(int)(_position.x + Wfar), 123 | cast(int)(_position.y + Hfar) 124 | ); 125 | } 126 | 127 | @property public void fov(const float fov) { 128 | _fov = min(max(fov, FOV_MIN), FOV_MAX); 129 | setProjection(); 130 | } 131 | 132 | @property public Matrix4 projectionViewMatrix() { 133 | return _projectionView; 134 | } 135 | 136 | @property public Vector3 position() { 137 | return _position; 138 | } 139 | 140 | @property public Vector3 velocity() { 141 | return _velocity; 142 | } 143 | 144 | @property public float width() { 145 | return _width; 146 | } 147 | 148 | @property public float height() { 149 | return _height; 150 | } 151 | } -------------------------------------------------------------------------------- /src/missionreadergta2.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.script.missionreadergta2; 29 | 30 | import std.stdio; 31 | import std.file; 32 | import std.path; 33 | import std.algorithm; 34 | import std.string; 35 | 36 | import game.game; 37 | 38 | import game.script.missionlist; 39 | import game.script.missionreader; 40 | import game.script.gta2script; 41 | 42 | import util.log; 43 | 44 | 45 | public final class MissionReaderGTA2 : MissionReader { 46 | 47 | this(const string baseDir) { 48 | readSequence(baseDir, buildPath(baseDir, "test1.seq")); 49 | 50 | // Read MMP files. 51 | string[] files; 52 | foreach (DirEntry entry; dirEntries(baseDir, SpanMode.breadth)) { 53 | if (!endsWith(entry.name, ".mmp")) { 54 | continue; 55 | } 56 | files ~= entry.name; 57 | } 58 | sort(files); 59 | foreach (string name; files) { 60 | readSequence(baseDir, name); 61 | _missions[_missions.length - 1].type = MissionType.MULTIPLAYER; 62 | } 63 | } 64 | 65 | private void readSequence(const string baseDir, const string fileName) { 66 | Mission mission; 67 | 68 | Log.write(Color.NORMAL, "Reading GTA 2 sequence '%s'...", fileName); 69 | 70 | const string input = cast(string)read(fileName); 71 | foreach (string line; input.splitter('\n')) { 72 | ptrdiff_t index = line.indexOf('='); 73 | if (index >= 0) { 74 | const string key = strip(line[0..index]); 75 | const string value = strip(line[index + 1..$]); 76 | 77 | if (key == "MainOrBonus") { 78 | if (value == "MAIN") { 79 | mission.type = MissionType.MAIN; 80 | } else if (value == "BONUS") { 81 | mission.type = MissionType.BONUS; 82 | } else { 83 | throw new Exception(format("Unknown MainOrBonus value '%s'.", value)); 84 | } 85 | 86 | } else if (key == "GMPFile") { 87 | if (gameDemo) { 88 | mission.mapName = stripExtension(value) ~ "demo" ~ extension(value); 89 | } else { 90 | mission.mapName = value; 91 | } 92 | 93 | } else if (key == "STYFile") { 94 | mission.styleName = value; 95 | 96 | } else if (key == "SCRFile") { 97 | string scriptName; 98 | if (gameDemo) { 99 | scriptName = stripExtension(value) ~ "demo" ~ extension(value); 100 | } else { 101 | scriptName = value; 102 | } 103 | mission.script = new GTA2Script(buildPath(baseDir, scriptName)); 104 | 105 | } else if (key == "Description") { 106 | mission.name = value; 107 | mission.cityName = "Anywhere"; 108 | 109 | if (gameDemo) { 110 | mission.audioName = "audio/bil"; 111 | } else { 112 | const string styleName = mission.styleName[0..mission.styleName.length - 4]; 113 | mission.audioName = format("audio/%s", styleName); 114 | } 115 | 116 | _missions ~= mission; 117 | mission = mission.init; 118 | 119 | } 120 | } 121 | } 122 | } 123 | 124 | } -------------------------------------------------------------------------------- /src/block.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.map.block; 29 | 30 | import game.map.map; 31 | 32 | import game.style.style; 33 | 34 | 35 | public immutable MapCoord BLOCK_SIZE = 64; 36 | 37 | 38 | public enum FaceIndex : ubyte { 39 | LEFT, 40 | RIGHT, 41 | TOP, 42 | BOTTOM, 43 | LID 44 | } 45 | 46 | public enum BlockDirection : ushort { 47 | NONE = 0x0, 48 | NORMAL_UP = 0x1, 49 | NORMAL_DOWN = 0x2, 50 | NORMAL_LEFT = 0x4, 51 | NORMAL_RIGHT = 0x8, 52 | SPECIAL_UP = 0x100, 53 | SPECIAL_DOWN = 0x200, 54 | SPECIAL_LEFT = 0x400, 55 | SPECIAL_RIGHT = 0x800 56 | } 57 | 58 | public enum BlockType : ubyte { 59 | AIR = 0, 60 | WATER = 1, 61 | ROAD = 2, 62 | PAVEMENT = 3, 63 | FIELD = 4, 64 | BUILDING = 5 65 | } 66 | 67 | public enum BlockFlags : ubyte { 68 | NONE = 0, 69 | RAILWAY = 1, 70 | TRAFFIC_LIGHT_1 = 2, 71 | TRAFFIC_LIGHT_2 = 4, 72 | TRAFFIC_LIGHT_3 = 8, 73 | TRAFFIC_LIGHT_4 = 16 74 | } 75 | 76 | public enum BlockShape : ubyte { 77 | CUBE = 0, 78 | 79 | SLOPE_UP26_LOW = 1, 80 | SLOPE_UP26_HIGH = 2, 81 | SLOPE_DOWN26_LOW = 3, 82 | SLOPE_DOWN26_HIGH = 4, 83 | SLOPE_LEFT26_LOW = 5, 84 | SLOPE_LEFT26_HIGH = 6, 85 | SLOPE_RIGHT26_LOW = 7, 86 | SLOPE_RIGHT26_HIGH = 8, 87 | 88 | SLOPE_UP7_1 = 9, 89 | SLOPE_UP7_2 = 10, 90 | SLOPE_UP7_3 = 11, 91 | SLOPE_UP7_4 = 12, 92 | SLOPE_UP7_5 = 13, 93 | SLOPE_UP7_6 = 14, 94 | SLOPE_UP7_7 = 15, 95 | SLOPE_UP7_8 = 16, 96 | 97 | SLOPE_DOWN7_1 = 17, 98 | SLOPE_DOWN7_2 = 18, 99 | SLOPE_DOWN7_3 = 19, 100 | SLOPE_DOWN7_4 = 20, 101 | SLOPE_DOWN7_5 = 21, 102 | SLOPE_DOWN7_6 = 22, 103 | SLOPE_DOWN7_7 = 23, 104 | SLOPE_DOWN7_8 = 24, 105 | 106 | SLOPE_LEFT7_1 = 25, 107 | SLOPE_LEFT7_2 = 26, 108 | SLOPE_LEFT7_3 = 27, 109 | SLOPE_LEFT7_4 = 28, 110 | SLOPE_LEFT7_5 = 29, 111 | SLOPE_LEFT7_6 = 30, 112 | SLOPE_LEFT7_7 = 31, 113 | SLOPE_LEFT7_8 = 32, 114 | 115 | SLOPE_RIGHT7_1 = 33, 116 | SLOPE_RIGHT7_2 = 34, 117 | SLOPE_RIGHT7_3 = 35, 118 | SLOPE_RIGHT7_4 = 36, 119 | SLOPE_RIGHT7_5 = 37, 120 | SLOPE_RIGHT7_6 = 38, 121 | SLOPE_RIGHT7_7 = 39, 122 | SLOPE_RIGHT7_8 = 40, 123 | 124 | SLOPE_UP45 = 41, 125 | SLOPE_DOWN45 = 42, 126 | SLOPE_LEFT45 = 43, 127 | SLOPE_RIGHT45 = 44, 128 | 129 | DIAG_UPLEFT = 45, 130 | DIAG_UPRIGHT = 46, 131 | DIAG_DOWNLEFT = 47, 132 | DIAG_DOWNRIGHT = 48, 133 | 134 | DIAG_SLOPE4_TOPLEFT = 49, 135 | DIAG_SLOPE4_TOPRIGHT = 50, 136 | DIAG_SLOPE4_BOTTOMLEFT = 51, 137 | DIAG_SLOPE4_BOTTOMRIGHT = 52, 138 | 139 | PART_LEFT = 53, 140 | PART_RIGHT = 54, 141 | PART_TOP = 55, 142 | PART_BOTTOM = 56, 143 | 144 | PART_TOPLEFT = 57, 145 | PART_TOPRIGHT = 58, 146 | PART_BOTTOMRIGHT = 59, 147 | PART_BOTTOMLEFT = 60, 148 | 149 | PART_CENTRE = 61, 150 | 151 | UNUSED = 62, 152 | 153 | SLOPE_ABOVE = 63, 154 | 155 | DIAG_SLOPE3_TOPLEFT = 64, 156 | DIAG_SLOPE3_TOPRIGHT = 65, 157 | DIAG_SLOPE3_BOTTOMLEFT = 66, 158 | DIAG_SLOPE3_BOTTOMRIGHT = 67 159 | } 160 | 161 | 162 | public enum BlockFaceFlags : ubyte { 163 | NONE = 0x0, 164 | WALL = 0x1, 165 | BULLET_WALL = 0x2, 166 | FLAT = 0x4, 167 | FLIP = 0x8, 168 | DOUBLESIDED = 0x10, 169 | SHADE0 = 0x20, 170 | SHADE1 = 0x40, 171 | } 172 | 173 | public struct BlockFace { 174 | BlockTextureIndex texture; 175 | BlockFaceFlags flags; 176 | float brightness = 1.0; 177 | float rotation = 0.0; 178 | } 179 | 180 | public struct Block { 181 | BlockDirection directions; 182 | BlockType type; 183 | BlockFlags flags; 184 | BlockShape shape; 185 | ubyte lidTextureRemap; 186 | 187 | BlockFace[5] faces; 188 | } 189 | -------------------------------------------------------------------------------- /src/renderer.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module render.renderer; 29 | 30 | import std.stdio; 31 | import std.string; 32 | 33 | import derelict.sdl2.sdl; 34 | import derelict.opengl; 35 | 36 | import util.console; 37 | import util.log; 38 | 39 | 40 | public final class Renderer { 41 | 42 | private uint _width; 43 | private uint _height; 44 | 45 | private SDL_Window* _window; 46 | private SDL_GLContext _gl; 47 | 48 | private CVar _cvarFullscreen; 49 | private CVar _cvarVSync; 50 | 51 | 52 | this() { 53 | _cvarFullscreen = CVars.get("r_fullscreen"); 54 | _cvarVSync = CVars.get("r_vsync"); 55 | 56 | _width = cast(uint)CVars.get("r_width").intVal; 57 | _height = cast(uint)CVars.get("r_height").intVal; 58 | 59 | DerelictGL3.load(); 60 | 61 | SDL_InitSubSystem(SDL_INIT_VIDEO); 62 | 63 | SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); 64 | SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, CVars.get("r_aasamples").intVal > 0 ? 1 : 0); 65 | SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, cast(uint)CVars.get("r_aasamples").intVal); 66 | 67 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); 68 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); 69 | 70 | // Create window. 71 | SDL_WindowFlags flags = SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI; 72 | 73 | // Fullscreen. 74 | if (_cvarFullscreen.intVal == 1) { 75 | Log.write(Color.NORMAL, "Running in %dx%d fullscreen mode.", _width, _height); 76 | flags |= SDL_WINDOW_FULLSCREEN; 77 | 78 | // Fullscreen, at desktop resolution. 79 | } else if (_cvarFullscreen.intVal == 2) { 80 | flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; 81 | 82 | SDL_DisplayMode desktopMode; 83 | SDL_GetDesktopDisplayMode(0, &desktopMode); 84 | 85 | _width = desktopMode.w; 86 | _height = desktopMode.h; 87 | 88 | Log.write(Color.NORMAL, "Running in %dx%d fullscreen windowed mode.", _width, _height); 89 | 90 | // Windowed mode. 91 | } else { 92 | Log.write(Color.NORMAL, "Running in %dx%d windowed mode.", _width, _height); 93 | 94 | } 95 | 96 | _window = SDL_CreateWindow( 97 | "GTA Test", 98 | SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 99 | width, height, 100 | flags 101 | ); 102 | if (_window == null) { 103 | throw new Exception(format("Could not create window: %s", SDL_GetError())); 104 | } 105 | 106 | _gl = SDL_GL_CreateContext(_window); 107 | SDL_GL_SetSwapInterval(_cvarVSync.intVal ? 1 : 0); 108 | 109 | glClearColor(0.0, 0.0, 0.0, 0.0); 110 | glClearDepth(1.0f); 111 | 112 | glDepthMask(GL_TRUE); 113 | glDepthFunc(GL_LEQUAL); 114 | glDepthRange(0.0f, 1.0f); 115 | 116 | glCullFace(GL_BACK); 117 | glFrontFace(GL_CCW); 118 | 119 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 120 | 121 | glEnable(GL_DEPTH_TEST); 122 | glEnable(GL_CULL_FACE); 123 | 124 | GLVersion ver = DerelictGL3.reload(); 125 | Log.write(Color.NORMAL, "Loaded OpenGL version %.1f.", ver / 10.0); 126 | } 127 | 128 | ~this() { 129 | SDL_GL_DeleteContext(_gl); 130 | SDL_DestroyWindow(_window); 131 | } 132 | 133 | public void start() { 134 | } 135 | 136 | public void end() { 137 | SDL_GL_SwapWindow(_window); 138 | } 139 | 140 | public void clear() { 141 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 142 | } 143 | 144 | @property uint width() { 145 | return _width; 146 | } 147 | 148 | @property uint height() { 149 | return _height; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/vertexbuffer.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module render.vertexbuffer; 29 | 30 | import derelict.opengl; 31 | 32 | import render.indexbuffer; 33 | import render.enums; 34 | 35 | 36 | public final class VertexBuffer(T) { 37 | 38 | private GLuint _id; 39 | 40 | private PrimitiveType _primitiveType; 41 | private BufferUsage _usage; 42 | private int _subBufferCount; 43 | 44 | private T[][] _data; 45 | private size_t[] _offsets; 46 | private size_t[] _sizes; 47 | 48 | private IndexBuffer _indexBuffer; 49 | private bool _isIndexed; 50 | 51 | 52 | this(const PrimitiveType primitiveType, const int subBufferCount, const BufferUsage usage, const bool isIndexed) { 53 | _primitiveType = primitiveType; 54 | _subBufferCount = subBufferCount; 55 | _usage = usage; 56 | _isIndexed = isIndexed; 57 | 58 | _offsets = new size_t[_subBufferCount]; 59 | _sizes = new size_t[_subBufferCount]; 60 | _data = new T[][](_subBufferCount); 61 | 62 | if (_isIndexed) { 63 | _indexBuffer = new IndexBuffer(_primitiveType, _subBufferCount, _usage); 64 | } 65 | } 66 | 67 | ~this() { 68 | glDeleteBuffers(1, &_id); 69 | } 70 | 71 | public void add(const T[] data, const int bufferIndex) { 72 | _data[bufferIndex] ~= data; 73 | } 74 | 75 | public void add(const T[] data, const GLushort[] indices, const int subBufferIndex) { 76 | _indexBuffer.add(indices, subBufferIndex, _data[subBufferIndex].length); 77 | add(data, subBufferIndex); 78 | } 79 | 80 | public void generate() { 81 | if (_id != 0) { 82 | throw new Exception("Attempted to generate an already generated vertex buffer."); 83 | } 84 | 85 | glGenBuffers(1, &_id); 86 | glBindBuffer(GL_ARRAY_BUFFER, _id); 87 | 88 | size_t size; 89 | foreach (T[] data; _data) { 90 | size += data.length * T.sizeof; 91 | } 92 | glBufferData(GL_ARRAY_BUFFER, size, null, _usage); 93 | 94 | size_t elementOffset = 0; 95 | size_t offset = 0; 96 | foreach (int index, T[] data; _data) { 97 | _offsets[index] = elementOffset; 98 | _sizes[index] = data.length; 99 | 100 | if (data.length) { 101 | glBufferSubData(GL_ARRAY_BUFFER, offset, data.length * T.sizeof, &data[0]); 102 | } 103 | 104 | offset += data.length * T.sizeof; 105 | elementOffset += data.length; 106 | } 107 | 108 | if (_isIndexed) { 109 | for (int subBufferIndex = 1; subBufferIndex < _subBufferCount; subBufferIndex++) { 110 | _indexBuffer.setStartIndex(subBufferIndex, _data[subBufferIndex - 1].length); 111 | } 112 | _indexBuffer.generate(); 113 | } 114 | } 115 | 116 | public void clear() { 117 | _data.length = 0; 118 | if (_isIndexed) { 119 | _indexBuffer.clear(); 120 | } 121 | } 122 | 123 | public void bind() { 124 | if (_id == 0) { 125 | throw new Exception("Attempted to bind vertex buffer before generating."); 126 | } 127 | glBindBuffer(GL_ARRAY_BUFFER, _id); 128 | 129 | if (_isIndexed) { 130 | _indexBuffer.bind(); 131 | } 132 | } 133 | 134 | public void draw(const int subBufferIndex) { 135 | if (_isIndexed) { 136 | _indexBuffer.draw(subBufferIndex); 137 | } else { 138 | throw new Exception("VertexBuffer does not support drawing without indices!"); 139 | //glDrawArray(_primitiveType, _indexBuffer.size(subBufferIndex), GL_UNSIGNED_INT, cast(void*)_indexBuffer.offset(subBufferIndex)); 140 | } 141 | } 142 | 143 | @property public size_t size(const int subBufferIndex) { 144 | return _sizes[subBufferIndex]; 145 | } 146 | 147 | @property public size_t offset(const int subBufferIndex) { 148 | return _offsets[subBufferIndex]; 149 | } 150 | } -------------------------------------------------------------------------------- /src/texture.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module render.texture; 29 | 30 | import std.stdio; 31 | 32 | import derelict.opengl; 33 | 34 | import render.enums; 35 | 36 | import util.console; 37 | 38 | 39 | public final class Texture { 40 | 41 | private GLuint _size; 42 | private TextureFormat _pixelFormat; 43 | private InternalTextureFormat _internalFormat; 44 | 45 | private TextureFilter _filterMagnify = TextureFilter.LINEAR; 46 | private TextureFilter _filterMinify = TextureFilter.LINEAR; 47 | private GLuint _anisotropy = 0; 48 | private TextureWrapMode _wrapMode = TextureWrapMode.CLAMP; 49 | private GLuint _mipmapLevels = 0; 50 | 51 | private GLuint _id; 52 | 53 | 54 | this(const GLuint size, const InternalTextureFormat internalFormat, const TextureFormat pixelFormat) { 55 | _size = size; 56 | _internalFormat = internalFormat; 57 | _pixelFormat = pixelFormat; 58 | 59 | _anisotropy = cast(uint)CVars.get("r_anisotropy").intVal; 60 | const uint filterType = cast(uint)CVars.get("r_filter").intVal; 61 | switch (filterType) { 62 | default: 63 | case 0: 64 | _filterMagnify = TextureFilter.NEAREST; 65 | _filterMinify = TextureFilter.NEAREST; 66 | break; 67 | case 1: 68 | _filterMagnify = TextureFilter.LINEAR; 69 | _filterMinify = TextureFilter.LINEAR; 70 | break; 71 | } 72 | 73 | glGenTextures(1, &_id); 74 | 75 | bind(); 76 | glTexStorage2D(GL_TEXTURE_2D, _mipmapLevels + 1, _internalFormat, _size, _size); 77 | } 78 | 79 | ~this() { 80 | glDeleteTextures(1, &_id); 81 | } 82 | 83 | public void generate(ubyte[] data) { 84 | bind(); 85 | glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _size, _size, _pixelFormat, GL_UNSIGNED_BYTE, &data[0]); 86 | 87 | if (_mipmapLevels > 0) { 88 | glGenerateMipmap(GL_TEXTURE_2D); 89 | 90 | if (_filterMinify == TextureFilter.NEAREST) { 91 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); 92 | } else if (_filterMinify == TextureFilter.LINEAR) { 93 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 94 | } 95 | } else { 96 | if (_filterMinify == TextureFilter.NEAREST) { 97 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 98 | } else if (_filterMinify == TextureFilter.LINEAR) { 99 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 100 | } 101 | } 102 | 103 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _filterMagnify); 104 | 105 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, _wrapMode); 106 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, _wrapMode); 107 | 108 | //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, _anisotropy); 109 | } 110 | 111 | private void update(ubyte[] data) { 112 | glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _size, _size, _pixelFormat, GL_UNSIGNED_BYTE, &data[0]); 113 | } 114 | 115 | public void bind() { 116 | glBindTexture(GL_TEXTURE_2D, _id); 117 | } 118 | 119 | @property public void wrapMode(const TextureWrapMode wrapMode) { 120 | _wrapMode = wrapMode; 121 | } 122 | 123 | @property public void anisotropy(const GLuint anisotropy) { 124 | _anisotropy = anisotropy; 125 | } 126 | 127 | @property public void filterMagnify(const TextureFilter filter) { 128 | _filterMagnify = filter; 129 | } 130 | 131 | @property public void filterMinify(const TextureFilter filter) { 132 | _filterMinify = filter; 133 | } 134 | 135 | @property public void mipmapLevels(const GLuint levels) { 136 | _mipmapLevels = levels; 137 | } 138 | 139 | @property public uint size() { 140 | return _size; 141 | } 142 | 143 | @property public GLint id() { 144 | return _id; 145 | } 146 | } -------------------------------------------------------------------------------- /src/delta.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.delta; 29 | 30 | import std.stdio; 31 | 32 | 33 | // GTA delta data format: 34 | // 1 byte: delta x offset inside sprite rectangle 35 | // 1 byte: delta y offset inside sprite rectangle 36 | // while inside delta data range: 37 | // 1 byte: number of pixels (n) 38 | // n bytes: pixel data 39 | // if end of data range reached, break 40 | // 1 byte: number of bytes to skip. skipping an entire row will skip > sprite.width, because 41 | // they are meant to be stored in 256x256 pages. 42 | // 1 byte: 0x00 43 | 44 | public class Delta { 45 | 46 | private ubyte[] _data; 47 | 48 | private uint _imageWidth; 49 | 50 | 51 | // Creates a new delta from raw data. 52 | this(const ubyte[] data, const uint imageWidth) { 53 | _imageWidth = imageWidth; 54 | _data = data.dup; 55 | } 56 | 57 | // Creates a new delta from a mask and image. 58 | this(const ubyte[] image, const ubyte[] mask, const uint imageWidth) { 59 | _imageWidth = imageWidth; 60 | _data.length = 0; 61 | 62 | uint offset = 0; 63 | while (offset < image.length) { 64 | 65 | // Count the pixels to skip. 66 | const uint skipStart = offset; 67 | while (!mask[offset++] && offset < mask.length) {} 68 | if (offset >= mask.length) { 69 | break; 70 | } 71 | offset--; 72 | 73 | // Add starting x, y. 74 | const uint skip = offset - skipStart; 75 | if (_data.length == 0) { 76 | _data ~= [cast(ubyte)(skip % imageWidth), cast(ubyte)(skip / imageWidth)]; 77 | 78 | // Add skip instruction. 79 | } else { 80 | _data ~= [cast(ubyte)skip, 0]; 81 | 82 | } 83 | 84 | // Count pixels to draw. 85 | const uint dataStart = offset; 86 | while (mask[offset++] && offset < mask.length) {} 87 | offset--; 88 | 89 | // Add pixels to draw. 90 | const uint dataEnd = offset; 91 | _data ~= cast(ubyte)(dataEnd - dataStart); 92 | _data ~= image[dataStart..dataEnd]; 93 | } 94 | } 95 | 96 | // Makes this delta's skip instructions take into account that the target image is not stored in a page with other sprites. 97 | public void makeNonPaged(const uint pageSize) { 98 | uint offset = 2; 99 | 100 | while (offset < _data.length) { 101 | offset += _data[offset++]; 102 | 103 | if (offset >= _data.length) { 104 | break; 105 | } 106 | 107 | // If the delta's skip command would go beyond the sprite's width, 108 | // transform it to be in the bounds of the sprite width instead of the page width. 109 | if (_data[offset] >= _imageWidth) { 110 | _data[offset] -= pageSize - _imageWidth; 111 | } 112 | offset += 2; 113 | } 114 | } 115 | 116 | // Applies this delta to an image. 117 | public void applyTo(ref ubyte[] image) { 118 | uint destOffset = _data[1] * _imageWidth + _data[0]; 119 | uint offset = 2; 120 | 121 | while (offset < _data.length) { 122 | const ubyte pixels = _data[offset++]; 123 | image[destOffset..destOffset + pixels] = _data[offset..offset + pixels]; 124 | offset += pixels; 125 | destOffset += pixels; 126 | 127 | if (offset >= _data.length) { 128 | break; 129 | } 130 | 131 | destOffset += _data[offset]; 132 | offset += 2; 133 | } 134 | } 135 | 136 | // Returns this delta's mask image, where non-0 pixels are pixels that are present in the delta information. 137 | public void applyMaskTo(ref ubyte[] mask) { 138 | uint destOffset = _data[1] * _imageWidth + _data[0]; 139 | uint offset = 2; 140 | 141 | while (offset < _data.length) { 142 | const ubyte pixels = _data[offset++]; 143 | mask[destOffset..destOffset + pixels] = 0xFF; 144 | offset += pixels; 145 | destOffset += pixels; 146 | 147 | if (offset >= _data.length) { 148 | break; 149 | } 150 | 151 | destOffset += _data[offset]; 152 | offset += 2; 153 | } 154 | } 155 | 156 | } -------------------------------------------------------------------------------- /src/main.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | import std.stdio; 29 | import std.string; 30 | 31 | import derelict.sdl2.sdl; 32 | 33 | import game.game; 34 | import game.gamestate; 35 | 36 | import util.timer; 37 | import util.console; 38 | import util.log; 39 | 40 | import cvars; 41 | 42 | 43 | private immutable uint MAX_TICKSKIP = 5; 44 | 45 | private immutable uint TICKRATE = 25; 46 | private immutable ulong TICKTIME = 1000000 / TICKRATE; 47 | 48 | private immutable string CONFIG_FILENAME = "data/config.json"; 49 | 50 | 51 | private bool running; 52 | private ubyte* keys; 53 | 54 | private ulong renderStart; 55 | private ulong renderEnd; 56 | 57 | private ulong updateStart; 58 | private ulong updateEnd; 59 | 60 | private ulong nextTick; 61 | private ulong nextFrame; 62 | 63 | private Game cGame; 64 | 65 | private CVar cvarFramerateLimit; 66 | private CVar cvarVSync; 67 | 68 | 69 | int main(string[] argv) { 70 | 71 | registerCVars(); 72 | CVars.load(CONFIG_FILENAME); 73 | 74 | cvarFramerateLimit = CVars.get("r_frameratelimit"); 75 | cvarVSync = CVars.get("r_vsync"); 76 | 77 | DerelictSDL2.load(); 78 | 79 | SDL_version sdlVersionCompiled; 80 | SDL_version sdlVersionLinked; 81 | 82 | SDL_VERSION(&sdlVersionCompiled); 83 | SDL_GetVersion(&sdlVersionLinked); 84 | 85 | Log.write(Color.NORMAL, "SDL (compile) %d.%d.%d", sdlVersionCompiled.major, sdlVersionCompiled.minor, sdlVersionCompiled.patch); 86 | Log.write(Color.NORMAL, "SDL (link) %d.%d.%d", sdlVersionLinked.major, sdlVersionLinked.minor, sdlVersionLinked.patch); 87 | 88 | SDL_Init(0); 89 | 90 | timerInit(); 91 | 92 | ulong counter; 93 | uint loopCount; 94 | 95 | double interpolation = 0.0f; 96 | 97 | // Determine framerate control settings. 98 | ulong renderTime; 99 | if (cvarFramerateLimit.intVal && !cvarVSync.intVal) { 100 | renderTime = 1000000 / cvarFramerateLimit.intVal; 101 | Log.write(Color.NORMAL, "Limiting framerate to %d FPS.", cvarFramerateLimit.intVal); 102 | } else { 103 | if (cvarVSync.intVal) { 104 | Log.write(Color.NORMAL, "VSync enabled."); 105 | } 106 | renderTime = 0; 107 | } 108 | 109 | cGame = new Game(); 110 | 111 | nextTick = timerGetCounter(); 112 | nextFrame = timerGetCounter(); 113 | 114 | running = true; 115 | for(;;) { 116 | // Execute ticks until we have caught up with the next tick time. 117 | // If many ticks were missed, do not try and catch up to all of them without rendering. 118 | loopCount = 0; 119 | while (running && timerGetCounter() > nextTick && loopCount < MAX_TICKSKIP) { 120 | loopCount++; 121 | 122 | updateKeys(); 123 | 124 | updateStart = timerGetCounter(); 125 | cGame.input(keys); 126 | cGame.update(1.0f / TICKRATE); 127 | updateEnd = timerGetCounter(); 128 | 129 | // Keep track of when the next tick must occur. 130 | nextTick += TICKTIME; 131 | } 132 | 133 | if (running == false) { 134 | break; 135 | } 136 | 137 | // Render a frame if there is time to render a full frame before the next tick. 138 | if (renderTime) { 139 | if (nextFrame < nextTick) { 140 | 141 | // Wait until we have to render the next frame. 142 | counter = timerGetCounter(); 143 | if (cast(long)nextFrame - cast(long)counter > 0) { 144 | timerWait(nextFrame - counter); 145 | } 146 | 147 | // Calculate the time of the next frame, and what position to interpolate the current frame at. 148 | nextFrame = timerGetCounter() + renderTime; 149 | render(); 150 | } 151 | } else { 152 | render(); 153 | } 154 | } 155 | 156 | cGame.destroy(); 157 | CVars.save(CONFIG_FILENAME); 158 | 159 | SDL_Quit(); 160 | 161 | return 0; 162 | } 163 | 164 | private void render() { 165 | const ulong counter = timerGetCounter(); 166 | const double interpolation = cast(double)(counter + TICKTIME - nextTick) / cast(double)TICKTIME; 167 | 168 | renderStart = timerGetCounter(); 169 | cGame.render(interpolation); 170 | renderEnd = timerGetCounter(); 171 | 172 | //Log.write(Color.DEBUG, "R: %.2f ms, U: %.2f ms", (renderEnd - renderStart) / 1000.0, (updateEnd - updateStart) / 1000.0); 173 | } 174 | 175 | private void updateKeys() { 176 | SDL_Event event; 177 | 178 | while(SDL_PollEvent(&event)) { 179 | switch (event.type) { 180 | case SDL_QUIT: 181 | running = false; 182 | break; 183 | default: 184 | break; 185 | } 186 | } 187 | 188 | keys = SDL_GetKeyboardState(null); 189 | 190 | if (keys[SDL_SCANCODE_ESCAPE] == true) { 191 | running = false; 192 | } 193 | } -------------------------------------------------------------------------------- /src/strings.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.strings; 29 | 30 | import std.stdio; 31 | import std.string; 32 | import std.path; 33 | import std.utf; 34 | import std.algorithm; 35 | 36 | import util.binaryfile; 37 | import util.rifffile; 38 | import util.log; 39 | 40 | 41 | private struct Key { 42 | uint offset; 43 | string name; 44 | } 45 | 46 | 47 | public static final class Strings { 48 | 49 | private static string[string] _strings; 50 | 51 | 52 | public static load(const string fileName) { 53 | const string ext = toLower(extension(fileName)); 54 | if (ext == ".fxt") { 55 | readFXT(fileName); 56 | } else if (ext == ".gxt") { 57 | readGXT(fileName); 58 | } else { 59 | throw new Exception(format("Strings file '%s' is of an unknown format.", fileName)); 60 | } 61 | 62 | Log.write(Color.NORMAL, "Read %d strings.", _strings.length); 63 | } 64 | 65 | private static void readGXT(const string fileName) { 66 | Log.write(Color.NORMAL, "Reading GXT strings from '%s'...", fileName); 67 | 68 | BinaryFile file = new BinaryFile(fileName); 69 | RIFFFile riff = new RIFFFile(file); 70 | if (riff.type[0..3] != "GBL" || riff.versionNum != 0x64) { 71 | throw new Exception(format("Strings file '%s' is invalid.", fileName)); 72 | } 73 | 74 | // Read keys and offsets. 75 | const Chunk chunk = riff.getChunk("TKEY"); 76 | uint endOffset = file.offset + chunk.size; 77 | Key[] keys; 78 | while (file.offset < endOffset) { 79 | Key key; 80 | key.offset = file.readUInt(); 81 | key.name = file.readNullString(8); 82 | 83 | keys ~= key; 84 | } 85 | 86 | // Read strings. 87 | const Chunk textChunk = riff.getChunk("TDAT"); 88 | foreach (Key key; keys) { 89 | file.seek(textChunk.offset + key.offset); 90 | 91 | char[] text; 92 | while (!file.eof) { 93 | const ubyte c = file.readUByte(); 94 | const ubyte m = file.readUByte(); 95 | 96 | if (c == 0 && m == 0) { 97 | break; 98 | } 99 | 100 | // Insert control characters directly into the string. 101 | if (m != 0) { 102 | text ~= m; 103 | } 104 | text ~= c; 105 | } 106 | 107 | _strings[key.name] = cast(string)text; 108 | } 109 | } 110 | 111 | private static void readFXT(const string fileName) { 112 | Log.write(Color.NORMAL, "Reading FXT strings from '%s'...", fileName); 113 | 114 | BinaryFile file = new BinaryFile(fileName); 115 | ubyte[] data = file.readBytes(file.size); 116 | 117 | // Detect GTA1 or GTA1 demo type from the first character, which should always be a '['. 118 | ubyte enc; 119 | byte offset; 120 | if (data[0] == 0xBF) { 121 | enc = 0x63; 122 | offset = -1; 123 | } else if (data[0] == 0xA6) { 124 | enc = 0x67; 125 | offset = 28; 126 | } else { 127 | throw new Exception("Cannot identify FXT file type."); 128 | } 129 | 130 | // Decrypt first 8 bytes. 131 | for (int i = 0; i < 8; i++) { 132 | data[i] = cast(ubyte)(data[i] - enc); 133 | enc <<= 1; 134 | } 135 | 136 | // "Decrypt" rest of file. 137 | for (int i = 0; i < data.length; i++) { 138 | data[i] += offset; 139 | } 140 | 141 | // Divide into lines. 142 | char[][] lines; 143 | uint start = 0; 144 | foreach (int index, char c; data) { 145 | if (c == 0) { 146 | lines ~= cast(char[])data[start..index]; 147 | start = index + 1; 148 | } 149 | } 150 | 151 | // Parse lines. 152 | char[] key; 153 | char[] txt; 154 | foreach (char[] line; lines) { 155 | foreach (int index, char c; line) { 156 | if (c == ']') { 157 | key = line[1..index]; 158 | txt = line[index + 1..$]; 159 | if (key.length) { 160 | _strings[cast(string)key] = toUTF8(txt); 161 | } 162 | break; 163 | } 164 | } 165 | } 166 | } 167 | 168 | public static string get(const string key) { 169 | if (key !in _strings) { 170 | throw new Exception(format("Cannot find string key '%s'.", key)); 171 | } 172 | 173 | return _strings[key]; 174 | } 175 | 176 | public static void dump() { 177 | string[] keys = _strings.keys.dup; 178 | sort(keys); 179 | 180 | foreach (string key; keys) { 181 | Log.write(Color.DEBUG, "[%s] %s", key, _strings[key]); 182 | } 183 | } 184 | 185 | } -------------------------------------------------------------------------------- /src/entity.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.entities.entity; 29 | 30 | import audio.source; 31 | 32 | import game.style.style; 33 | 34 | import game.audiobank; 35 | import game.spritetextures; 36 | 37 | import util.vector3; 38 | import util.color; 39 | 40 | 41 | public enum EntityFlags : ushort { 42 | NONE = 0x0, 43 | INVISIBLE = 0x1, 44 | DISABLED = 0x2, 45 | NO_SHADOW = 0x4, 46 | } 47 | 48 | public enum EntityClass : ubyte { 49 | OBSTACLE, 50 | DECORATION, 51 | VEHICLE, 52 | POWERUP, 53 | PED, 54 | PROJECTILE, 55 | } 56 | 57 | 58 | public struct EntityType { 59 | string name; 60 | EntityClass classId; 61 | 62 | ushort width; 63 | ushort height; 64 | ushort depth; 65 | 66 | SpriteFrameIndex baseFrame; 67 | SpriteFrameIndex frameCount; 68 | 69 | ubyte weight; 70 | ushort aux; 71 | EntityFlags flags; 72 | } 73 | 74 | 75 | public class Entity { 76 | 77 | protected Vector3 _position; 78 | 79 | protected SpriteFrameIndex _baseFrame; 80 | protected SpriteFrameIndex _frame; 81 | protected SpriteFrameIndex _frameCount; 82 | 83 | protected SpriteTexture* _spriteTexture; 84 | 85 | protected AudioSource _audioSource; 86 | protected SoundIndex _sound; 87 | protected SoundType _soundType; 88 | 89 | protected ubyte _remap; 90 | protected HSL _hsl; 91 | 92 | protected EntityFlags _flags; 93 | 94 | protected ushort _width; 95 | protected ushort _height; 96 | protected ushort _depth; 97 | protected ubyte _weight; 98 | 99 | protected float _rotation = 0.0; 100 | protected float _pitch = 0.0; 101 | protected float _roll = 0.0; 102 | 103 | 104 | public this() { 105 | } 106 | 107 | public void initializeFromType(EntityType type) { 108 | _width = type.width; 109 | _height = type.height; 110 | _depth = type.depth; 111 | _weight = type.weight; 112 | _flags = type.flags; 113 | _baseFrame = type.baseFrame; 114 | _frameCount = type.frameCount; 115 | } 116 | 117 | public void update(const double delta) { 118 | } 119 | 120 | @property public SpriteFrameIndex baseFrame() { 121 | return _baseFrame; 122 | } 123 | 124 | @property public SpriteFrameIndex frameCount() { 125 | return _frameCount; 126 | } 127 | 128 | @property public SpriteFrameIndex frame() { 129 | return _frame; 130 | } 131 | 132 | @property public void frame(const SpriteFrameIndex frame) { 133 | _frame = frame; 134 | } 135 | 136 | @property public EntityFlags flags() { 137 | return _flags; 138 | } 139 | 140 | @property public void flags(const EntityFlags flags) { 141 | _flags = flags; 142 | } 143 | 144 | @property public ref Vector3 position() { 145 | return _position; 146 | } 147 | 148 | @property public void position(const Vector3 position) { 149 | _position = position; 150 | } 151 | 152 | @property public ubyte remap() { 153 | return _remap; 154 | } 155 | 156 | @property public void remap(const ubyte remap) { 157 | _remap = remap; 158 | } 159 | 160 | @property public HSL hsl() { 161 | return _hsl; 162 | } 163 | 164 | @property public void hsl(const HSL hsl) { 165 | _hsl = hsl; 166 | } 167 | 168 | @property public float rotation() { 169 | return _rotation; 170 | } 171 | 172 | @property public void rotation(const float rotation) { 173 | _rotation = rotation; 174 | } 175 | 176 | @property public float pitch() { 177 | return _pitch; 178 | } 179 | 180 | @property public void pitch(const float pitch) { 181 | _pitch = pitch; 182 | } 183 | 184 | @property public float roll() { 185 | return _roll; 186 | } 187 | 188 | @property public void roll(const float roll) { 189 | _roll = roll; 190 | } 191 | 192 | @property public uint deltaMask() { 193 | return 0; 194 | } 195 | 196 | @property public SpriteTexture* spriteTexture() { 197 | return _spriteTexture; 198 | } 199 | 200 | @property public void spriteTexture(SpriteTexture* spriteTexture) { 201 | _spriteTexture = spriteTexture; 202 | } 203 | 204 | @property public PaletteIndex palette(const SpriteFrame frame) { 205 | return frame.palette; 206 | } 207 | 208 | 209 | // Sound 210 | @property public AudioSource audioSource() { 211 | return _audioSource; 212 | } 213 | 214 | @property public void audioSource(AudioSource source) { 215 | _audioSource = source; 216 | } 217 | 218 | @property public SoundIndex sound() { 219 | return _sound; 220 | } 221 | 222 | @property public void sound(SoundIndex sound) { 223 | _sound = sound; 224 | } 225 | 226 | @property public SoundType soundType() { 227 | return _soundType; 228 | } 229 | 230 | @property public void soundType(const SoundType type) { 231 | _soundType = type; 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/mapprocessorgta2.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.map.mapprocessorgta2; 29 | 30 | import std.stdio; 31 | 32 | import game.map.map; 33 | import game.map.mapprocessor; 34 | import game.map.block; 35 | 36 | import game.style.style; 37 | 38 | import game.entities.entity; 39 | 40 | 41 | public final class MapProcessorGTA2 : MapProcessor { 42 | 43 | this(Map map) { 44 | super(map); 45 | } 46 | 47 | override public void process(Style style) { 48 | processBlocks(style); 49 | settleMapEntities(); 50 | } 51 | 52 | private void settleMapEntities() { 53 | MapEntity[] entities = _map.getMapEntities(); 54 | 55 | foreach (ref MapEntity entity; entities) { 56 | entity.z = _map.getSpawnZ(cast(MapCoord)entity.x, cast(MapCoord)entity.y); 57 | } 58 | } 59 | 60 | private void processBlocks(Style style) { 61 | Block[][][] blocks = _map.getBlocks(); 62 | float[] shades = _map.shades; 63 | 64 | for (MapCoord y = 0; y < _map.height; y++) { 65 | for (MapCoord x = 0; x < _map.width; x++) { 66 | foreach (ref Block block; blocks[x][y]) { 67 | 68 | // Side face shadowing. 69 | block.faces[FaceIndex.LEFT].brightness = shades[1]; 70 | block.faces[FaceIndex.BOTTOM].brightness = shades[3]; 71 | block.faces[FaceIndex.TOP].brightness = shades[5]; 72 | block.faces[FaceIndex.RIGHT].brightness = shades[7]; 73 | 74 | const BlockFaceFlags flags = block.faces[FaceIndex.LID].flags; 75 | if ((flags & BlockFaceFlags.SHADE0) && (flags & BlockFaceFlags.SHADE1)) { 76 | block.faces[FaceIndex.LID].brightness = shades[3]; 77 | } else if (flags & BlockFaceFlags.SHADE1) { 78 | block.faces[FaceIndex.LID].brightness = shades[2]; 79 | } else if (flags & BlockFaceFlags.SHADE0) { 80 | block.faces[FaceIndex.LID].brightness = shades[1]; 81 | } else { 82 | block.faces[FaceIndex.LID].brightness = shades[0]; 83 | } 84 | 85 | // Slope shadowing. Map brightness overwrites this. 86 | if (block.faces[FaceIndex.LID].brightness == 1.0) { 87 | 88 | // Up 89 | if (block.shape >= 9 && block.shape <= 16 || 90 | block.shape >= 1 && block.shape <= 2 || 91 | block.shape == 41) { 92 | block.faces[FaceIndex.LID].brightness = shades[2]; 93 | 94 | // Down 95 | } else if (block.shape >= 17 && block.shape <= 24 || 96 | block.shape >= 3 && block.shape <= 4 || 97 | block.shape == 42) { 98 | block.faces[FaceIndex.LID].brightness = shades[4]; 99 | 100 | // Left 101 | } else if (block.shape >= 25 && block.shape <= 32 || 102 | block.shape >= 5 && block.shape <= 6 || 103 | block.shape == 43) { 104 | block.faces[FaceIndex.LID].brightness = shades[6]; 105 | 106 | // Right 107 | } else if (block.shape >= 33 && block.shape <= 40 || 108 | block.shape >= 7 && block.shape <= 8 || 109 | block.shape == 44) { 110 | block.faces[FaceIndex.LID].brightness = shades[1]; 111 | 112 | // Top left diagonal 113 | } else if (block.shape == 45) { 114 | block.faces[FaceIndex.LEFT].brightness = shades[3]; 115 | 116 | // Top right diagonal 117 | } else if (block.shape == 46) { 118 | block.faces[FaceIndex.RIGHT].brightness = shades[6]; 119 | 120 | // Bottom left diagonal 121 | } else if (block.shape == 47) { 122 | block.faces[FaceIndex.LEFT].brightness = shades[2]; 123 | 124 | // Bottom right diagonal 125 | } else if (block.shape == 48) { 126 | block.faces[FaceIndex.RIGHT].brightness = shades[5]; 127 | 128 | // Top left 3\4 sided 129 | } else if (block.shape == 49 || block.shape == 64) { 130 | block.faces[FaceIndex.LEFT].brightness = shades[2]; 131 | 132 | // Top right 3\4 sided 133 | } else if (block.shape == 50 || block.shape == 65) { 134 | block.faces[FaceIndex.RIGHT].brightness = shades[5]; 135 | 136 | // Bottom left 3\4 sided 137 | } else if (block.shape == 51 || block.shape == 66) { 138 | block.faces[FaceIndex.RIGHT].brightness = shades[1]; 139 | 140 | // Bottom right 3\4 sided 141 | } else if (block.shape == 52 || block.shape == 67) { 142 | block.faces[FaceIndex.LEFT].brightness = shades[4]; 143 | 144 | } 145 | } 146 | } 147 | } 148 | } 149 | } 150 | 151 | } -------------------------------------------------------------------------------- /src/gta1tokenizer.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.script.gta1tokenizer; 29 | 30 | import std.stdio; 31 | import std.file; 32 | import std.conv; 33 | import std.traits; 34 | import std.string; 35 | 36 | 37 | public enum TokenType : ubyte { 38 | INTEGER, 39 | STRING, 40 | BRACE_OPEN, 41 | BRACE_CLOSE, 42 | BRACKET_OPEN, 43 | BRACKET_CLOSE, 44 | NEWLINE, 45 | EOF, 46 | } 47 | 48 | 49 | public union TokenValue { 50 | int integer; 51 | string str; 52 | } 53 | 54 | public struct Token { 55 | uint line; 56 | uint column; 57 | 58 | TokenType type; 59 | TokenValue value; 60 | } 61 | 62 | 63 | public final class GTA1Tokenizer { 64 | 65 | private char[] _input; 66 | private uint _index; 67 | private uint _line; 68 | private uint _column; 69 | 70 | private Token[] _tokens; 71 | 72 | 73 | this(const string fileName) { 74 | _input = cast(char[])readText(fileName); 75 | 76 | _line = 1; 77 | char[] str; 78 | while (_index < _input.length) { 79 | const char c = _input[_index]; 80 | 81 | // Comment 82 | if (c == ';') { 83 | emitToken(str); 84 | str.length = 0; 85 | while (_input[_index] != '\n') { 86 | _index++; 87 | _line++; 88 | _column = 0; 89 | } 90 | 91 | // Brackets 92 | } else if (c == '[') { 93 | emitToken(str); 94 | str.length = 0; 95 | emitToken(TokenType.BRACKET_OPEN); 96 | } else if (c == ']') { 97 | emitToken(str); 98 | str.length = 0; 99 | emitToken(TokenType.BRACKET_CLOSE); 100 | 101 | // Braces 102 | } else if (c == '(') { 103 | emitToken(str); 104 | str.length = 0; 105 | emitToken(TokenType.BRACE_OPEN); 106 | } else if (c == ')') { 107 | emitToken(str); 108 | str.length = 0; 109 | emitToken(TokenType.BRACE_CLOSE); 110 | 111 | // Separators 112 | } else if (c == ' ' || c == ',' || c == '\r' || c == '\t') { 113 | emitToken(str); 114 | str.length = 0; 115 | 116 | // Newlines 117 | if (c == '\r') { 118 | if (_input[_index + 1] == '\n') { 119 | emitToken(TokenType.NEWLINE); 120 | _line++; 121 | _column = 0; 122 | 123 | _index++; 124 | } 125 | } 126 | 127 | } else { 128 | str ~= c; 129 | 130 | } 131 | 132 | _index++; 133 | _column++; 134 | } 135 | 136 | emitToken(TokenType.EOF); 137 | } 138 | 139 | private bool isInteger(const char[] str) { 140 | if (!str.length) { 141 | return false; 142 | } 143 | 144 | foreach (char c; str) { 145 | if ((c < '0' || c > '9') && c != '-' && c != '.') { 146 | return false; 147 | } 148 | } 149 | 150 | return true; 151 | } 152 | 153 | private void emitToken(const char[] str) { 154 | if (!str.length) { 155 | return; 156 | } 157 | 158 | Token token; 159 | token.line = _line; 160 | token.column = _column - str.length; 161 | 162 | if (isInteger(str)) { 163 | token.type = TokenType.INTEGER; 164 | 165 | int pIndex = -1; 166 | foreach (int index, char c; str) { 167 | if (c == '.') { 168 | pIndex = index; 169 | break; 170 | } 171 | } 172 | 173 | // Split malformed integers into two integer tokens. 174 | if (pIndex != -1) { 175 | token.value.integer = to!int(str[0..pIndex - 1]); 176 | _tokens ~= token; 177 | 178 | token.column += pIndex; 179 | token.value.integer = to!int(str[pIndex + 1..$]); 180 | } else { 181 | token.value.integer = to!int(str); 182 | } 183 | } else { 184 | token.type = TokenType.STRING; 185 | token.value.str = to!string(str); 186 | } 187 | 188 | _tokens ~= token; 189 | } 190 | 191 | private void emitToken(const TokenType type) { 192 | Token token; 193 | token.type = type; 194 | token.line = _line; 195 | token.column = _column; 196 | _tokens ~= token; 197 | } 198 | 199 | @property public Token[] tokens() { 200 | return _tokens; 201 | } 202 | } 203 | 204 | public string getTokenTypeName(const TokenType type) { 205 | if (type == TokenType.BRACE_CLOSE) { 206 | return "closing brace"; 207 | } else if (type == TokenType.BRACE_OPEN) { 208 | return "opening brace"; 209 | } else if (type == TokenType.BRACKET_CLOSE) { 210 | return "closing bracket"; 211 | } else if (type == TokenType.BRACKET_OPEN) { 212 | return "opening bracket"; 213 | } else if (type == TokenType.INTEGER) { 214 | return "integer"; 215 | } else if (type == TokenType.STRING) { 216 | return "string"; 217 | } else if (type == TokenType.NEWLINE) { 218 | return "newline"; 219 | } else if (type == TokenType.EOF) { 220 | return "end of file"; 221 | } 222 | 223 | throw new Exception(format("No type name for token type %d.", type)); 224 | } -------------------------------------------------------------------------------- /src/game.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.game; 29 | 30 | import std.stdio; 31 | import std.path; 32 | import std.string; 33 | 34 | import game.gamestate; 35 | import game.gamestate_level; 36 | import game.strings; 37 | 38 | import game.script.missionlist; 39 | 40 | import render.renderer; 41 | 42 | import audio.audio; 43 | 44 | import util.console; 45 | import util.log; 46 | 47 | 48 | public enum GameMode : ubyte { 49 | NONE, 50 | GTA1, 51 | GTA2 52 | } 53 | 54 | public GameMode gameMode = GameMode.NONE; 55 | public bool gameDemo = false; 56 | 57 | 58 | public final class Game { 59 | 60 | private Renderer _renderer; 61 | private Audio _audio; 62 | 63 | private GameState[] _states; 64 | 65 | private MissionList _missions; 66 | 67 | private CVar _cvarMission; 68 | private CVar _cvarBaseDir; 69 | private CVar _cvarLanguage; 70 | 71 | 72 | this() { 73 | _cvarMission = CVars.get("g_mission"); 74 | _cvarBaseDir = CVars.get("g_basedir"); 75 | _cvarLanguage = CVars.get("g_language"); 76 | 77 | _renderer = new Renderer(); 78 | _audio = new Audio(); 79 | 80 | // Determine game mode. 81 | switch (toLower(_cvarBaseDir.strVal)) { 82 | case "gta1": 83 | case "uk": 84 | case "uk1961": 85 | gameMode = GameMode.GTA1; 86 | gameDemo = false; 87 | Log.write(Color.NORMAL, "Running in GTA 1 mode."); 88 | break; 89 | case "gta1demo": 90 | case "gta1demo3dfx": 91 | case "gta1demoects": 92 | gameMode = GameMode.GTA1; 93 | gameDemo = true; 94 | Log.write(Color.NORMAL, "Running in GTA 1 demo mode."); 95 | break; 96 | case "gta2": 97 | gameMode = GameMode.GTA2; 98 | gameDemo = false; 99 | Log.write(Color.NORMAL, "Running in GTA 2 mode."); 100 | break; 101 | case "gta2demo": 102 | gameMode = GameMode.GTA2; 103 | gameDemo = true; 104 | Log.write(Color.NORMAL, "Running in GTA 2 demo mode."); 105 | break; 106 | default: 107 | Log.write(Color.WARNING, "Unrecognized base directory '%s'. Defaulting to GTA 1 mode.", _cvarBaseDir.strVal); 108 | gameMode = GameMode.GTA1; 109 | gameDemo = false; 110 | break; 111 | } 112 | 113 | // Read string table. 114 | const string langFile = getLanguageFile(_cvarLanguage.strVal); 115 | Strings.load(buildPath("base", _cvarBaseDir.strVal, langFile)); 116 | 117 | // Build mission list. 118 | _missions = new MissionList(); 119 | Mission mission = _missions.getMission(cast(uint)_cvarMission.intVal); 120 | 121 | _states ~= new GameStateLevel(this, mission); 122 | } 123 | 124 | public void destroy() { 125 | _renderer.destroy(); 126 | } 127 | 128 | public void input(ubyte* keys) { 129 | foreach_reverse (GameState state; _states) { 130 | if (state.isEnabled) { 131 | state.input(keys); 132 | break; 133 | } 134 | } 135 | } 136 | 137 | public void update(const double delta) { 138 | foreach (GameState state; _states) { 139 | if (state.isEnabled) { 140 | state.update(delta); 141 | } 142 | } 143 | } 144 | 145 | public void render(const double lerp) { 146 | _renderer.start(); 147 | _renderer.clear(); 148 | 149 | foreach (GameState state; _states) { 150 | if (state.isEnabled) { 151 | state.render(lerp); 152 | } 153 | } 154 | 155 | _renderer.end(); 156 | } 157 | 158 | private string getLanguageFile(const string code) { 159 | if (gameMode == GameMode.GTA1 && _cvarBaseDir.strVal == "uk") { 160 | switch (_cvarLanguage.strVal) { 161 | case "en": return "enguk.fxt"; 162 | case "de": return "geruk.fxt"; 163 | case "it": return "itauk.fxt"; 164 | case "fr": return "freuk.fxt"; 165 | default: 166 | throw new Exception(format("Unknown GTA 1 language code '%s'.", code)); 167 | } 168 | 169 | } else if (gameMode == GameMode.GTA1 && _cvarBaseDir.strVal == "uk1961") { 170 | return "enguke.fxt"; 171 | 172 | } else if (gameMode == GameMode.GTA1) { 173 | switch (_cvarLanguage.strVal) { 174 | case "en": return "english.fxt"; 175 | case "de": return "german.fxt"; 176 | case "it": return "italian.fxt"; 177 | case "fr": return "french.fxt"; 178 | default: 179 | throw new Exception(format("Unknown GTA 1 language code '%s'.", code)); 180 | } 181 | 182 | } else if (gameMode == GameMode.GTA2) { 183 | switch (_cvarLanguage.strVal) { 184 | case "en": return "e.gxt"; 185 | case "de": return "g.gxt"; 186 | case "it": return "i.gxt"; 187 | case "fr": return "f.gxt"; 188 | case "es": return "s.gxt"; 189 | case "ja": return "j.gxt"; 190 | default: 191 | throw new Exception(format("Unknown GTA 2 language code '%s'.", code)); 192 | } 193 | } 194 | 195 | throw new Exception("Invalid gamemode."); 196 | } 197 | 198 | @property public Renderer renderer() { 199 | return _renderer; 200 | } 201 | 202 | @property public Audio audio() { 203 | return _audio; 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /src/vehicle.d: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Dennis Meuwissen 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | License does not apply to code and information found in the ./info directory. 26 | */ 27 | 28 | module game.entities.vehicle; 29 | 30 | import std.stdio; 31 | 32 | import game.entities.entity; 33 | 34 | import game.style.style; 35 | 36 | import util.color; 37 | 38 | 39 | public alias ubyte VehicleModel; 40 | 41 | 42 | public struct VehicleDoor { 43 | short relX; 44 | short relY; 45 | short entityTypeIndex; 46 | short deltaIndex; 47 | } 48 | 49 | public enum VehicleKind : ubyte { 50 | BUS = 0, 51 | TRUCK_CAB = 1, 52 | TRUCK_TRAILER = 2, 53 | MOTORBIKE = 3, 54 | CAR = 4, 55 | TRAIN = 8, 56 | UNKNOWN9 = 9, 57 | UNKNOWN13 = 13, 58 | UNKNOWN14 = 14, 59 | GTA2 = 255 60 | } 61 | 62 | public enum VehicleFlags : ushort { 63 | IS_CONVERTIBLE = 0x1, 64 | SOUND_FASTCHANGE = 0x2, 65 | CANNOT_JUMP_OVER = 0x4, 66 | HAS_EMERGENCY_LIGHTS = 0x8, 67 | HAS_ROOF_LIGHTS = 0x10, 68 | ARTIC_CAB = 0x20, 69 | ARTIC_TRAILER = 0x40, 70 | HAS_HIRE_LIGHTS = 0x80, 71 | HAS_ROOF_DECAL = 0x100, 72 | HAS_REAR_EMERGENCY_LIGHTS = 0x200, 73 | CAN_CRUSH_CARS = 0x400, 74 | HAS_POPUP_HEADLIGHTS = 0x800, 75 | TURBO = 0x1000, 76 | RECYCLE = 0x2000 77 | } 78 | 79 | public struct VehicleType { 80 | string name; 81 | 82 | VehicleKind kind; 83 | VehicleFlags flags; 84 | VehicleModel model; 85 | SpriteFrameIndex sprite; 86 | SpriteFrameIndex spriteCount; 87 | SpriteFrameIndex wreckSprite; 88 | 89 | ushort width; 90 | ushort height; 91 | ushort depth; 92 | 93 | HSL[] remapsHSL; 94 | ubyte[] remaps; 95 | 96 | uint[] value; 97 | 98 | ubyte passengerCount; 99 | ubyte rating; 100 | 101 | float damageFactor; 102 | 103 | ubyte engineType; 104 | ubyte radioType; 105 | ubyte hornType; 106 | 107 | ubyte soundFunction; 108 | 109 | VehiclePhysicsGTA1 physics1; 110 | VehiclePhysicsGTA2 physics2; 111 | 112 | VehicleDoor[] doors; 113 | } 114 | 115 | public struct VehiclePhysicsGTA1 { 116 | 117 | // Vehicle speed is clamped to these values. 118 | short speedMax; 119 | short speedMin; 120 | 121 | // Generic handling attributes. ??? 122 | short acceleration; 123 | short braking; 124 | short grip; 125 | short handling; 126 | ubyte turning; 127 | 128 | // Total weight of this vehicle. Units? 129 | ushort weight; 130 | 131 | // Center of mass, offset from center of vehicle, for torque. 132 | byte centerMassX; 133 | byte centerMassY; 134 | 135 | // Inertia, usually calculated from mass and dimensions. 136 | int momentInertia; 137 | 138 | // Mass of this vehicle. 139 | float mass; 140 | 141 | // Thrust output in 1st gear. Are there other gears? 142 | float thrustGear1; 143 | 144 | // How much friction tires have with the ground in both axes? Probably a multiplier of some base friction amount. 145 | float tireAdhesionX; 146 | float tireAdhesionY; 147 | 148 | // The friction applied by various braking systems. 149 | float handBrakeFriction; 150 | float footBrakeFriction; 151 | 152 | // How is braking power divided between forward\backward wheels? 1.0 is front, 0.0 is back? 153 | float frontBrakeBias; 154 | 155 | // How quick turning is, how sharp corners are made. 156 | short turnRatio; 157 | 158 | // Y offset from the center of the vehicle to where the wheels are. 159 | short driveWheelOffset; 160 | short steeringWheelOffset; 161 | 162 | float backEndSlide; 163 | float handBrakeSlide; 164 | } 165 | 166 | public struct VehiclePhysicsGTA2 { 167 | byte steeringWheelOffset; 168 | byte driveWheelOffset; 169 | byte frontWindowOffset; 170 | byte rearWindowOffset; 171 | 172 | float mass; 173 | float frontDriveBias; 174 | float frontMassBias; 175 | float brakeFriction; 176 | float turnIn; 177 | float turnRatio; 178 | float rearStability; 179 | float handbrakeSlide; 180 | float thrust; 181 | float maxSpeed; 182 | float antiStrength; 183 | float skidThreshold; 184 | float gear1Multi; 185 | float gear2Multi; 186 | float gear3Multi; 187 | float gear2Speed; 188 | float gear3Speed; 189 | } 190 | 191 | 192 | public final class EntityVehicle : Entity { 193 | 194 | private VehicleType* _vehicleType; 195 | private uint _deltaMask; 196 | 197 | 198 | this(VehicleType* vehicle) { 199 | super(); 200 | 201 | _width = vehicle.width; 202 | _height = vehicle.height; 203 | _depth = vehicle.depth; 204 | _weight = 0; 205 | _baseFrame = vehicle.sprite; 206 | _frameCount = 1; 207 | 208 | _vehicleType = vehicle; 209 | } 210 | 211 | @property public VehicleModel model() { 212 | return _vehicleType.model; 213 | } 214 | 215 | public void enableDelta(const uint index) { 216 | _deltaMask |= (1 << index); 217 | _spriteTexture = null; 218 | } 219 | 220 | public void disableDelta(const uint index) { 221 | _deltaMask &= ~(1 << index); 222 | _spriteTexture = null; 223 | } 224 | 225 | public void enableDeltaMask(const uint mask) { 226 | _deltaMask |= mask; 227 | _spriteTexture = null; 228 | } 229 | 230 | public void disableDeltaMask(const uint mask) { 231 | _deltaMask &= ~mask; 232 | _spriteTexture = null; 233 | } 234 | 235 | override @property public uint deltaMask() { 236 | return _deltaMask; 237 | } 238 | 239 | public void setVehicleRemap(const ubyte index) { 240 | _remap = _vehicleType.remaps[index]; 241 | } 242 | 243 | override @property public PaletteIndex palette(const SpriteFrame frame) { 244 | if (_vehicleType.remaps.length == 0 || _remap == 0xFF || _remap == 0) { 245 | return frame.palette; 246 | } 247 | return PaletteIndex(PaletteType.VEHICLE, _remap); 248 | } 249 | 250 | } 251 | --------------------------------------------------------------------------------