├── .gitignore ├── LICENSE ├── README.md ├── bin ├── data │ ├── actors │ │ ├── actor.json │ │ └── grampire.png │ ├── audio │ │ ├── asylum.ogg │ │ ├── bighead-death.wav │ │ ├── boss_dead_find_exit.ogg │ │ ├── circus_idea.ogg │ │ ├── death.wav │ │ ├── door.wav │ │ ├── grampire-death.wav │ │ ├── grampire-hurt.wav │ │ ├── highlight.wav │ │ ├── hit.wav │ │ ├── hooded_priest-death.wav │ │ ├── hurt.wav │ │ ├── ingame1.ogg │ │ ├── key.wav │ │ ├── machinegun.wav │ │ ├── medkit.wav │ │ ├── menuback.wav │ │ ├── menuselect.wav │ │ ├── monsters.ogg │ │ ├── notify.wav │ │ ├── pistol.wav │ │ ├── rain.ogg │ │ ├── raven-death.wav │ │ ├── raven.wav │ │ ├── reload.wav │ │ ├── rust_ambient.ogg │ │ ├── scroll.wav │ │ ├── shotgun.wav │ │ ├── skeleton-death.wav │ │ ├── skeleton-old1.wav │ │ ├── squish.wav │ │ ├── thunder.wav │ │ ├── uzis.wav │ │ └── zombie-death.wav │ ├── background.png │ ├── effects │ │ ├── fogLayer.png │ │ ├── particle.png │ │ └── rain.png │ ├── hud │ │ ├── arrow.png │ │ ├── background2.png │ │ ├── hitpoint.png │ │ ├── hp.png │ │ ├── life.png │ │ ├── lifebar.png │ │ ├── portrait_bernie.png │ │ ├── portrait_fetusmaximus.png │ │ ├── portrait_grampire.png │ │ ├── portrait_maskkid.png │ │ └── portrait_steven.png │ ├── logo.png │ ├── maps.json │ ├── maps │ │ ├── church.tmx │ │ ├── circusTileset.png │ │ ├── graveyardTileset.json │ │ ├── graveyardTileset.png │ │ ├── modern.tmx │ │ ├── modernTileset.json │ │ ├── modernTileset.png │ │ ├── objectSet1.json │ │ ├── objectSet1.png │ │ ├── objects.json │ │ ├── objects.png │ │ ├── spawns.png │ │ ├── theGraveyard.tmx │ │ └── void_cavern.tmx │ ├── particle.png │ ├── qorpse.png │ ├── shaders │ │ ├── basic.fp │ │ ├── basic.json │ │ └── basic.vp │ ├── things │ │ ├── bernie.json │ │ ├── bernie.png │ │ ├── bighead.json │ │ ├── bighead.png │ │ ├── bloodpool1.png │ │ ├── bloodpool2.png │ │ ├── bullet.png │ │ ├── fetus_maximus.png │ │ ├── ghost.json │ │ ├── ghost.png │ │ ├── gib1.png │ │ ├── gib2.png │ │ ├── gib3.png │ │ ├── hooded_priest.json │ │ ├── hooded_priest.png │ │ ├── mask_kid.json │ │ ├── mask_kid.png │ │ ├── meat_fly.json │ │ ├── meat_fly.png │ │ ├── raven.json │ │ ├── raven.png │ │ ├── scarecrow.png │ │ ├── severed_head.json │ │ ├── severed_head.png │ │ ├── skeleton.json │ │ ├── skeleton.png │ │ ├── zombie.json │ │ └── zombie.png │ ├── vendor │ │ └── ionicons │ │ │ ├── .gitignore │ │ │ ├── LICENSE │ │ │ └── png │ │ │ └── 512 │ │ │ ├── load-a.png │ │ │ ├── load-b.png │ │ │ ├── load-c.png │ │ │ └── load-d.png │ └── weapons.json ├── profiles │ ├── default.json │ ├── gamepad.json_ │ └── keyboard.json_ ├── qorpse.desktop ├── settings.json └── settings.schema.json ├── premake4.lua ├── qorpse.tasks ├── sg.json └── src ├── Character.cpp ├── Character.h ├── CharacterInterface.cpp ├── CharacterInterface.h ├── HUD.cpp ├── HUD.h ├── Info.h ├── Main.cpp ├── MenuState.cpp ├── MenuState.h ├── QorpseState.cpp ├── QorpseState.h ├── TextScroller.cpp ├── TextScroller.h ├── Thing.cpp ├── Thing.h ├── Weapon.h ├── World.cpp └── World.h /.gitignore: -------------------------------------------------------------------------------- 1 | bin/qorpse 2 | bin/qorpse_dist 3 | bin/*.log 4 | obj/ 5 | src/kit 6 | src/Qor 7 | src/shaders 8 | src/scripts 9 | __pycache__/ 10 | *.make 11 | Makefile 12 | 13 | # Compiled Object files 14 | *.slo 15 | *.lo 16 | *.o 17 | *.obj 18 | 19 | # Compiled Dynamic libraries 20 | *.so 21 | *.dylib 22 | *.dll 23 | 24 | # Compiled Static libraries 25 | *.lai 26 | *.la 27 | *.a 28 | *.lib 29 | 30 | # Executables 31 | *.exe 32 | *.out 33 | *.app 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Grady O'Connell 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Qorpse 2 | ====== 3 | 4 | Retro horror shooter written in C++11. 5 | 6 | [![Video 1](http://img.youtube.com/vi/5Fyw-sdIT_8/0.jpg)](http://www.youtube.com/watch?v=5Fyw-sdIT_8) 7 | 8 | [![Video 2](http://img.youtube.com/vi/3S3Bc8S_yjg/0.jpg)](https://www.youtube.com/watch?v=3S3Bc8S_yjg) 9 | 10 | ## Credits ## 11 | 12 | ### Programming 13 | - [Grady O'Connell](http://github.com/flipcoder) (flipcoder@gmail.com) 14 | 15 | ### Graphics 16 | - [Mark "Alfred" McDaniel](http://github.com/AlfredAnonymous) (alfred523@gmail.com) 17 | 18 | ### Music & Sound 19 | - [Grady O'Connell](http://github.com/flipcoder) (flipcoder@gmail.com) 20 | 21 | -------------------------------------------------------------------------------- /bin/data/actors/actor.json: -------------------------------------------------------------------------------- 1 | { 2 | "type" : "sprite", 3 | "size" : [ 16, 16 ], 4 | "tile-size" : [ 16, 16 ], 5 | "origin" : [ 0.5, 0.75 ], 6 | "mask" : [ 0.25, 0.5, 0.75, 1.0 ], 7 | "animation" : { 8 | "speed" : 10.0, 9 | "frames" : { 10 | "down": { 11 | "stand": [0], 12 | "walk": [0,1,0,2] 13 | }, 14 | "up": { 15 | "stand": [3], 16 | "walk": [3,4,3,5] 17 | }, 18 | "left": { 19 | "stand": [6], 20 | "walk": [6,7,6,8] 21 | }, 22 | "right": { 23 | "stand": ["hflip",6], 24 | "walk": ["hflip",6,7,6,8] 25 | }, 26 | "death": ["once",9,10,11] 27 | } 28 | }, 29 | "skins": [ 30 | "grampire" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /bin/data/actors/grampire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/actors/grampire.png -------------------------------------------------------------------------------- /bin/data/audio/asylum.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/asylum.ogg -------------------------------------------------------------------------------- /bin/data/audio/bighead-death.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/bighead-death.wav -------------------------------------------------------------------------------- /bin/data/audio/boss_dead_find_exit.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/boss_dead_find_exit.ogg -------------------------------------------------------------------------------- /bin/data/audio/circus_idea.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/circus_idea.ogg -------------------------------------------------------------------------------- /bin/data/audio/death.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/death.wav -------------------------------------------------------------------------------- /bin/data/audio/door.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/door.wav -------------------------------------------------------------------------------- /bin/data/audio/grampire-death.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/grampire-death.wav -------------------------------------------------------------------------------- /bin/data/audio/grampire-hurt.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/grampire-hurt.wav -------------------------------------------------------------------------------- /bin/data/audio/highlight.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/highlight.wav -------------------------------------------------------------------------------- /bin/data/audio/hit.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/hit.wav -------------------------------------------------------------------------------- /bin/data/audio/hooded_priest-death.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/hooded_priest-death.wav -------------------------------------------------------------------------------- /bin/data/audio/hurt.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/hurt.wav -------------------------------------------------------------------------------- /bin/data/audio/ingame1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/ingame1.ogg -------------------------------------------------------------------------------- /bin/data/audio/key.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/key.wav -------------------------------------------------------------------------------- /bin/data/audio/machinegun.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/machinegun.wav -------------------------------------------------------------------------------- /bin/data/audio/medkit.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/medkit.wav -------------------------------------------------------------------------------- /bin/data/audio/menuback.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/menuback.wav -------------------------------------------------------------------------------- /bin/data/audio/menuselect.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/menuselect.wav -------------------------------------------------------------------------------- /bin/data/audio/monsters.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/monsters.ogg -------------------------------------------------------------------------------- /bin/data/audio/notify.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/notify.wav -------------------------------------------------------------------------------- /bin/data/audio/pistol.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/pistol.wav -------------------------------------------------------------------------------- /bin/data/audio/rain.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/rain.ogg -------------------------------------------------------------------------------- /bin/data/audio/raven-death.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/raven-death.wav -------------------------------------------------------------------------------- /bin/data/audio/raven.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/raven.wav -------------------------------------------------------------------------------- /bin/data/audio/reload.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/reload.wav -------------------------------------------------------------------------------- /bin/data/audio/rust_ambient.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/rust_ambient.ogg -------------------------------------------------------------------------------- /bin/data/audio/scroll.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/scroll.wav -------------------------------------------------------------------------------- /bin/data/audio/shotgun.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/shotgun.wav -------------------------------------------------------------------------------- /bin/data/audio/skeleton-death.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/skeleton-death.wav -------------------------------------------------------------------------------- /bin/data/audio/skeleton-old1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/skeleton-old1.wav -------------------------------------------------------------------------------- /bin/data/audio/squish.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/squish.wav -------------------------------------------------------------------------------- /bin/data/audio/thunder.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/thunder.wav -------------------------------------------------------------------------------- /bin/data/audio/uzis.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/uzis.wav -------------------------------------------------------------------------------- /bin/data/audio/zombie-death.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/audio/zombie-death.wav -------------------------------------------------------------------------------- /bin/data/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/background.png -------------------------------------------------------------------------------- /bin/data/effects/fogLayer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/effects/fogLayer.png -------------------------------------------------------------------------------- /bin/data/effects/particle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/effects/particle.png -------------------------------------------------------------------------------- /bin/data/effects/rain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/effects/rain.png -------------------------------------------------------------------------------- /bin/data/hud/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/hud/arrow.png -------------------------------------------------------------------------------- /bin/data/hud/background2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/hud/background2.png -------------------------------------------------------------------------------- /bin/data/hud/hitpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/hud/hitpoint.png -------------------------------------------------------------------------------- /bin/data/hud/hp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/hud/hp.png -------------------------------------------------------------------------------- /bin/data/hud/life.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/hud/life.png -------------------------------------------------------------------------------- /bin/data/hud/lifebar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/hud/lifebar.png -------------------------------------------------------------------------------- /bin/data/hud/portrait_bernie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/hud/portrait_bernie.png -------------------------------------------------------------------------------- /bin/data/hud/portrait_fetusmaximus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/hud/portrait_fetusmaximus.png -------------------------------------------------------------------------------- /bin/data/hud/portrait_grampire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/hud/portrait_grampire.png -------------------------------------------------------------------------------- /bin/data/hud/portrait_maskkid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/hud/portrait_maskkid.png -------------------------------------------------------------------------------- /bin/data/hud/portrait_steven.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/hud/portrait_steven.png -------------------------------------------------------------------------------- /bin/data/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/logo.png -------------------------------------------------------------------------------- /bin/data/maps.json: -------------------------------------------------------------------------------- 1 | { 2 | "maps": [ 3 | "theGraveyard", 4 | "modern", 5 | "church" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /bin/data/maps/church.tmx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 12 | 9,9,9,9,9,9,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,9,9,9,9,9, 13 | 9,9,9,9,9,9,21,21,21,21,21,21,21,21,21,42,21,21,21,21,21,21,21,21,21,9,9,9,9,9, 14 | 9,9,9,9,9,9,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,9,9,9,9,9, 15 | 9,9,9,9,9,9,21,21,21,21,21,21,21,21,183,179,185,21,21,21,21,21,21,21,21,9,9,9,9,9, 16 | 9,9,9,9,9,9,21,21,21,21,21,21,21,21,180,186,181,21,21,21,21,21,21,21,21,9,9,9,9,9, 17 | 9,9,9,9,9,9,21,21,21,21,21,21,21,21,180,186,181,21,21,21,21,21,21,21,21,9,9,9,9,9, 18 | 9,9,9,9,9,9,21,21,21,21,21,21,21,21,180,186,181,21,21,21,21,21,21,21,21,9,9,9,9,9, 19 | 9,9,9,9,9,9,21,21,21,21,21,21,21,21,180,186,181,21,21,21,21,21,21,21,21,9,9,9,9,9, 20 | 9,9,9,9,9,9,21,21,21,21,21,21,21,21,180,186,181,21,21,21,21,21,21,21,21,9,9,9,9,9, 21 | 9,9,9,9,9,9,21,21,21,21,21,21,21,21,180,186,181,21,21,21,21,21,21,21,21,9,9,9,9,9, 22 | 9,9,9,9,9,9,21,21,21,21,21,21,21,21,180,186,181,21,21,21,21,21,21,21,21,9,9,9,9,9, 23 | 9,9,9,9,9,9,21,21,21,21,21,21,21,21,184,178,182,21,21,21,21,21,21,21,21,9,9,9,9,9, 24 | 9,9,9,9,9,9,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,9,9,9,9,9, 25 | 9,9,9,9,9,9,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,9,9,9,9,9, 26 | 9,9,9,9,8,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,9,9,9,9,9, 27 | 9,9,9,9,8,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,9,9,9,9,9, 28 | 9,9,9,9,8,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,9,9,9,9,9, 29 | 9,9,9,9,8,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,9,9,9,9,9, 30 | 9,9,9,9,8,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,9,9,9,9,9, 31 | 9,9,9,9,8,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,9,9,9,9,9, 32 | 9,9,9,9,8,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,9,9,9,9,9, 33 | 9,9,9,9,8,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,9,9,9,9,9, 34 | 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 35 | 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 36 | 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 37 | 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 38 | 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 39 | 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 40 | 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 41 | 42 | 43 | 44 | 45 | 0,0,0,0,0,0,23,23,24,23,23,23,24,23,23,23,23,23,24,23,23,23,24,23,23,0,0,0,0,0, 46 | 0,0,0,0,0,0,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,0,0,0,0,0, 47 | 0,0,0,0,0,0,19,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,18,0,0,0,0,0, 48 | 0,0,0,0,0,0,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,0,0,0,0,0, 49 | 76,79,79,79,78,0,19,0,213,214,0,0,213,214,0,0,0,213,214,0,0,213,214,0,18,0,0,0,0,0, 50 | 91,62,60,63,90,0,19,0,213,214,0,0,213,214,0,0,0,213,214,0,0,213,214,0,18,0,0,0,0,0, 51 | 91,64,61,74,90,0,19,0,213,214,0,0,213,214,0,0,0,213,214,0,0,213,214,0,18,0,0,0,0,0, 52 | 77,80,80,80,75,0,19,0,213,214,0,0,213,214,0,0,0,213,214,0,0,213,214,0,18,0,0,0,0,0, 53 | 0,0,0,0,0,0,19,0,213,214,0,0,213,214,0,0,0,213,214,0,0,213,214,0,18,0,0,0,0,0, 54 | 3,2,2,2,5,0,19,0,213,214,0,0,213,214,0,0,0,213,214,0,0,213,214,0,18,0,0,0,0,0, 55 | 1,0,0,0,1,0,19,0,213,214,0,0,213,214,0,0,0,213,214,0,0,213,214,0,18,0,0,0,0,0, 56 | 4,2,2,2,6,0,19,0,213,214,0,0,213,214,0,0,0,213,214,0,0,213,214,0,18,0,0,0,0,0, 57 | 0,0,0,0,0,0,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,0,0,0,0,0, 58 | 0,0,0,0,0,0,19,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,18,0,0,0,0,0, 59 | 0,0,0,0,76,79,17,17,17,17,17,17,17,22,17,20,17,22,17,17,17,17,17,17,17,78,0,0,0,0, 60 | 0,0,0,58,91,134,135,134,135,134,135,134,135,134,135,134,135,134,135,134,135,134,135,134,135,90,0,0,0,0, 61 | 0,0,0,58,91,150,151,150,151,150,151,150,151,150,151,150,151,150,151,150,151,150,151,150,151,90,0,0,0,0, 62 | 0,0,0,58,91,134,135,134,135,134,135,134,135,134,135,134,135,134,135,134,135,134,135,134,135,90,0,0,0,0, 63 | 0,0,0,58,91,150,151,150,151,150,151,150,151,150,151,150,151,150,151,150,151,150,151,150,151,90,0,0,0,0, 64 | 0,0,0,58,91,134,135,134,135,134,135,134,135,134,135,134,135,134,135,134,135,134,135,134,135,90,0,0,0,0, 65 | 0,0,0,58,91,150,151,150,151,150,151,150,151,150,151,150,151,150,151,150,151,150,151,150,151,90,0,0,0,0, 66 | 0,0,0,58,91,134,135,134,135,134,135,134,135,134,135,134,135,134,135,134,135,134,135,134,135,90,0,0,0,0, 67 | 0,0,0,58,91,150,151,150,151,150,151,150,151,150,151,150,151,150,151,150,151,150,151,150,151,90,0,0,0,0, 68 | 0,0,0,0,77,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,75,0,0,0,0, 69 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 70 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 71 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 72 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 73 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 74 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 75 | 76 | 77 | 78 | 79 | 0,0,0,0,0,0,19,0,0,0,0,0,0,0,0,0,0,0,0,0,26,0,0,0,18,0,0,0,0,0, 80 | 0,0,0,0,0,0,0,0,0,0,0,0,0,29,29,29,29,29,0,0,0,0,0,0,0,0,0,0,0,0, 81 | 0,0,0,0,0,0,0,0,0,0,0,0,0,45,45,45,45,45,0,0,0,0,0,0,0,0,0,0,0,0, 82 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 83 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 84 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 85 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 86 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 87 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 88 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 89 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 90 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 91 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 92 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 93 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 94 | 0,0,0,0,140,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,0,0,0,0, 95 | 0,0,0,0,156,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,157,0,0,0,0, 96 | 0,0,0,0,140,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,0,0,0,0, 97 | 0,0,0,0,156,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,157,0,0,0,0, 98 | 0,0,0,0,140,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,0,0,0,0, 99 | 0,0,0,0,156,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,157,0,0,0,0, 100 | 0,0,0,0,140,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,0,0,0,0, 101 | 0,0,0,0,156,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,157,0,0,0,0, 102 | 0,0,0,0,0,166,167,166,167,166,167,166,167,166,167,166,167,166,167,166,167,166,167,166,167,0,0,0,0,0, 103 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 104 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 105 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 106 | 49,49,49,49,49,49,49,49,49,49,49,49,49,0,0,0,0,0,49,49,49,49,49,49,49,49,49,49,49,49, 107 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 108 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /bin/data/maps/circusTileset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/maps/circusTileset.png -------------------------------------------------------------------------------- /bin/data/maps/graveyardTileset.json: -------------------------------------------------------------------------------- 1 | { 2 | "1,1": { 3 | "sidewall": "right" 4 | }, 5 | "2,1": { 6 | "sidewall": "" 7 | }, 8 | "1,3": { 9 | "sidewall": "" 10 | }, 11 | "1,4": { 12 | "sidewall": "right" 13 | }, 14 | "3,1": { 15 | "door": "" 16 | }, 17 | "4,7": { 18 | "trap": "" 19 | }, 20 | "5,7": { 21 | "trap": "" 22 | }, 23 | "5,1": { 24 | "window": "" 25 | }, 26 | "7,1": { 27 | "window": "" 28 | }, 29 | "3,1": { 30 | "door": "" 31 | }, 32 | "8,3": { 33 | "sidewall": "", 34 | "door": "" 35 | }, 36 | "0,14": { 37 | "sidewall":"" 38 | }, 39 | "1,14": { 40 | "sidewall": "right" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /bin/data/maps/graveyardTileset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/maps/graveyardTileset.png -------------------------------------------------------------------------------- /bin/data/maps/modernTileset.json: -------------------------------------------------------------------------------- 1 | { 2 | "3,3": { 3 | "door": "" 4 | }, 5 | "3,7": { 6 | "window": "" 7 | }, 8 | "4,2": { 9 | "sidewall": "" 10 | }, 11 | "4,7": { 12 | "window": "" 13 | }, 14 | "4,13": { 15 | "door": "" 16 | }, 17 | "5,7": { 18 | "window": "" 19 | }, 20 | "6,3": { 21 | "stairs": "" 22 | }, 23 | "7,3": { 24 | "stairs": "" 25 | }, 26 | "8,5": { 27 | "stairs": "" 28 | }, 29 | "9,2": { 30 | "sidewall": "" 31 | }, 32 | "9,6": { 33 | "door": "" 34 | }, 35 | "10,6": { 36 | "stairs": "" 37 | }, 38 | "12,0": { 39 | "door": "" 40 | }, 41 | "12,5": { 42 | "window": "" 43 | }, 44 | "12,6": { 45 | "window": "" 46 | }, 47 | "13,5": { 48 | "window": "" 49 | }, 50 | "13,6": { 51 | "window": "" 52 | }, 53 | "14,5": { 54 | "window": "" 55 | }, 56 | "14,6": { 57 | "window": "" 58 | }, 59 | "15,5": { 60 | "window": "" 61 | }, 62 | "15,6": { 63 | "window": "" 64 | }, 65 | "16,5": { 66 | "window": "" 67 | }, 68 | "16,6": { 69 | "window": "" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /bin/data/maps/modernTileset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/maps/modernTileset.png -------------------------------------------------------------------------------- /bin/data/maps/objectSet1.json: -------------------------------------------------------------------------------- 1 | { 2 | "6,5": { 3 | "breakable": "" 4 | }, 5 | "6,6": { 6 | "breakable": "" 7 | }, 8 | "7,5": { 9 | "breakable": "", 10 | "explode": "" 11 | }, 12 | "7,6": { 13 | "breakable": "", 14 | "explode": "" 15 | }, 16 | "8,5": { 17 | "name": "lever" 18 | }, 19 | "8,6": { 20 | "name": "lever" 21 | }, 22 | "12,4": { 23 | "breakable": "" 24 | }, 25 | "13,5": { 26 | "breakable": "" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /bin/data/maps/objectSet1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/maps/objectSet1.png -------------------------------------------------------------------------------- /bin/data/maps/objects.json: -------------------------------------------------------------------------------- 1 | { 2 | "0,0": { 3 | "name": "player_start" 4 | }, 5 | "0,1": { 6 | "name": "medkit" 7 | }, 8 | "0,5": { 9 | "name": "scarecrow" 10 | }, 11 | "0,6": { 12 | "name": "bernie" 13 | }, 14 | "0,7": { 15 | "name": "fetus_maximus" 16 | }, 17 | "1,2": { 18 | "name": "player_start_2" 19 | }, 20 | "1,2":{ 21 | "name": "tomb" 22 | }, 23 | "1,5": { 24 | "name": "gorecrow" 25 | }, 26 | "1,6": { 27 | "name": "zombie" 28 | }, 29 | "1,7": { 30 | "name": "ghost" 31 | }, 32 | "2,3": { 33 | "name": "comic" 34 | }, 35 | "2,6": { 36 | "name": "grampire" 37 | }, 38 | "2,7": { 39 | "name": "hooded_priest" 40 | }, 41 | "3,0": { 42 | "name": "random_weapon_spawn" 43 | }, 44 | "3,6": { 45 | "name": "mask_kid" 46 | }, 47 | "3,7": { 48 | "name": "raven" 49 | }, 50 | "4,0": { 51 | "name": "script_trigger" 52 | }, 53 | "4,6": { 54 | "name": "severed_head" 55 | }, 56 | "4,7": { 57 | "name": "bighead" 58 | }, 59 | "5,6": { 60 | "name": "skeleton" 61 | }, 62 | "5,7": { 63 | "name": "meatfly" 64 | }, 65 | "6,0": { 66 | "name": "key" 67 | }, 68 | "6,1": { 69 | "name": "lock" 70 | }, 71 | "7,0": { 72 | "name": "rifle" 73 | }, 74 | "7,1": { 75 | "name": "grenade_launcher" 76 | }, 77 | "7,2": { 78 | "name": "flamethrower" 79 | }, 80 | "7,3": { 81 | "name": "shotgun" 82 | }, 83 | "7,4": { 84 | "name": "bazooka" 85 | }, 86 | "7,5": { 87 | "name": "uzis" 88 | }, 89 | "7,6": { 90 | "name": "minigun" 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /bin/data/maps/objects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/maps/objects.png -------------------------------------------------------------------------------- /bin/data/maps/spawns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/maps/spawns.png -------------------------------------------------------------------------------- /bin/data/particle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/particle.png -------------------------------------------------------------------------------- /bin/data/qorpse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/qorpse.png -------------------------------------------------------------------------------- /bin/data/shaders/basic.fp: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | /*varying vec3 VertexPosition;*/ 4 | varying vec3 Position; 5 | varying vec2 Wrap; 6 | /*varying vec2 Normal;*/ 7 | 8 | uniform sampler2D Texture; 9 | uniform vec4 Ambient = vec4(1.0, 1.0, 1.0, 1.0); 10 | /*uniform int Flags = 0;*/ 11 | 12 | /*#define FLAG_FOG 0x1*/ 13 | 14 | // This color key stuff could be done on the CPU, and using a separate tex 15 | /*uniform vec4 ColorKeyLow;*/ 16 | /*uniform vec4 ColorKeyHigh;*/ 17 | /*uniform vec4 ColorReplaceLow;*/ 18 | /*uniform vec4 ColorReplaceHigh;*/ 19 | 20 | #define M_PI 3.1415926535897932384626433832795 21 | #define M_TAU (M_PI * 2.0) 22 | 23 | bool floatcmp(float a, float b, float e) 24 | { 25 | return abs(a-b) < e; 26 | } 27 | 28 | bool colorcmp(vec4 a, vec4 b, float t) 29 | { 30 | return floatcmp(a.r,b.r,t) && 31 | floatcmp(a.g,b.g,t) && 32 | floatcmp(a.b,b.b,t); 33 | } 34 | 35 | vec4 grayscale(vec4 c) 36 | { 37 | float v = (c.r + c.g + c.b) / 3.0; 38 | return vec4(v,v,v, c.a); 39 | } 40 | 41 | vec4 evil(vec4 c) 42 | { 43 | if(colorcmp(c, vec4(1.0, 0.0, 0.0, 1.0), 0.1)) 44 | return c; 45 | return grayscale(c); 46 | } 47 | 48 | float avg(vec3 c) 49 | { 50 | return (c.r + c.g + c.b) / 3.0; 51 | } 52 | 53 | float saturate(float v) 54 | { 55 | return clamp(v, 0.0, 1.0); 56 | } 57 | 58 | vec3 saturate(vec3 v) 59 | { 60 | vec3 r; 61 | r.x = saturate(v.x); 62 | r.y = saturate(v.y); 63 | r.z = saturate(v.z); 64 | return r; 65 | } 66 | 67 | void main() 68 | { 69 | vec4 color = texture2D(Texture, Wrap); 70 | float e = 0.1; // threshold 71 | if(floatcmp(color.r, 1.0, e) && 72 | floatcmp(color.g, 0.0, e) && 73 | floatcmp(color.b, 1.0, e)) 74 | { 75 | discard; 76 | } 77 | if(floatcmp(color.a, 0.0, e)) { 78 | discard; 79 | } 80 | 81 | float dist = length(Position.xy); 82 | /*if((Flags & FLAG_FOG) != 0)*/ 83 | /*{*/ 84 | /*float fog = 1.0f - sqrt(dist * dist * dist) * 0.6f;*/ 85 | /*gl_FragColor = vec4(saturate(vec3(color.xyz * Ambient.xyz * fog)), color.a * Ambient.a);*/ 86 | /*}*/ 87 | /*else*/ 88 | /*{*/ 89 | gl_FragColor = vec4(saturate(vec3(color.xyz * Ambient.xyz)), color.a * Ambient.a); 90 | /*}*/ 91 | /*gl_FragColor = vec4(saturate(vec3(color * vec4(vec3(1.5),1.0) * fog)), 1.0);*/ 92 | /*gl_FragColor = mix(color, evil(color), 1.0-fog) * LightBrightness;*/ 93 | } 94 | 95 | -------------------------------------------------------------------------------- /bin/data/shaders/basic.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "shader" 3 | } 4 | -------------------------------------------------------------------------------- /bin/data/shaders/basic.vp: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | attribute vec3 VertexPosition; 4 | attribute vec2 VertexWrap; 5 | 6 | varying vec3 Position; 7 | varying vec2 Wrap; 8 | 9 | uniform mat4 ModelViewProjection; 10 | uniform mat4 ModelView; 11 | uniform mat4 NormalMatrix; 12 | 13 | void main() 14 | { 15 | /*Position = VertexPosition;*/ 16 | gl_Position = ModelViewProjection * vec4(VertexPosition, 1.0); 17 | Wrap = VertexWrap; 18 | Position = gl_Position.xyz; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /bin/data/things/bernie.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bernie", 3 | "image": "bernie.png", 4 | "size" : [ 16, 16 ], 5 | "tile-size" : [ 16, 16 ], 6 | "origin" : [ 0.5, 0.75 ], 7 | "mask" : [ 0.25, 0.5, 0.875, 1.0 ], 8 | "animation" : { 9 | "speed" : 10.0, 10 | "frames" : { 11 | "down": [0,1], 12 | "up":[2,3], 13 | "left": [4,5], 14 | "right": ["hflip",4,5] 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /bin/data/things/bernie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/things/bernie.png -------------------------------------------------------------------------------- /bin/data/things/bighead.json: -------------------------------------------------------------------------------- 1 | { 2 | "size" : [ 32, 32 ], 3 | "tile-size" : [ 32, 32 ], 4 | "origin" : [ 0.5, 0.75 ], 5 | "mask" : [ 0.25, 0.5, 0.875, 1.0 ], 6 | "animation" : { 7 | "speed" : 10.0, 8 | "frames" : { 9 | "down": [0,1], 10 | "up": [4], 11 | "left": [2,3], 12 | "right": ["hflip",2,3], 13 | "death": ["once", 4,5,6] 14 | } 15 | }, 16 | "hp": 10 17 | } 18 | -------------------------------------------------------------------------------- /bin/data/things/bighead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/things/bighead.png -------------------------------------------------------------------------------- /bin/data/things/bloodpool1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/things/bloodpool1.png -------------------------------------------------------------------------------- /bin/data/things/bloodpool2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/things/bloodpool2.png -------------------------------------------------------------------------------- /bin/data/things/bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/things/bullet.png -------------------------------------------------------------------------------- /bin/data/things/fetus_maximus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/things/fetus_maximus.png -------------------------------------------------------------------------------- /bin/data/things/ghost.json: -------------------------------------------------------------------------------- 1 | { 2 | "type" : "sprite", 3 | "size" : [ 16, 16 ], 4 | "tile-size" : [ 16, 16 ], 5 | "origin" : [ 0.5, 0.75 ], 6 | "mask" : [ 0.5, 0.5, 0.875, 1.0 ], 7 | "animation" : { 8 | "speed" : 10.0, 9 | "frames" : { 10 | "down": [0,1,2], 11 | "up":[3,4,5], 12 | "left": [6,7,8], 13 | "right": ["hflip",6,7,8] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /bin/data/things/ghost.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/things/ghost.png -------------------------------------------------------------------------------- /bin/data/things/gib1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/things/gib1.png -------------------------------------------------------------------------------- /bin/data/things/gib2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/things/gib2.png -------------------------------------------------------------------------------- /bin/data/things/gib3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/things/gib3.png -------------------------------------------------------------------------------- /bin/data/things/hooded_priest.json: -------------------------------------------------------------------------------- 1 | { 2 | "type" : "sprite", 3 | "size" : [ 16, 16 ], 4 | "tile-size" : [ 16, 16 ], 5 | "origin" : [ 0.5, 0.75 ], 6 | "mask" : [ 0.25, 0.50, 0.875, 1.0 ], 7 | "animation" : { 8 | "speed" : 10.0, 9 | "frames" : { 10 | "down": [0,1,0,2], 11 | "up":[3,4,3,5], 12 | "left": [6,7,6,8], 13 | "right": ["hflip",6,7,6,8], 14 | "death": ["once",9,10,11] 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /bin/data/things/hooded_priest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/things/hooded_priest.png -------------------------------------------------------------------------------- /bin/data/things/mask_kid.json: -------------------------------------------------------------------------------- 1 | { 2 | "type" : "sprite", 3 | "size" : [ 16, 16 ], 4 | "tile-size" : [ 16, 16 ], 5 | "origin" : [ 0.5, 0.75], 6 | "mask" : [ 0.25, 0.5, 0.75, 1.0 ], 7 | "animation" : { 8 | "speed" : 10.0, 9 | "frames" : { 10 | "down": [0,1,0,2], 11 | "up": [3,4,3,5], 12 | "left": [6,7,6,8], 13 | "right": ["hflip",6,7,6,8], 14 | "death": ["once", 9, 10, 11] 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /bin/data/things/mask_kid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/things/mask_kid.png -------------------------------------------------------------------------------- /bin/data/things/meat_fly.json: -------------------------------------------------------------------------------- 1 | { 2 | "size" : [ 16, 16 ], 3 | "tile-size" : [ 16, 16 ], 4 | "origin" : [ 0.5, 0.75 ], 5 | "mask" : [ 0.25, 0.5, 0.875, 1.0 ], 6 | "animation" : { 7 | "speed" : 10.0, 8 | "frames" : { 9 | "down": [0,1], 10 | "up":[2,3], 11 | "left": ["hflip",4,5], 12 | "right": [4,5], 13 | "death": ["once", 7,8] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /bin/data/things/meat_fly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/things/meat_fly.png -------------------------------------------------------------------------------- /bin/data/things/raven.json: -------------------------------------------------------------------------------- 1 | { 2 | "size" : [ 16, 16 ], 3 | "tile-size" : [ 16, 16 ], 4 | "origin" : [ 0.5, 0.75 ], 5 | "mask" : [ 0.25, 0.5, 0.875, 1.0 ], 6 | "animation" : { 7 | "speed" : 10.0, 8 | "frames" : { 9 | "down": [0,1], 10 | "up":[2,3], 11 | "left": ["hflip",4,5], 12 | "right": [4,5], 13 | "death": ["once", 6,7,8] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /bin/data/things/raven.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/things/raven.png -------------------------------------------------------------------------------- /bin/data/things/scarecrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/things/scarecrow.png -------------------------------------------------------------------------------- /bin/data/things/severed_head.json: -------------------------------------------------------------------------------- 1 | { 2 | "size" : [ 16, 16 ], 3 | "tile-size" : [ 16, 16 ], 4 | "origin" : [ 0.5, 0.75 ], 5 | "mask" : [ 0.25, 0.5, 0.875, 1.0 ], 6 | "animation" : { 7 | "speed" : 10.0, 8 | "frames" : { 9 | "down": [0,1,2,3], 10 | "up": [0,1,2,3], 11 | "left": [0,1,2,3], 12 | "right": [0,1,2,3], 13 | "death": ["once",4,5,6,7] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /bin/data/things/severed_head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/things/severed_head.png -------------------------------------------------------------------------------- /bin/data/things/skeleton.json: -------------------------------------------------------------------------------- 1 | { 2 | "type" : "sprite", 3 | "size" : [ 16, 16 ], 4 | "tile-size" : [ 16, 16 ], 5 | "origin" : [ 0.5, 0.75 ], 6 | "mask" : [ 0.5, 0.5, 0.875, 1.0 ], 7 | "animation" : { 8 | "speed" : 10.0, 9 | "frames" : { 10 | "down": [0,1,2], 11 | "up":[3,4,5], 12 | "left": [6,7,8], 13 | "right": ["hflip",6,7,8], 14 | "death": ["once", 9,10] 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /bin/data/things/skeleton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/things/skeleton.png -------------------------------------------------------------------------------- /bin/data/things/zombie.json: -------------------------------------------------------------------------------- 1 | { 2 | "type" : "sprite", 3 | "size" : [ 16, 16 ], 4 | "tile-size" : [ 16, 16 ], 5 | "origin" : [ 0.5, 0.75 ], 6 | "mask" : [ 0.5, 0.5, 0.875, 1.0 ], 7 | "animation" : { 8 | "speed" : 10.0, 9 | "frames" : { 10 | "spawn": [0,1,2], 11 | "down": [3,4,3,5], 12 | "up":[6,7,6,8], 13 | "left": [9,10,9,11], 14 | "right": ["hflip",9,10,9,11], 15 | "death": ["once",12,13,14] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /bin/data/things/zombie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/things/zombie.png -------------------------------------------------------------------------------- /bin/data/vendor/ionicons/.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | .sass-cache 3 | *.scssc 4 | *.swp -------------------------------------------------------------------------------- /bin/data/vendor/ionicons/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Drifty (http://drifty.com/) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /bin/data/vendor/ionicons/png/512/load-a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/vendor/ionicons/png/512/load-a.png -------------------------------------------------------------------------------- /bin/data/vendor/ionicons/png/512/load-b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/vendor/ionicons/png/512/load-b.png -------------------------------------------------------------------------------- /bin/data/vendor/ionicons/png/512/load-c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/vendor/ionicons/png/512/load-c.png -------------------------------------------------------------------------------- /bin/data/vendor/ionicons/png/512/load-d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flipcoder/qorpse/218a92067d66b6a98b949e53c4063d712437b469/bin/data/vendor/ionicons/png/512/load-d.png -------------------------------------------------------------------------------- /bin/data/weapons.json: -------------------------------------------------------------------------------- 1 | { 2 | "pistol": { 3 | "ammo-type": "pistol bullets", 4 | "gfx": "bullet", 5 | "ammo": 25, 6 | "in-clip": 5, 7 | "clip-capacity": 5, 8 | "chamber": 1, 9 | "range": 256.0, 10 | "fire-speed": 5.0, 11 | "reload-speed": 3.0, 12 | "bullet-speed": 512.0, 13 | "accuracy": 1.0 14 | }, 15 | "shotgun": { 16 | "ammo-type": "shells", 17 | "gfx": "bullet", 18 | "ammo": 48, 19 | "in-clip": 8, 20 | "clip-capacity": 8, 21 | "chamber": 4, 22 | "range": 256.0, 23 | "fire-speed": 3.0, 24 | "reload-speed": 3.0, 25 | "bullet-speed": 512.0, 26 | "accuracy": 0.5 27 | }, 28 | "uzis": { 29 | "ammo-type": "uzi rounds", 30 | "gfx": "bullet", 31 | "ammo": 128, 32 | "in-clip": 24, 33 | "clip-capacity":24, 34 | "chamber": 1, 35 | "range": 256.0, 36 | "fire-speed": 10.0, 37 | "reload-speed": 1.0, 38 | "bullet-speed": 512.0, 39 | "accuracy": 0.95, 40 | "flags": ["dual"] 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /bin/profiles/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Player", 3 | "actor": "grampire", 4 | "input": { 5 | "binds": { 6 | "left": "left", 7 | "right": "right", 8 | "up": "up", 9 | "down": "down", 10 | "z": [ 11 | "shoot", 12 | "select" 13 | ], 14 | "left shift": "sprint", 15 | "x": "strafe", 16 | "v": "reload", 17 | "c": [ 18 | "next_weapon", 19 | "back" 20 | ], 21 | "d": "previous_weapon", 22 | "a": "action", 23 | "escape": "escape" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /bin/profiles/gamepad.json_: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Player", 3 | "actor": "grampire", 4 | "input": { 5 | "binds": { 6 | "gamepad hat 0": "left", 7 | "gamepad hat 1": "right", 8 | "gamepad hat 2": "up", 9 | "gamepad hat 3": "down", 10 | "gamepad 0": [ 11 | "shoot", 12 | "select" 13 | ], 14 | "gamepad 12": "sprint", 15 | "gamepad 3": "strafe", 16 | "gamepad 1": "reload", 17 | "gamepad 4": [ 18 | "next_weapon", 19 | "back" 20 | ], 21 | "gamepad 17": "previous_weapon", 22 | "gamepad 20": "action", 23 | "gamepad 21": "escape" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /bin/profiles/keyboard.json_: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Player", 3 | "actor": "grampire", 4 | "input": { 5 | "binds": { 6 | "left": "left", 7 | "right": "right", 8 | "up": "up", 9 | "down": "down", 10 | "z": [ 11 | "shoot", 12 | "select" 13 | ], 14 | "left shift": "sprint", 15 | "x": "strafe", 16 | "v": "reload", 17 | "c": [ 18 | "next_weapon", 19 | "back" 20 | ], 21 | "d": "previous_weapon", 22 | "a": "action", 23 | "escape": "escape" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /bin/qorpse.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=qorpse 3 | Exec=qorpse 4 | Icon=qorpse 5 | Terminal=false 6 | Type=Application 7 | Categories=Game; 8 | -------------------------------------------------------------------------------- /bin/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "audio" : { 3 | "music-volume" : 100, 4 | "sound-volume" : 100, 5 | "volume" : 100 6 | }, 7 | "video" : { 8 | "resolution" : "1920x1080", 9 | "vsync" : false, 10 | "windowed" : false 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /bin/settings.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "video": { 3 | "resolution": { 4 | ".name": "Display Resolution", 5 | ".testable": true 6 | }, 7 | "windowed": { 8 | ".name": "Window", 9 | ".desc": "Play in windowed or fullscreen mode", 10 | ".values": [ false, true ], 11 | ".options": [ 12 | "Fullscreen", 13 | "Windowed", 14 | "Borderless" 15 | ] 16 | }, 17 | "aa": { 18 | ".name": "Anti-Aliasing", 19 | ".values": [ false ], 20 | ".desc": "Smooths sharp edges" 21 | }, 22 | "vsync": { 23 | ".name": "Vertical Sync", 24 | ".desc": "Reduces tearing but may lower frame rate", 25 | ".values": [ false, true ] 26 | } 27 | }, 28 | 29 | "audio": { 30 | "volume": { 31 | ".name": "Volume", 32 | ".min": 0, 33 | ".max": 100, 34 | ".default": 50, 35 | ".postfix": "%" 36 | } 37 | }, 38 | 39 | "controls": { 40 | }, 41 | 42 | "advanced": { 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /premake4.lua: -------------------------------------------------------------------------------- 1 | solution("qorpse") 2 | configurations {"Debug", "Release"} 3 | 4 | targetdir("bin") 5 | configuration "Debug" 6 | defines { "DEBUG" } 7 | --optimize "debug" 8 | flags { "Symbols" } 9 | configuration "Release" 10 | defines { "NDEBUG" } 11 | --optimize "speed" 12 | flags { "OptimizeSpeed", "FloatFast" } 13 | targetname("qorpse_dist") 14 | 15 | project("qorpse") 16 | --uuid("") 17 | kind("WindowedApp") 18 | language("C++") 19 | links { 20 | "pthread", 21 | "GL", 22 | "GLU", 23 | "SDL2", 24 | "GLEW", 25 | --"assimp", 26 | "IL", 27 | "ILU", 28 | "openal", 29 | "alut", 30 | "ogg", 31 | "vorbis", 32 | "vorbisfile", 33 | "boost_system", 34 | "boost_thread", 35 | "boost_filesystem", 36 | "boost_python", 37 | "boost_coroutine", 38 | "jsoncpp", 39 | "assimp", 40 | "freeimage" 41 | } 42 | files { 43 | "src/**.h", 44 | "src/**.cpp" 45 | } 46 | excludes { 47 | "src/Qor/Main.cpp", 48 | "src/Qor/Info.cpp", 49 | -- no physics: 50 | "src/Qor/Physics.*", 51 | -- no net: 52 | "src/Qor/Net.*", 53 | "src/Qor/DemoState.*", 54 | "src/Qor/tests/**", 55 | "src/Qor/scripts/**", 56 | "src/Qor/addons/**", 57 | "src/Qor/shaders/**" 58 | } 59 | includedirs { 60 | "vendor/include/", 61 | "/usr/local/include/", 62 | --"/usr/include/cegui-0/", 63 | "/usr/include/bullet/", 64 | } 65 | libdirs { 66 | --"/usr/lib/cegui-0.8/", 67 | "/usr/local/lib/", 68 | "/usr/local/lib64/", 69 | } 70 | 71 | defines { "QOR_NO_PHYSICS" } 72 | buildoptions { 73 | "`python2-config --includes`", 74 | "`pkg-config --cflags cairomm-1.0 pangomm-1.4`" 75 | } 76 | linkoptions { 77 | "`python2-config --libs`", 78 | "`pkg-config --libs cairomm-1.0 pangomm-1.4`" 79 | --"`pkg-config --libs cairomm pangomm`" 80 | } 81 | configuration {"debug"} 82 | defines { "BACKWARD_HAS_BFD=1" } 83 | links { 84 | "z", 85 | "bfd", 86 | "iberty" 87 | } 88 | linkoptions { "`llvm-config --libs core` `llvm-config --ldflags`" } 89 | configuration {} 90 | 91 | --configuration { "linux" } 92 | -- includedirs { 93 | -- "/usr/include/lua5.1", 94 | -- } 95 | --configuration {} 96 | 97 | configuration { "gmake" } 98 | buildoptions { "-std=c++11" } 99 | --buildoptions { "-std=c++11", "-pedantic", "-Wall", "-Wextra" } 100 | configuration { "macosx" } 101 | buildoptions { "-U__STRICT_ANSI__", "-stdlib=libc++" } 102 | linkoptions { "-stdlib=libc++" } 103 | configuration {} 104 | 105 | -------------------------------------------------------------------------------- /qorpse.tasks: -------------------------------------------------------------------------------- 1 | [ ] Qor: Canvas draw optimizations 2 | [.] parallax view layers 3 | [.] BUG: transparency blend options don't work (?) 4 | [.] block walking outside the map 5 | [.] confine camera to map 6 | [?] would be nice to have access for resources to have access to config schema for value clamping 7 | # isn't there a better way to do this? don't remember what i was thinking when i wrote this ^ 8 | # just add a validation function to meta? 9 | # access to value range allows visual progress bars, etc. instead of just validity test 10 | # they DO have access, schema is stored inside resource cache 11 | [|] keys, locked doors 12 | [x] keys and inventory 13 | [?] BUG Thing::LOCK collision still not working 14 | [ ] level win conditions 15 | -------------------------------------------------------------------------------- /sg.json: -------------------------------------------------------------------------------- 1 | { 2 | "options": { 3 | "env": { 4 | "CXX": "clang++" 5 | }, 6 | "makefile_params": [ 7 | "CXX='clang++'" 8 | ] 9 | }, 10 | 11 | "required": { 12 | "flipcoder/qor": { 13 | "link": "Qor" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Character.cpp: -------------------------------------------------------------------------------- 1 | #include "Character.h" 2 | using namespace std; 3 | using namespace glm; 4 | 5 | Character :: Character( 6 | Session* session, 7 | Profile* profile, 8 | const string& fn, 9 | Cache* resources, 10 | glm::vec3 pos = glm::vec3(0.0f) 11 | ): 12 | Sprite(fn, resources, profile->config()->at("actor"), pos), 13 | m_HP(100), 14 | m_MaxHP(100), 15 | m_Armor(0), 16 | m_MaxArmor(0), 17 | m_Skin(profile->config()->at("actor")), 18 | m_pSession(session), 19 | m_pProfile(profile), 20 | //m_pController(profile->controller().get()), 21 | m_pResources(resources) 22 | { 23 | auto cfg = make_shared( 24 | m_pResources->transform("weapons.json") 25 | ); 26 | for(auto&& e: *cfg) 27 | { 28 | auto wpn = e.as>(); 29 | m_Weapons.emplace_back( 30 | e.key, 31 | wpn->at("ammo-type"), 32 | wpn->at("gfx"), 33 | make_shared(wpn->at("ammo")), 34 | wpn->at("in-clip"), 35 | wpn->at("clip-capacity"), 36 | wpn->at("chamber"), 37 | wpn->at("range"), 38 | wpn->at("fire-speed"), 39 | wpn->at("reload-speed"), 40 | wpn->at("bullet-speed"), 41 | wpn->at("accuracy") 42 | ); 43 | } 44 | 45 | m_Weapon.on_change.connect([this](const int&){ 46 | if(m_Weapons.empty()){ 47 | m_Ammo = 0; 48 | return; 49 | } 50 | m_Ammo = m_Weapons[m_Weapon].ammo(); 51 | }); 52 | set_states({"stand","down"}); 53 | m_Dead.on_change.connect([this](const bool& b){ 54 | if(b) 55 | set_states({"death"}); 56 | }); 57 | } 58 | 59 | void Character :: logic_self(Freq::Time t) 60 | { 61 | Sprite::logic_self(t); 62 | for(auto& w: m_Weapons) 63 | w.logic(t); 64 | } 65 | 66 | bool Character :: indoors(bool b) 67 | { 68 | //if(m_bIndoors == b) 69 | // return false; 70 | 71 | m_Indoors += b?1:-1; 72 | return m_Indoors; 73 | 74 | //bool clean = true; 75 | //for(auto&& c: m_Cameras) { 76 | // auto cam = c.lock(); 77 | // if(not cam) { 78 | // clean = false; 79 | // continue; 80 | // } 81 | // cam->range( 82 | // -(b ? position().z: 100.0f), 83 | // 100.0f 84 | // ); 85 | //} 86 | //if(not clean) 87 | // kit::remove_if(m_Cameras, [](weak_ptr cam){ 88 | // return not cam.lock(); 89 | // }); 90 | } 91 | 92 | void Character :: add_camera(std::weak_ptr camera) 93 | { 94 | m_Cameras.push_back(camera); 95 | kit::remove_if(m_Cameras, [](weak_ptr const& cam){ 96 | return not cam.lock(); 97 | }); 98 | } 99 | 100 | -------------------------------------------------------------------------------- /src/Character.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAYER_H_R0CEOUXE 2 | #define PLAYER_H_R0CEOUXE 3 | 4 | #include 5 | #include "Qor/kit/cache/cache.h" 6 | #include "Qor/Sprite.h" 7 | #include "Qor/Session.h" 8 | #include "Qor/Profile.h" 9 | #include "Qor/Session.h" 10 | #include "Weapon.h" 11 | #include "Qor/kit/reactive/reactive.h" 12 | 13 | class Character: 14 | public Sprite 15 | { 16 | public: 17 | 18 | Character( 19 | Session* session, 20 | Profile* profile, 21 | const std::string& fn, 22 | Cache* resources, 23 | glm::vec3 pos 24 | ); 25 | virtual ~Character() {} 26 | 27 | virtual void logic_self(Freq::Time t) override; 28 | 29 | void set_direction(glm::vec2 dir) { 30 | m_Dir = dir; 31 | } 32 | 33 | int clip() const { 34 | return weapon() ? weapon()->clip() : 0; 35 | } 36 | int ammo() const { 37 | return weapon() ? weapon()->ammo() : 0; 38 | } 39 | int clip_size() const { 40 | return weapon() ? weapon()->clip_size() : 0; 41 | } 42 | bool reload() { 43 | if(weapon()) 44 | return weapon()->reload(); 45 | return false; 46 | } 47 | 48 | void clip(int a) { 49 | if(weapon()) 50 | weapon()->clip(a); 51 | } 52 | 53 | int hp() const { 54 | return m_HP; 55 | } 56 | void hp(int a) { 57 | m_HP = std::min(m_HP+a, m_MaxHP); 58 | } 59 | bool heal() { 60 | if(m_HP < m_MaxHP){ 61 | m_HP = m_MaxHP; 62 | return true; 63 | } 64 | return false; 65 | } 66 | bool heal(int hp) { 67 | if(hp >= 0 && m_HP == m_MaxHP) 68 | return false; 69 | m_HP = std::min(m_HP+hp, m_MaxHP); 70 | if(m_HP <= 0) { 71 | m_HP = std::max(m_HP, 0); 72 | m_Dead = true; 73 | return false; 74 | } 75 | return true; 76 | } 77 | bool damage(int hp) { return heal(-hp); } 78 | 79 | int hp_percent() { 80 | // dont round 81 | return ( 82 | (m_MaxHP ? (m_HP*1.0f) / (m_MaxHP*1.0f) : 0) + 83 | (m_MaxArmor ? (m_Armor*1.0f) / (m_MaxArmor*1.0f) : 0) 84 | ) * 100.0f; 85 | } 86 | 87 | int max_hp() const { 88 | return m_MaxHP; 89 | } 90 | void max_hp(int a) { 91 | m_MaxHP = a; 92 | } 93 | 94 | Weapon* weapon() { 95 | assert(m_Weapon < m_Weapons.size()); 96 | if(!m_Weapons.empty()) 97 | return &m_Weapons[m_Weapon]; 98 | assert(false); 99 | return nullptr; 100 | } 101 | const Weapon* weapon() const { 102 | return const_cast(this)->weapon(); 103 | } 104 | 105 | bool switch_weapon(int idx) { 106 | int old_idx = m_Weapon; 107 | m_Weapon = kit::mod( 108 | m_Weapon + idx, m_Weapons.size() 109 | ); 110 | return idx == old_idx; 111 | } 112 | 113 | int shoot() { 114 | if(weapon()) 115 | return weapon()->shoot(); 116 | return 0; 117 | } 118 | 119 | bool indoors() const { return m_Indoors; } 120 | 121 | bool indoors(bool b); 122 | 123 | void add_camera(std::weak_ptr camera); 124 | 125 | bool alive() const { return not m_Dead; } 126 | bool dead() const { return m_Dead; } 127 | 128 | KIT_REACTIVE_SIGNAL(on_hp_change, m_HP) 129 | KIT_REACTIVE_SIGNAL(on_weapon_change, m_Weapon) 130 | KIT_REACTIVE_SIGNAL(on_ammo_change, m_Ammo) 131 | 132 | KIT_REACTIVE_SIGNAL(on_speak, m_Speak) 133 | KIT_REACTIVE_SIGNAL(on_death, m_Dead) 134 | 135 | void say(std::string msg) { 136 | m_Speak = msg; // reactive 137 | } 138 | bool give(std::string id) { 139 | if(not kit::has(m_Items, id)){ 140 | m_Items.push_back(id); 141 | return true; 142 | } 143 | return false; 144 | } 145 | bool has(std::string id) const { 146 | return kit::has(m_Items, id); 147 | } 148 | std::string skin() const { 149 | return m_Skin; 150 | } 151 | 152 | private: 153 | 154 | kit::reactive m_Speak; 155 | 156 | kit::reactive m_Dead = false; 157 | kit::reactive m_HP; 158 | int m_MaxHP; 159 | kit::reactive m_Ammo; // current weapon ammo 160 | kit::reactive m_Armor; 161 | int m_MaxArmor; 162 | int m_Indoors = 0; 163 | 164 | std::vector m_Weapons; 165 | std::vector m_Items; 166 | kit::reactive m_Weapon = 0; 167 | 168 | std::string m_Skin; 169 | Session* m_pSession = nullptr; 170 | Profile* m_pProfile = nullptr; 171 | Controller* m_pController = nullptr; 172 | Cache* m_pResources = nullptr; 173 | 174 | glm::vec2 m_Dir; 175 | 176 | std::vector> m_Cameras; 177 | }; 178 | 179 | #endif 180 | 181 | -------------------------------------------------------------------------------- /src/CharacterInterface.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "CharacterInterface.h" 3 | #include "Qor/Sound.h" 4 | #include 5 | #include "World.h" 6 | using namespace std; 7 | using namespace glm; 8 | 9 | CharacterInterface :: CharacterInterface( 10 | const shared_ptr& input, 11 | const shared_ptr& character, 12 | World* world, 13 | Cache* resources 14 | ): 15 | NodeInterface(input, character), 16 | m_pWorld(world), 17 | m_pResources(resources) 18 | { 19 | lock_sprite(); 20 | BOOST_SCOPE_EXIT(this_) { 21 | this_->unlock_sprite(); 22 | } BOOST_SCOPE_EXIT_END 23 | 24 | m_ButtonNames = { 25 | "up", 26 | "down", 27 | "left", 28 | "right", 29 | 30 | "shoot", 31 | "sprint", 32 | "action", 33 | "strafe", 34 | "reload", 35 | "previous_weapon", 36 | "next_weapon" 37 | }; 38 | 39 | m_StateNames = { 40 | "up", 41 | "down", 42 | "left", 43 | "right", 44 | 45 | "stand", 46 | "walk", 47 | 48 | "death" 49 | }; 50 | 51 | for(unsigned int i=0; i<(unsigned int)Button::MAX; ++i) 52 | try{ 53 | m_Buttons[i] = input->button_id(m_ButtonNames[i]); 54 | }catch(const out_of_range&){ 55 | m_Buttons[i] = numeric_limits::max(); 56 | } 57 | for(unsigned int i=0; i<(unsigned int)State::MAX; ++i) 58 | try{ 59 | m_States[i] = m_pCharacter->state_id(m_StateNames[i]); 60 | }catch(const out_of_range&){ 61 | m_States[i] = numeric_limits::max(); 62 | } 63 | 64 | set_state(State::STAND); 65 | set_state(State::DOWN); 66 | 67 | auto crosshair = make_shared( 68 | resources->transform("arrow.png"), 69 | resources, 70 | "", // no skin 71 | vec3(0.0f, 0.0f, 10.0f) 72 | ); 73 | crosshair->mesh()->offset(vec3(0.0f, -m_fCrosshairDist, 0.0f)); 74 | m_pCharacter->add(crosshair); 75 | crosshair->each([](Node* n){ 76 | n->layer(World::INDICATOR); 77 | }, Node::Each::RECURSIVE | Node::Each::INCLUDE_SELF); 78 | 79 | m_pCrosshair = crosshair; 80 | 81 | m_vDir = vec2(0.0f, 1.0f); 82 | m_CrosshairEase.stop(Angle::degrees(180)); 83 | } 84 | 85 | void CharacterInterface :: event() 86 | { 87 | lock_input(); 88 | BOOST_SCOPE_EXIT_ALL(this) { 89 | unlock_input(); 90 | }; 91 | lock_sprite(); 92 | BOOST_SCOPE_EXIT_ALL(this) { 93 | unlock_sprite(); 94 | }; 95 | 96 | if(m_pCharacter->dead()) // block control on death 97 | { 98 | m_pCharacter->velocity(glm::vec3(0.0f)); 99 | return; 100 | } 101 | 102 | auto crosshair = m_pCrosshair.lock(); 103 | assert(crosshair); 104 | 105 | const bool strafe = button(Button::STRAFE); 106 | 107 | m_vMove = vec2(0.0f); 108 | if(button(Button::LEFT)) 109 | m_vMove += vec2(-button(Button::LEFT).pressure(), 0.0f); 110 | if(button(Button::RIGHT)) 111 | m_vMove += vec2(button(Button::RIGHT).pressure(), 0.0f); 112 | if(button(Button::UP)) 113 | m_vMove += vec2(0.0f, -button(Button::UP).pressure()); 114 | if(button(Button::DOWN)) 115 | m_vMove += vec2(0.0f, button(Button::DOWN).pressure()); 116 | 117 | if(m_vMove != vec2()) 118 | m_vMove = glm::normalize(m_vMove); 119 | 120 | if(!strafe) 121 | { 122 | m_CrosshairEase.resume(); 123 | 124 | if(m_vMove != vec2()) 125 | { 126 | vec2 old_dir = m_vDir; 127 | m_vDir = normalize(m_vMove); 128 | 129 | Angle a(angle(old_dir, m_vDir), Angle::RADIANS); 130 | if(fabs(a.degrees()) > K_EPSILON) 131 | { 132 | //auto atest = orientedAngle( 133 | // vec2(m_vDir.x, -m_vDir.y), 134 | // vec2(0.0f, 1.0f) 135 | //); 136 | //LOGf("atest: %s", atest); 137 | m_CrosshairEase.stop( 138 | Angle::radians( 139 | orientedAngle( 140 | vec2(m_vDir.x, -m_vDir.y), 141 | vec2(0.0f, 1.0f) 142 | ) 143 | ), 144 | Freq::Time(100), 145 | //Freq::Time((fabs(a.degrees()) > 90.0f + K_EPSILON) ? 0 : 100), 146 | INTERPOLATE(linear) 147 | //[](const Angle& a, const Angle& b, float t){ 148 | // return a + (b-a)*t; 149 | //} 150 | ); 151 | } 152 | } 153 | 154 | // TODO: get direction from crosshair angle 155 | if(fabs(m_vDir.y) > K_EPSILON) 156 | set_state(m_vDir.y > 0.0f ? State::DOWN : State::UP); 157 | else if(fabs(m_vDir.x) > K_EPSILON) 158 | set_state(m_vDir.x > 0.0f ? State::RIGHT: State::LEFT); 159 | } 160 | else 161 | { 162 | m_CrosshairEase.pause(); 163 | } 164 | 165 | //crosshair->reset_orientation(); 166 | //*crosshair->matrix() = rotate(mat4(), 167 | // orientedAngle( 168 | // vec2(m_vDir.x, -m_vDir.y), vec2(0.0f, 1.0f) 169 | // ), 170 | // Axis::Z 171 | //); 172 | 173 | m_fSpeed = m_fWalkSpeed * std::max( 174 | std::max(button(Button::LEFT).pressure(), button(Button::RIGHT).pressure()), 175 | std::max(button(Button::UP).pressure(), button(Button::DOWN).pressure()) 176 | ); 177 | if(button(Button::SPRINT) && !strafe) 178 | { 179 | m_fSpeed = m_fWalkSpeed * m_fSprintMult; 180 | m_pCharacter->speed(m_fSprintMult); 181 | } 182 | else 183 | { 184 | m_pCharacter->resume(); 185 | } 186 | 187 | set_state(length(m_vMove) > K_EPSILON ? State::WALK : State::STAND); 188 | 189 | if(button(Button::PREVIOUS_WEAPON).pressed_now()) 190 | m_pCharacter->switch_weapon(-1); 191 | if(button(Button::NEXT_WEAPON).pressed_now()) 192 | m_pCharacter->switch_weapon(1); 193 | } 194 | 195 | void CharacterInterface :: logic(Freq::Time t) 196 | { 197 | lock_sprite(); 198 | BOOST_SCOPE_EXIT_ALL(this) { 199 | unlock_sprite(); 200 | }; 201 | lock_input(); 202 | BOOST_SCOPE_EXIT_ALL(this) { 203 | unlock_input(); 204 | }; 205 | 206 | if(m_pCharacter->dead()) // block control on death 207 | return; 208 | 209 | m_CrosshairEase.logic(t); 210 | auto crosshair = m_pCrosshair.lock(); 211 | assert(crosshair); 212 | //*crosshair->matrix() = m_CrosshairEase.get(); 213 | 214 | crosshair->reset_orientation(); 215 | glm::vec3 pos = Matrix::translation(*crosshair->matrix()); 216 | 217 | *crosshair->matrix() = glm::rotate(m_CrosshairEase.get().radians(), Axis::Z); 218 | //crosshair->pend(); // will pend below 219 | crosshair->position(pos); 220 | 221 | //if(length(m_vMove) > K_EPSILON) 222 | m_pCharacter->velocity(vec3(m_vMove * m_fSpeed, 0.0f)); 223 | 224 | //if(!m_pCharacter->clip()) 225 | // reload(); 226 | 227 | if(button(Button::RELOAD).pressed()) 228 | reload(); 229 | 230 | if(m_pCharacter->weapon() && !m_pCharacter->weapon()->delayed() && button(Button::SHOOT).pressed()) 231 | { 232 | if(m_pCharacter->clip()) 233 | { 234 | unsigned num_bullets = m_pCharacter->shoot(); 235 | 236 | if(num_bullets) 237 | { 238 | auto bulletsound = make_shared( 239 | m_pResources->transform(m_pCharacter->weapon()->name() + ".wav"), 240 | m_pResources 241 | ); 242 | m_pCharacter->add(bulletsound); 243 | bulletsound->play(); 244 | auto bulletsoundptr = bulletsound.get(); 245 | bulletsound->on_tick.connect([bulletsoundptr](Freq::Time){ 246 | if(not bulletsoundptr->source()->playing()) 247 | bulletsoundptr->detach(); 248 | }); 249 | } 250 | 251 | auto path = m_pResources->transform(m_pCharacter->weapon()->bullet_gfx() + ".png"); 252 | for(unsigned i=0; i(path, m_pResources); 255 | float vary = (rand() % (1+kit::round_int((1.0f - m_pCharacter->weapon()->accuracy()) * 45.0f))) * 1.0f; 256 | vary *= (rand() % 2) ? 1.0f : -1.0f; 257 | auto dir = (m_CrosshairEase.get() - Angle(90.0f + vary, Angle::DEGREES)).vector(); 258 | auto bulletptr = bullet.get(); 259 | auto range = make_shared(m_pCharacter->weapon()->range()); 260 | auto bullet_speed = m_pCharacter->weapon()->bullet_speed(); 261 | bullet->on_tick.connect([bulletptr, dir, range, bullet_speed, vary] (Freq::Time t){ 262 | bulletptr->move(vec3( 263 | dir.x * bullet_speed * t.s(), 264 | dir.y * bullet_speed * t.s(), 265 | 0.0f 266 | )); 267 | *range -= t.s() * bullet_speed; 268 | if(*range <= 0.0f) 269 | bulletptr->detach(); 270 | }); 271 | crosshair->add(bullet); 272 | bullet->position(glm::vec3(0.0f, 0.0f, 0.0f)); 273 | bullet->collapse(); // collapse to player 274 | bullet->collapse(); // collapse to root / map 275 | auto bpos = bullet->position(); 276 | bullet->position(glm::vec3( 277 | bpos.x, bpos.y, m_pCharacter->position().z + 0.25f 278 | )); 279 | m_pWorld->setup_bullet(bullet); 280 | } 281 | }else{ 282 | reload(); 283 | } 284 | } 285 | } 286 | 287 | void CharacterInterface :: reload() 288 | { 289 | if(m_pCharacter->reload()) 290 | { 291 | auto sound = make_shared( 292 | "reload.wav", 293 | m_pResources 294 | ); 295 | m_pCharacter->add(sound); 296 | sound->play(); 297 | auto soundptr = sound.get(); 298 | sound->on_tick.connect([soundptr](Freq::Time){ 299 | if(not soundptr->source()->playing()) 300 | soundptr->detach(); 301 | }); 302 | } 303 | } 304 | 305 | -------------------------------------------------------------------------------- /src/CharacterInterface.h: -------------------------------------------------------------------------------- 1 | #ifndef _QORPSEINTERFACE_H_W5TRRMBU 2 | #define _QORPSEINTERFACE_H_W5TRRMBU 3 | 4 | #include 5 | #include 6 | #include 7 | #include "Qor/kit/cache/cache.h" 8 | #include "Qor/NodeInterface.h" 9 | #include "Qor/Input.h" 10 | #include "Qor/Sprite.h" 11 | #include "Qor/kit/freq/animation.h" 12 | #include "Character.h" 13 | class World; 14 | 15 | class CharacterInterface: 16 | public NodeInterface, 17 | public std::enable_shared_from_this 18 | { 19 | public: 20 | 21 | enum class Button: unsigned int 22 | { 23 | UP, 24 | DOWN, 25 | LEFT, 26 | RIGHT, 27 | 28 | SHOOT, 29 | SPRINT, 30 | ACTION, 31 | STRAFE, 32 | RELOAD, 33 | PREVIOUS_WEAPON, 34 | NEXT_WEAPON, 35 | 36 | MAX, 37 | 38 | }; 39 | 40 | enum class State: unsigned int 41 | { 42 | UP, 43 | DOWN, 44 | LEFT, 45 | RIGHT, 46 | 47 | STAND, 48 | WALK, 49 | 50 | DEAD, 51 | 52 | MAX 53 | }; 54 | 55 | CharacterInterface( 56 | const std::shared_ptr& input, 57 | const std::shared_ptr& character, 58 | World* world, 59 | Cache* resources 60 | ); 61 | virtual ~CharacterInterface() {} 62 | 63 | virtual void event() override; 64 | virtual void logic(Freq::Time t) override; 65 | 66 | const Input::Switch& button( 67 | Button btn 68 | ){ 69 | assert(m_pInput); // should be locked 70 | 71 | try{ 72 | unsigned int idx = m_Buttons.at((unsigned int)btn); 73 | if(idx != std::numeric_limits::max()) 74 | return m_pInput->button(idx); 75 | }catch(const std::out_of_range&){} 76 | 77 | return m_pInput->input()->dummy_switch(); 78 | } 79 | 80 | void set_state(State state) { 81 | assert(m_pCharacter); 82 | unsigned int idx = m_States.at((unsigned int)state); 83 | assert(idx != std::numeric_limits::max()); 84 | m_pCharacter->set_state(idx); 85 | } 86 | 87 | /* 88 | * Turns NodeInterface's controller() weak_ptr into m_pInput 89 | */ 90 | void lock_input() { m_pInput = controller(); } 91 | void unlock_input() { m_pInput.reset(); } 92 | void lock_sprite() { m_pCharacter= std::static_pointer_cast(node()); } 93 | void unlock_sprite() { m_pCharacter.reset(); } 94 | 95 | /* 96 | * Make sure Input can call this interface's logic 97 | * 98 | * Warning: can't do this in constructor, so we do it on the fly 99 | */ 100 | void plug() { 101 | if(!m_InterfaceID) 102 | m_InterfaceID = controller()->add_interface( 103 | std::static_pointer_cast( 104 | shared_from_this() 105 | ) 106 | ); 107 | } 108 | /* 109 | * Manually unplugs interface from input system 110 | * 111 | * This does not need to be called since Input system's weak_ptr's 112 | * will allow Interfaces that go out of scope to auto-unplug 113 | */ 114 | void unplug() { 115 | if(m_InterfaceID) 116 | { 117 | controller()->remove_interface( 118 | *m_InterfaceID 119 | ); 120 | m_InterfaceID = boost::optional(); 121 | } 122 | } 123 | 124 | std::shared_ptr character() { 125 | return std::static_pointer_cast(node()); 126 | } 127 | 128 | std::shared_ptr crosshair() { 129 | return m_pCrosshair.lock(); 130 | } 131 | 132 | glm::vec2 direction() const { 133 | return m_vDir; 134 | } 135 | 136 | void reload(); 137 | 138 | private: 139 | 140 | World* m_pWorld; 141 | 142 | std::shared_ptr m_pInput; 143 | std::shared_ptr m_pCharacter; 144 | 145 | std::array m_Buttons; 146 | std::array m_States; 147 | std::vector m_ButtonNames; 148 | std::vector m_StateNames; 149 | 150 | glm::vec2 m_vMove; 151 | glm::vec2 m_vDir; 152 | 153 | float m_fSprintMult = 1.5f; 154 | float m_fWalkSpeed = 100.0f; 155 | float m_fSpeed = 100.0f; 156 | float m_fCrosshairDist = 32.0f; 157 | 158 | Animation m_CrosshairEase; 159 | std::weak_ptr m_pCrosshair; 160 | boost::optional m_InterfaceID; 161 | 162 | Cache* m_pResources; 163 | 164 | }; 165 | 166 | #endif 167 | 168 | -------------------------------------------------------------------------------- /src/HUD.cpp: -------------------------------------------------------------------------------- 1 | #include "HUD.h" 2 | #include 3 | #include 4 | using namespace std; 5 | using namespace glm; 6 | 7 | HUD :: HUD(Window* win, const std::shared_ptr& character): 8 | m_pWindow(win), 9 | m_pCanvas(make_shared(win->size().x, win->size().y)), 10 | m_pCharacter(character) 11 | { 12 | add(m_pCanvas); 13 | setup_character(); 14 | } 15 | 16 | void HUD :: logic_self(Freq::Time t) 17 | { 18 | if(m_Dirty) 19 | { 20 | auto cairo = m_pCanvas->context(); 21 | cairo->save(); 22 | cairo->set_source_rgba(1.0f, 1.0f, 1.0f, 0.0f); 23 | cairo->set_operator(Cairo::OPERATOR_SOURCE); 24 | cairo->paint(); 25 | cairo->restore(); 26 | 27 | auto ch = m_pCharacter.lock(); 28 | if(!ch) return; 29 | 30 | m_Fade = 0.0f; 31 | 32 | //m_Fade += t.s() * 2.5f *(1.0f-ch->hp_percent()/100.0f); 33 | //m_Fade = fmod(m_Fade, 1.0f); 34 | 35 | cairo->select_font_face( 36 | "Press Start 2P", 37 | Cairo::FONT_SLANT_NORMAL, 38 | Cairo::FONT_WEIGHT_NORMAL 39 | ); 40 | float sz = kit::round_int(m_pWindow->size().y / 24.0f); 41 | const float shadow = 4.0f; 42 | 43 | // text with shadow offset 44 | for(int i=1; i>=0; --i) 45 | { 46 | cairo->set_font_size(sz); 47 | Color c = Color::white(); 48 | if(i == 1) 49 | c = Color::black(); 50 | //cairo->set_source_rgba(c.r(), c.g(), c.b(), c.a()); 51 | m_pCanvas->text( 52 | boost::to_upper_copy(ch->weapon()->name()) + " " + to_string(+ ch->clip()) + " / " + to_string(ch->ammo()), 53 | c, 54 | vec2(sz/2.0f - i*shadow, m_pWindow->size().y - sz/2.0f + i*shadow) 55 | ); 56 | if(i == 0){ 57 | c *= Color( 58 | 1.0f, 59 | ch->hp_percent() / 100.0f, 60 | ch->hp_percent() / 100.0f 61 | ); 62 | } 63 | //cairo->set_source_rgba(c.r(), c.g(), c.b(), c.a()); 64 | cairo->set_font_size( 65 | sz + (1.0f - ch->hp_percent() / 100.0f) * 4.0f * (1.0f+sin(m_Fade * K_TAU) 66 | //+ sz * (1.0f - ch->hp_percent() / 100.0f) 67 | )); 68 | m_pCanvas->text( 69 | to_string(ch->hp_percent()) + "%", 70 | c, 71 | vec2(m_pWindow->size().x - sz/2.0f - i*shadow, m_pWindow->size().y - sz/2.0f + i*shadow), 72 | Canvas::Align::RIGHT 73 | ); 74 | } 75 | 76 | m_Dirty = false; 77 | m_pCanvas->dirty(true); 78 | } 79 | } 80 | 81 | void HUD :: setup_character() 82 | { 83 | auto ch = m_pCharacter.lock(); 84 | if(!ch) return; 85 | 86 | auto _this = this; 87 | auto dirty_cb = [_this](const int&){ 88 | _this->m_Dirty = true; 89 | }; 90 | 91 | // setup change signals 92 | //m_pWindow->on_resize(dirty_cb); 93 | m_HPChange = ch->on_hp_change(dirty_cb); 94 | m_AmmoChange = ch->on_ammo_change(dirty_cb); 95 | } 96 | 97 | -------------------------------------------------------------------------------- /src/HUD.h: -------------------------------------------------------------------------------- 1 | #ifndef HUD_H_OWVM6H1E 2 | #define HUD_H_OWVM6H1E 3 | 4 | #include 5 | #include "Qor/Node.h" 6 | #include "Qor/Window.h" 7 | #include "Qor/Canvas.h" 8 | #include "Character.h" 9 | 10 | class HUD: 11 | public Node 12 | { 13 | public: 14 | 15 | HUD(Window* win, const std::shared_ptr& character); 16 | virtual void logic_self(Freq::Time t) override; 17 | 18 | void target(const std::shared_ptr& character) { 19 | m_pCharacter = character; 20 | } 21 | std::shared_ptr target() { 22 | return m_pCharacter.lock(); 23 | } 24 | 25 | void dirty(bool b) { m_Dirty = true; } 26 | 27 | private: 28 | 29 | void setup_character(); 30 | 31 | float m_Fade = 0.0f; 32 | 33 | Window* m_pWindow = nullptr; 34 | std::shared_ptr m_pCanvas; 35 | std::weak_ptr m_pCharacter; 36 | 37 | boost::signals2::scoped_connection m_HPChange; 38 | boost::signals2::scoped_connection m_AmmoChange; 39 | 40 | bool m_Dirty = true; 41 | }; 42 | 43 | #endif 44 | 45 | -------------------------------------------------------------------------------- /src/Info.h: -------------------------------------------------------------------------------- 1 | #ifndef _INFO_DEF_H_1VI18RQT 2 | #define _INFO_DEF_H_1VI18RQT 3 | 4 | #define PACKAGE "Qorpse" 5 | #define PACKAGE_VERSION "test" 6 | 7 | namespace Info 8 | { 9 | const char* const Program = PACKAGE; 10 | const char* const Version = PACKAGE_VERSION; 11 | } 12 | 13 | #endif 14 | 15 | -------------------------------------------------------------------------------- /src/Main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Qor/kit/kit.h" 5 | #include "Qor/Qor.h" 6 | #include "Info.h" 7 | #include "QorpseState.h" 8 | #include "MenuState.h" 9 | 10 | #include "Qor/kit/log/log.h" 11 | #include "Qor/kit/async/async.h" 12 | 13 | #ifdef DEBUG 14 | #include 15 | #endif 16 | 17 | using namespace std; 18 | using namespace kit; 19 | 20 | int main(int argc, const char** argv) 21 | { 22 | 23 | Args args(argc, argv); 24 | args.set("mod","qorpse"); 25 | //args.set("basic_shader","fog"); 26 | //args.set("no_loading_fade","true"); 27 | 28 | Texture::DEFAULT_FLAGS = Texture::TRANS | Texture::MIPMAP; 29 | 30 | #ifndef DEBUG 31 | try{ 32 | #endif 33 | auto engine = kit::make_unique(args); 34 | engine->states().register_class("menu"); 35 | engine->states().register_class("game"); 36 | engine->run("menu"); 37 | #ifndef DEBUG 38 | }catch(const Error&){ 39 | // already logged 40 | }catch(const std::exception& e){ 41 | LOGf("Uncaught exception: %s", e.what()); 42 | } 43 | #endif 44 | return 0; 45 | } 46 | 47 | -------------------------------------------------------------------------------- /src/MenuState.cpp: -------------------------------------------------------------------------------- 1 | #include "MenuState.h" 2 | #include "Qor/Input.h" 3 | #include "Qor/Qor.h" 4 | #include "Qor/TileMap.h" 5 | #include "Qor/Sound.h" 6 | #include "Qor/Sprite.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "Qor/ScreenFader.h" 12 | #include "TextScroller.h" 13 | #include "Qor/IPartitioner.h" 14 | #include "Qor/BasicPartitioner.h" 15 | using namespace std; 16 | using namespace glm; 17 | 18 | MenuState :: MenuState( 19 | Qor* engine 20 | //string fn 21 | ): 22 | m_pQor(engine), 23 | m_pInput(engine->input()), 24 | //m_pInterpreter(engine->interpreter()), 25 | //m_pScript(make_shared(engine->interpreter())), 26 | m_pPipeline(engine->pipeline()), 27 | m_pResources(engine->resources()), 28 | m_pRoot(make_shared()), 29 | m_pCanvas(make_shared( 30 | engine->window()->size().x, engine->window()->size().y 31 | )), 32 | m_pMenuGUI(make_shared( 33 | engine->session()->profile(0)->controller().get(), 34 | &m_MenuContext, 35 | &m_MainMenu, 36 | m_pPipeline->partitioner(), 37 | m_pCanvas.get(), 38 | m_pResources, 39 | "Press Start 2P", 40 | engine->window()->size().y / 30.0f, 41 | &m_Fade, 42 | 7 43 | )) 44 | { 45 | } 46 | 47 | void MenuState :: preload() 48 | { 49 | m_pCamera = make_shared(m_pResources, m_pQor->window()); 50 | m_pRoot->add(m_pCamera); 51 | m_pCamera->listen(true); 52 | 53 | //try{ 54 | // m_pScene = m_pResources->cache_cast("menu.json"); 55 | //}catch(const std::exception& e){ 56 | // ERRORf(GENERAL, "scene problem: %s", e.what()); 57 | //} 58 | //m_pRoot->add(m_pScene->root()); 59 | 60 | auto win = m_pQor->window(); 61 | 62 | auto logo = make_shared( 63 | make_shared( 64 | Prefab::quad( 65 | -vec2(win->size().y, win->size().y)/4.0f, 66 | vec2(win->size().y, win->size().y)/4.0f 67 | ) 68 | )); 69 | logo->add_modifier(make_shared(Prefab::quad_wrap( 70 | glm::vec2(0.0f, 1.0f), glm::vec2(1.0f, 0.0f) 71 | ))); 72 | auto tex = m_pResources->cache_cast("qorpse.png"); 73 | logo->material(make_shared(tex)); 74 | logo->move(vec3( 75 | win->center().x, 76 | win->center().y / 2.0f + win->size().y / 4.0f / 2.0f, 77 | -1.0f 78 | )); 79 | m_pRoot->add(logo); 80 | 81 | m_pMusic = m_pQor->make("monsters.ogg"); 82 | m_pRoot->add(m_pMusic); 83 | m_Ambient.on_change.connect([this](const Color& c){ 84 | int u = m_pPipeline->shader(1)->uniform("Ambient"); 85 | if(u != -1) 86 | m_pPipeline->shader(1)->uniform(u, vec4(c.vec3(), c.a())); 87 | }); 88 | 89 | //if(m_Filename.empty()) 90 | // m_Filename = m_pQor->args().value_or("mod", "demo"); 91 | // TODO: ensure filename contains only valid filename chars 92 | //m_pScript->execute_file("mods/"+ m_Filename +"/__init__.py"); 93 | //m_pScript->execute_string("preload()"); 94 | } 95 | 96 | void MenuState :: enter() 97 | { 98 | m_pCamera->ortho(); 99 | m_pPipeline->winding(true); 100 | m_pPipeline->bg_color(Color::black()); 101 | 102 | m_pInput->relative_mouse(false); 103 | if(m_pMusic) 104 | { 105 | m_pMusic->source()->pitch = 0.0f; 106 | m_pMusic->source()->play(); 107 | m_pMusic->source()->refresh(); 108 | } 109 | 110 | float sw = m_pQor->window()->size().x; 111 | float sh = m_pQor->window()->size().y; 112 | 113 | m_pRoot->add(m_pCanvas); 114 | 115 | //m_pRoot->add(make_shared( 116 | // make_shared(Prefab::quad(vec2(sw, sh))), 117 | // vector>{ 118 | // make_shared(Prefab::quad_wrap( 119 | // vec2(1.0f,1.0f), vec2() 120 | // )) 121 | // }, 122 | // make_shared(m_pCanvas->texture()) 123 | //)); 124 | 125 | auto bg = make_shared( 126 | make_shared(Prefab::quad(vec2(sw, sh), vec2(0.0f, 0.0f))), 127 | vector>{ 128 | make_shared(Prefab::quad_wrap()) 129 | }, 130 | make_shared("background.png", m_pResources) 131 | ); 132 | //auto bg2 = bg->instance(); 133 | bg->position(vec3(0.0f,0.0f,-1.0f)); 134 | //bg2->position(vec3(0.0f,0.0f,-1.0f)); 135 | m_pRoot->add(bg); 136 | on_tick.connect([this, bg](Freq::Time t){ 137 | m_WrapAccum.x += t.seconds() * 0.01f; 138 | m_WrapAccum.y += t.seconds() * 0.01f; 139 | 140 | m_WrapAccum += m_pInput->mouse_rel() * 0.0001f; 141 | m_WrapAccum.x = std::fmod(m_WrapAccum.x, 1.0f); 142 | m_WrapAccum.y = std::fmod(m_WrapAccum.y, 1.0f); 143 | 144 | bg->swap_modifier(0, make_shared(Prefab::quad_wrap( 145 | vec2(0.0f), vec2(1.0f), 146 | vec2(0.8f + 0.2f * m_Fade), //scale 147 | vec2(m_WrapAccum.x * 1.0f, m_WrapAccum.y * 1.0f) //offset 148 | ))); 149 | }); 150 | 151 | //auto z = make_shared( 152 | // make_shared(Prefab::quad(vec2(),vec2(sw, sh))), 153 | // vector>{ 154 | // make_shared(Prefab::quad_wrap()) 155 | // }, 156 | // make_shared("z.png", m_pResources) 157 | //); 158 | //z->position(vec3(0.0f,0.0f,-1.0f)); 159 | //m_pRoot->add(z); 160 | 161 | //on_tick.connect([this](Freq::Time){ 162 | // if(m_pInput->key(SDLK_F10)) 163 | // m_pQor->quit(); 164 | //}); 165 | 166 | // set up screen fade 167 | m_pPipeline->blend(true); 168 | 169 | //m_MainMenu.name("Qorpse"); 170 | m_MainMenu.options().emplace_back("New Game", [this]{ 171 | m_pDone = make_shared>([this]{ 172 | m_pQor->change_state("game"); 173 | }); 174 | m_pMenuGUI->pause(); 175 | }); 176 | m_MainMenu.options().emplace_back("Continue", [this]{ 177 | m_pDone = make_shared>([this]{ 178 | m_pQor->change_state("game"); 179 | }); 180 | m_pMenuGUI->pause(); 181 | }); 182 | m_MainMenu.options().emplace_back("Options", [this]{ 183 | m_MenuContext.push(&m_OptionsMenu); 184 | }); 185 | m_MainMenu.options().emplace_back("Quit", [this]{ 186 | m_pDone = make_shared>([this]{ 187 | m_pQor->pop_state(); 188 | }); 189 | }); 190 | 191 | m_pVolumeText = std::make_shared(string("Global Volume: ") + to_string( 192 | m_pResources->config()->meta("audio")->at("volume") 193 | ) + "%" 194 | ); 195 | m_pSoundText = std::make_shared(string("Sound Volume: ") + to_string( 196 | m_pResources->config()->meta("audio")->at("sound-volume") 197 | ) + "%" 198 | ); 199 | m_pMusicText = std::make_shared(string("Music Volume: ") + to_string( 200 | m_pResources->config()->meta("audio")->at("music-volume") 201 | ) + "%" 202 | ); 203 | m_OptionsMenu.options().emplace_back(m_pVolumeText, 204 | [this]{ 205 | }, 206 | [this](int ofs){ 207 | int old_v = m_pResources->config()->meta("audio")->at("volume"); 208 | int v = kit::clamp(old_v + ofs * 10, 0, 100); 209 | if(v!=old_v) { 210 | m_pResources->config()->meta("audio")->set("volume", v); 211 | *m_pVolumeText = string("Global Volume: ") + to_string(v) + "%"; 212 | Sound::play(m_pRoot.get(), "scroll.wav", m_pResources); 213 | return true; 214 | } 215 | return false; 216 | } 217 | ); 218 | m_OptionsMenu.options().emplace_back(m_pMusicText, 219 | [this]{ 220 | //Sound::play(m_pRoot.get(), "scroll.wav", m_pResources); 221 | }, 222 | [this](int ofs){ 223 | int old_v = m_pResources->config()->meta("audio")->at("music-volume"); 224 | int v = kit::clamp(old_v + ofs * 10, 0, 100); 225 | if(v!=old_v) { 226 | m_pResources->config()->meta("audio")->set("music-volume", v); 227 | *m_pMusicText = string("Music Volume: ") + to_string(v) + "%"; 228 | return true; 229 | } 230 | return false; 231 | }); 232 | m_OptionsMenu.options().emplace_back(m_pSoundText, 233 | [this]{ 234 | }, 235 | [this](int ofs){ 236 | int old_v = m_pResources->config()->meta("audio")->at("sound-volume"); 237 | int v = kit::clamp(old_v + ofs * 10, 0, 100); 238 | if(v!=old_v) { 239 | m_pResources->config()->meta("audio")->set("sound-volume", v); 240 | *m_pSoundText = string("Sound Volume: ") + to_string(v) + "%"; 241 | Sound::play(m_pRoot.get(), "scroll.wav", m_pResources); 242 | return true; 243 | } 244 | return false; 245 | }); 246 | m_OptionsMenu.options().emplace_back("Customize Controls", [this]{ 247 | m_MenuContext.push(&m_ControlsMenu); 248 | }); 249 | m_OptionsMenu.options().emplace_back( 250 | "Back", 251 | [this]{ 252 | m_pQor->save_settings(); 253 | m_MenuContext.pop(); 254 | }, 255 | std::function(), // no adjust 256 | string(), // no desc 257 | Menu::Option::BACK 258 | ); 259 | 260 | init_controls_menu(); 261 | 262 | m_MenuContext.clear(&m_MainMenu); 263 | m_pRoot->add(m_pMenuGUI); 264 | 265 | on_tick.connect(std::move(screen_fader( 266 | [this](Freq::Time, float fade) { 267 | m_Fade = fade; 268 | float v = m_pResources->config()->meta("audio")->at("volume") / 100.0f; 269 | v *= m_pResources->config()->meta("audio")->at("music-volume") / 100.0f; 270 | m_pMusic->source()->pitch = fade; 271 | m_pMusic->source()->gain = fade * v; 272 | m_Ambient = Color(fade); 273 | }, 274 | [this](Freq::Time){ 275 | return !!m_pDone; 276 | }, 277 | [this](Freq::Time){ 278 | m_Ambient = Color::white(); 279 | if(m_pDone) 280 | (*m_pDone)(); 281 | else 282 | m_pQor->pop_state(); 283 | } 284 | ))); 285 | 286 | // set up gradual border animation 287 | { 288 | auto fade = make_shared>(); 289 | 290 | fade->stop(vec2(0.0f, 0.0f)); 291 | fade->frame(Frame( 292 | vec2(sw / 16.0f, sh / 16.0f), 293 | Freq::Time::seconds(2.0f), 294 | INTERPOLATE(out_sine) 295 | )); 296 | } 297 | 298 | m_MenuContext.on_stack_empty.connect([this]{ 299 | m_pDone = make_shared>([this]{ 300 | m_pQor->pop_state(); 301 | }); 302 | }); 303 | } 304 | 305 | MenuState :: ~MenuState() 306 | { 307 | m_pPipeline->partitioner()->clear(); 308 | } 309 | 310 | void MenuState :: init_controls_menu() 311 | { 312 | shared_ptr binds; 313 | TRY(binds = m_pQor->session()->profile(0)->config()-> 314 | meta("input")->meta("binds") 315 | ); 316 | 317 | if(binds) 318 | { 319 | for(auto&& bind: *binds) 320 | { 321 | try{ 322 | // individual action -> key 323 | m_Binds[bind.as()].push_back(bind.key); 324 | }catch(const boost::bad_any_cast&){ 325 | // many actions -> one key 326 | auto bind_list = bind.as>(); 327 | for(auto&& key: *bind_list) 328 | m_Binds[key.as()].push_back(bind.key); 329 | } 330 | } 331 | 332 | // TODO: add empty binds for buttons not found 333 | 334 | for(auto&& bind: m_Binds) 335 | { 336 | string action = bind.first; 337 | vector keys = bind.second; 338 | auto text = make_shared( 339 | boost::to_upper_copy(action) + ": " + 340 | boost::algorithm::join(keys, ", ") 341 | ); 342 | m_ControlsMenu.options().emplace_back( 343 | text, 344 | [this, action, text]{ 345 | *text = action + ": ..."; 346 | m_pMenuGUI->visible(false); 347 | } 348 | ); 349 | } 350 | } 351 | 352 | m_ControlsMenu.options().emplace_back( 353 | "Back", 354 | [this]{ 355 | m_pQor->save_settings(); 356 | m_MenuContext.pop(); 357 | }, 358 | std::function(), // no adjust 359 | string(), // no desc 360 | Menu::Option::BACK 361 | ); 362 | } 363 | 364 | void MenuState :: logic(Freq::Time t) 365 | { 366 | Actuation::logic(t); 367 | m_pPipeline->logic(t); 368 | if(m_pInput->key(SDLK_F10)) 369 | m_pQor->quit(); 370 | 371 | //auto cairo = m_pCanvas->context(); 372 | 373 | //// clear 374 | //cairo->save(); 375 | //cairo->set_operator(Cairo::OPERATOR_CLEAR); 376 | //cairo->paint(); 377 | //cairo->restore(); 378 | 379 | //cairo->set_source_rgba(1.0, 1.0, 1.0, 0.5); 380 | ////cairo->select_font_face("Gentium Book Basic", Cairo::FONT_SLANT_NORMAL, 381 | //cairo->select_font_face( 382 | // //"Slackey", 383 | // "Press Start", 384 | // Cairo::FONT_SLANT_NORMAL, 385 | // Cairo::FONT_WEIGHT_NORMAL 386 | //); 387 | //m_pCanvas->dirty(true); 388 | 389 | //auto mr = m_pInput->mouse_rel(); 390 | //if(!floatcmp(length(mr), 0.0f)){ 391 | // m_TextOffset = mr * 0.1f; 392 | // const float max_offset = 1.0f; 393 | // if(length(m_TextOffset) > max_offset) 394 | // m_TextOffset = normalize(m_TextOffset) * max_offset; 395 | // m_TextOffset += vec2(1.0f, 1.0f); 396 | //} 397 | 398 | //m_TextOffset = vec2(m_Fade); 399 | 400 | m_pRoot->logic(t); 401 | } 402 | 403 | void MenuState :: render() const 404 | { 405 | //m_pScript->execute_string(( 406 | // boost::format("render()") 407 | //).str()); 408 | m_pPipeline->render(m_pRoot.get(), m_pCamera.get()); 409 | } 410 | 411 | -------------------------------------------------------------------------------- /src/MenuState.h: -------------------------------------------------------------------------------- 1 | #ifndef _MENUSTATE_H_VZ3QNB09 2 | #define _MENUSTATE_H_VZ3QNB09 3 | 4 | #include "Qor/State.h" 5 | #include "Qor/Input.h" 6 | #include "Qor/TileMap.h" 7 | #include "Qor/Camera.h" 8 | #include "Qor/Pipeline.h" 9 | #include "Qor/Mesh.h" 10 | #include "Qor/Interpreter.h" 11 | #include "Qor/Physics.h" 12 | #include "Qor/Canvas.h" 13 | //#include "Qor/BasicPhysics.h" 14 | #include "Qor/Sprite.h" 15 | #include "Qor/PlayerInterface2D.h" 16 | #include "Qor/Sound.h" 17 | #include "Qor/Scene.h" 18 | #include "Qor/Profile.h" 19 | #include "Qor/Menu.h" 20 | 21 | class Qor; 22 | 23 | class MenuState: 24 | public State 25 | { 26 | public: 27 | 28 | MenuState(Qor* engine); 29 | virtual ~MenuState(); 30 | 31 | virtual void preload() override; 32 | //virtual void start() override; 33 | virtual void enter() override; 34 | virtual void logic(Freq::Time t) override; 35 | virtual void render() const override; 36 | virtual bool needs_load() const override { 37 | return true; 38 | } 39 | 40 | virtual Pipeline* pipeline() { 41 | return m_pPipeline; 42 | } 43 | virtual const Pipeline* pipeline() const { 44 | return m_pPipeline; 45 | } 46 | 47 | virtual std::shared_ptr root() override { 48 | return m_pRoot; 49 | } 50 | virtual std::shared_ptr root() const override { 51 | return m_pRoot; 52 | } 53 | 54 | virtual std::shared_ptr camera() override { 55 | return m_pCamera; 56 | } 57 | virtual std::shared_ptr camera() const override { 58 | return m_pCamera; 59 | } 60 | 61 | virtual void camera(const std::shared_ptr& camera)override{ 62 | m_pCamera = std::dynamic_pointer_cast(camera); 63 | } 64 | 65 | void init_controls_menu(); 66 | 67 | private: 68 | 69 | Qor* m_pQor = nullptr; 70 | Input* m_pInput = nullptr; 71 | Pipeline* m_pPipeline = nullptr; 72 | Cache* m_pResources = nullptr; 73 | 74 | std::shared_ptr m_pRoot; 75 | //Interpreter* m_pInterpreter; 76 | //std::shared_ptr m_pScript; 77 | std::shared_ptr m_pCamera; 78 | //std::shared_ptr m_pPhysics; 79 | 80 | std::string m_Filename; 81 | std::shared_ptr m_pMusic; 82 | //std::shared_ptr m_pScene; 83 | std::shared_ptr m_pCanvas; 84 | glm::vec2 m_WrapAccum; 85 | 86 | Profile* m_pProfile = nullptr; 87 | 88 | glm::vec2 m_TextOffset; 89 | 90 | std::shared_ptr m_pMenuGUI; 91 | MenuContext m_MenuContext; 92 | Menu m_MainMenu; 93 | Menu m_OptionsMenu; 94 | Menu m_ControlsMenu; 95 | 96 | kit::reactive m_Ambient; 97 | 98 | float m_Fade = 0.0f; 99 | std::shared_ptr> m_pDone; 100 | std::shared_ptr m_pVolumeText; 101 | std::shared_ptr m_pSoundText; 102 | std::shared_ptr m_pMusicText; 103 | 104 | std::map> m_Binds; 105 | }; 106 | 107 | #endif 108 | 109 | -------------------------------------------------------------------------------- /src/QorpseState.cpp: -------------------------------------------------------------------------------- 1 | #include "QorpseState.h" 2 | #include "Qor/Input.h" 3 | #include "Qor/Qor.h" 4 | #include "Qor/TileMap.h" 5 | #include "Qor/Sound.h" 6 | #include "Qor/Sprite.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "Qor/BasicPartitioner.h" 12 | #include "World.h" 13 | #include "Thing.h" 14 | using namespace std; 15 | using namespace glm; 16 | 17 | QorpseState :: QorpseState( 18 | Qor* engine 19 | //std::string fn 20 | ): 21 | m_pQor(engine), 22 | m_pInput(engine->input()), 23 | m_pPipeline(engine->pipeline()) 24 | { 25 | } 26 | 27 | QorpseState :: ~QorpseState() 28 | { 29 | m_Ambient = Color::white(); // reset shader 30 | m_pRoot = nullptr; 31 | m_pPipeline->partitioner()->clear(); 32 | } 33 | 34 | void QorpseState :: preload() 35 | { 36 | float sw = m_pQor->window()->size().x; 37 | float sh = m_pQor->window()->size().y; 38 | 39 | m_pPartitioner = m_pPipeline->partitioner(); 40 | m_pRoot = make_shared(m_pQor, m_pPartitioner); 41 | m_pCamera = make_shared(m_pQor->resources(), m_pQor->window()); 42 | m_pRoot->add(m_pCamera); 43 | 44 | //auto num = rand() % 2 + 1; 45 | //m_pMusic = m_pQor->make(string("sh_tribute")+to_string(num)+".ogg"); 46 | //m_pMusic = m_pQor->make("boss_dead_find_exit.ogg"); 47 | m_pMusic = m_pQor->make("ingame1.ogg"); 48 | m_pRoot->add(m_pMusic); 49 | 50 | m_pPlayer = make_shared( 51 | m_pQor->session(), 52 | m_pQor->session()->profile(0).get(), 53 | m_pQor->resource_path("actor.json"), 54 | m_pQor->resources(), 55 | vec3(0.0f) 56 | //vec3(500.0f, 128.0f, 10.0f + 2.0f) 57 | ); 58 | m_pPlayer->on_death([this](const bool& b){ 59 | m_pMusic->detach(); 60 | m_pMusic = m_pQor->make("rust_ambient.ogg"); 61 | Sound::play(m_pRoot.get(), m_pPlayer->skin() + "-death.wav", m_pQor->resources()); 62 | m_pRoot->add(m_pMusic); 63 | m_pMusic->play(); 64 | // fade out 65 | auto fade = make_shared(0.0f); 66 | on_tick.connect([this, fade](Freq::Time t){ 67 | const float inc = t.s() / 5.0f; 68 | *fade += inc; // fade seconds 69 | m_Ambient = m_Ambient * Color(1.0f - *fade); 70 | auto scale = 300.0f / std::max( 71 | m_pQor->window()->size().x * 1.0f,1.0f 72 | ); 73 | scale *= 1.0f + *fade; 74 | m_pCamera->track(nullptr); 75 | m_pCamera->rescale(glm::vec3( 76 | scale, scale, 77 | 1.0f 78 | )); 79 | if(*fade > 1.0f) 80 | m_pQor->change_state(m_pQor->states().class_id("menu")); 81 | }); 82 | }); 83 | if(m_pRoot->map()) 84 | m_pPlayer->mesh()->set_geometry(m_pRoot->map()->tilted_tile_geometry()); 85 | 86 | m_pPlayer->add_camera(m_pCamera); 87 | m_pHUD = make_shared( 88 | m_pQor->window(), 89 | m_pPlayer 90 | ); 91 | m_pHUD->move(vec3(0.0f, 0.0f, 55.0f)); 92 | m_pCamera->add(m_pHUD); 93 | m_pHUD->each([](Node* n){ 94 | n->layer(World::INDICATOR); 95 | }, Node::Each::RECURSIVE | Node::Each::INCLUDE_SELF); 96 | 97 | //m_pPlayer->scale(2.0f); 98 | //m_pRoot->rescale(glm::vec3(4.0f, 4.0f, 1.0f)); 99 | 100 | m_pPlayerInterface = make_shared( 101 | m_pQor->session()->profile(0)->controller(), 102 | m_pPlayer, 103 | m_pRoot.get(), // world 104 | m_pQor->resources() 105 | ); 106 | m_pPlayerInterface->plug(); 107 | 108 | // update direction, this is not automatic yet until we 109 | // make a custom PlayerInteface2D 110 | //{ 111 | // auto playerptr = m_pPlayer.get(); 112 | // auto playerint = m_pPlayerInterface.get(); 113 | // m_pPlayer->on_tick.connect([playerptr, playerint](Freq::Time){ 114 | // playerptr->set_direction(playerint->direction()); 115 | // }); 116 | //} 117 | 118 | auto scale = 300.0f / std::max(sw * 1.0f,1.0f); 119 | m_pCamera->rescale(glm::vec3( 120 | scale, scale, 121 | 1.0f 122 | )); 123 | m_pCamera->mode(Tracker::FOLLOW); 124 | m_pCamera->track(m_pPlayerInterface->crosshair()->mesh()->as_node()); 125 | m_pCamera->focal_offset(vec3( 126 | -m_pQor->window()->center().x * 1.0f, 127 | -m_pQor->window()->center().y * 1.0f, 128 | 0.0f 129 | )); 130 | m_pCamera->listen(true); 131 | m_pRoot->setup_camera(m_pCamera.get()); 132 | m_pRoot->spawn_player(m_pPlayer); 133 | 134 | m_Ambient.on_change.connect([this](const Color& c){ 135 | int u = m_pPipeline->shader(1)->uniform("Ambient"); 136 | float fade = m_Thunder.get(); 137 | if(u != -1) 138 | m_pPipeline->shader(1)->uniform(u, vec4(c.vec3() * fade, c.a())); 139 | }); 140 | 141 | m_pRain = m_pQor->make("rain.ogg"); 142 | m_pCamera->add(m_pRain); 143 | 144 | m_pTextScroller = make_shared( 145 | m_pQor->window(), 146 | m_pQor->session()->profile(0)->controller().get(), 147 | "Press Start 2P", 148 | m_pQor->resources(), 149 | TextScroller::TIMED 150 | ); 151 | m_pCamera->add(m_pTextScroller); 152 | m_pTextScroller->each([](Node* n){ 153 | n->layer(World::INDICATOR); 154 | }, Node::Each::RECURSIVE | Node::Each::INCLUDE_SELF); 155 | 156 | m_pTextScroller->move(vec3(0.0f, 0.0f, 55.0f)); 157 | m_pTextScroller->write("portrait_grampire.png", "Where is everyone?"); 158 | 159 | m_pPlayer->on_speak([this](const std::string& s){ 160 | m_pTextScroller->write("portrait_grampire.png", s); 161 | }); 162 | 163 | auto bg = make_shared( 164 | make_shared(Prefab::quad(vec2(sw, sh), vec2(0.0, 0.0))), 165 | vector>{ 166 | make_shared(Prefab::quad_wrap()) 167 | }, 168 | make_shared("rain.png", m_pQor->resources()) 169 | ); 170 | //auto bg2 = bg->instance(); 171 | bg->position(vec3(0.0f,0.0f,50.0f)); 172 | m_pCamera->add(bg); 173 | bg->each([](Node* n){ 174 | n->layer(World::WEATHER); // weather effect layer 175 | }, Node::Each::RECURSIVE | Node::Each::INCLUDE_SELF); 176 | 177 | on_tick.connect([this, bg](Freq::Time t){ 178 | m_WrapAccum.x += t.seconds() * 0.5f; 179 | m_WrapAccum.y -= t.seconds() * 0.5f; 180 | 181 | m_WrapAccum.x = std::fmod(m_WrapAccum.x, 1.0f); 182 | m_WrapAccum.y = std::fmod(m_WrapAccum.y, 1.0f); 183 | 184 | // TODO: offset this by camera world position 185 | 186 | bg->swap_modifier(0, make_shared(Prefab::quad_wrap( 187 | vec2(0.0f, 0.0f), vec2(1.0f, 1.0f), 188 | vec2(1.6f, 0.9f), //scale 189 | vec2(m_WrapAccum.x * 1.0f, m_WrapAccum.y * 1.0f) //offset 190 | ))); 191 | }); 192 | m_pThunderSound = m_pQor->make("thunder.wav"); 193 | m_pCamera->add(m_pThunderSound); 194 | on_tick.connect([this](Freq::Time t){ 195 | m_Thunder.logic(t); 196 | 197 | float fade = m_Thunder.get(); 198 | int fadev = m_pPipeline->shader(1)->uniform("Ambient"); 199 | //vec3 randv( 200 | // //(rand() % 200) / 1000.f, 201 | // //(rand() % 200) / 1000.f, 202 | // (rand() % 100) / 1000.f 203 | //); 204 | if(fadev != -1) 205 | m_pPipeline->shader(1)->uniform( 206 | fadev, 207 | vec4(m_Ambient.get().vec3() * fade/* + randv*/, 1.0f) 208 | ); 209 | }); 210 | 211 | m_pRoot->cache(); 212 | m_pPartitioner->preload(); 213 | } 214 | 215 | void QorpseState :: enter() 216 | { 217 | float sw = m_pQor->window()->size().x; 218 | float sh = m_pQor->window()->size().y; 219 | 220 | m_pPipeline->blend(false); 221 | 222 | m_pCamera->ortho(); 223 | m_pPipeline->winding(true); 224 | 225 | m_pCamera->focus_time(Freq::Time::seconds(0.0)); 226 | m_pCamera->finish(); 227 | m_pCamera->focus_time(Freq::Time::seconds(0.5)); 228 | 229 | if(m_pMusic) 230 | m_pMusic->play(); 231 | 232 | m_pRain->play(); 233 | 234 | reset_thunder(); 235 | m_Ambient = Color(0.6f, 0.5f, 0.6f); 236 | } 237 | 238 | void QorpseState :: reset_thunder() 239 | { 240 | m_Thunder.stop(1.0f); 241 | m_Thunder.frame(Frame( 242 | 1.0f, Freq::Time::seconds(2), 243 | INTERPOLATE(linear)) 244 | ); 245 | m_Thunder.frame(Frame( 246 | 5.0f, Freq::Time::ms(20), 247 | INTERPOLATE(in_sine),[this]{ 248 | m_pThunderSound->source()->stop(); 249 | m_pThunderSound->play(); 250 | } 251 | )); 252 | m_Thunder.frame(Frame(1.0f, 253 | Freq::Time::ms(200), 254 | INTERPOLATE(out_sine) 255 | )); 256 | m_Thunder.frame(Frame( 257 | 1.0f, Freq::Time::seconds(2 + (rand() % 4) * 2), 258 | INTERPOLATE(linear), 259 | std::bind(&QorpseState::reset_thunder, this) 260 | )); 261 | } 262 | 263 | void QorpseState :: logic(Freq::Time t) 264 | { 265 | Actuation::logic(t); 266 | 267 | //auto pp = m_pPlayerInterface->crosshair()->position(Space::WORLD); 268 | //LOGf("player: (%s, %s, %s)", 269 | // pp.x % 270 | // pp.y % 271 | // pp.z 272 | //); 273 | 274 | m_pPlayer->set_direction(m_pPlayerInterface->direction()); 275 | //if(m_pPlayer->on_dead()) 276 | // m_pQor->change_state(m_pQor->states().class_id("menu")); 277 | 278 | if(m_pInput->key(SDLK_ESCAPE)) 279 | m_pQor->quit(); 280 | 281 | m_pRoot->logic(t); 282 | //m_pPipeline->logic(t); 283 | } 284 | 285 | void QorpseState :: render() const 286 | { 287 | m_pPipeline->render(m_pRoot.get(), m_pCamera.get()); 288 | } 289 | 290 | -------------------------------------------------------------------------------- /src/QorpseState.h: -------------------------------------------------------------------------------- 1 | #ifndef _POINTCLICKSTATE_H_VZ3QNB09 2 | #define _POINTCLICKSTATE_H_VZ3QNB09 3 | 4 | #include "Qor/State.h" 5 | #include "Qor/Input.h" 6 | #include "Qor/TileMap.h" 7 | #include "Qor/Camera.h" 8 | #include "Qor/Pipeline.h" 9 | #include "Qor/Mesh.h" 10 | #include "Qor/Interpreter.h" 11 | #include "Qor/Physics.h" 12 | #include "Qor/ViewModel.h" 13 | //#include "Qor/BasicPhysics.h" 14 | #include "Qor/Sprite.h" 15 | #include "CharacterInterface.h" 16 | //#include "Qor/PlayerInterface3D.h" 17 | #include "TextScroller.h" 18 | class BasicPartitioner; 19 | #include "HUD.h" 20 | #include "Character.h" 21 | #include "World.h" 22 | 23 | class Qor; 24 | 25 | class QorpseState: 26 | public State 27 | { 28 | public: 29 | 30 | QorpseState(Qor* engine); 31 | virtual ~QorpseState(); 32 | 33 | virtual void preload() override; 34 | virtual void enter() override; 35 | virtual void logic(Freq::Time t) override; 36 | virtual void render() const override; 37 | virtual bool needs_load() const override { 38 | return true; 39 | } 40 | 41 | virtual Pipeline* pipeline() { 42 | return m_pPipeline; 43 | } 44 | virtual const Pipeline* pipeline() const { 45 | return m_pPipeline; 46 | } 47 | 48 | virtual std::shared_ptr root() override { 49 | return m_pRoot; 50 | } 51 | virtual std::shared_ptr root() const override { 52 | return m_pRoot; 53 | } 54 | 55 | virtual std::shared_ptr camera() override { 56 | return m_pCamera; 57 | } 58 | virtual std::shared_ptr camera() const override { 59 | return m_pCamera; 60 | } 61 | 62 | virtual void camera(const std::shared_ptr& camera)override{ 63 | m_pCamera = std::dynamic_pointer_cast(camera); 64 | } 65 | 66 | void reset_thunder(); 67 | 68 | private: 69 | 70 | Qor* m_pQor = nullptr; 71 | Input* m_pInput = nullptr; 72 | Pipeline* m_pPipeline = nullptr; 73 | 74 | std::shared_ptr m_pRoot; 75 | //Interpreter* m_pInterpreter; 76 | //std::shared_ptr m_pScript; 77 | std::shared_ptr m_pCamera; 78 | std::shared_ptr m_pGun; 79 | std::shared_ptr m_pViewModel; 80 | //std::shared_ptr m_pMap; 81 | std::shared_ptr m_pPlayer; 82 | std::shared_ptr m_pMusic; 83 | BasicPartitioner* m_pPartitioner = nullptr; 84 | 85 | std::shared_ptr m_pPlayerInterface; 86 | //std::shared_ptr m_pPlayerInterface; 87 | std::shared_ptr m_pTextScroller; 88 | std::shared_ptr m_pHUD; 89 | //std::shared_ptr m_pPhysics; 90 | 91 | // TODO: move to world or scene 92 | glm::vec2 m_WrapAccum; 93 | Animation m_Thunder; 94 | std::shared_ptr m_pThunderSound; 95 | 96 | kit::reactive m_Ambient; 97 | 98 | std::string m_Filename; 99 | std::shared_ptr m_pRain; 100 | 101 | // Testing level or writing to profile? 102 | bool m_bTestMode = false; 103 | }; 104 | 105 | #endif 106 | 107 | -------------------------------------------------------------------------------- /src/TextScroller.cpp: -------------------------------------------------------------------------------- 1 | #include "TextScroller.h" 2 | using namespace std; 3 | using namespace glm; 4 | 5 | TextScroller :: TextScroller( 6 | Window* window, 7 | Controller* ctrl, 8 | std::string font, 9 | Cache* resources, 10 | Mode mode 11 | ): 12 | m_pWindow(window), 13 | m_pController(ctrl), 14 | m_Font(font), 15 | m_Mode(mode), 16 | m_AutoSkip(&m_Timer), 17 | m_pResources(resources), 18 | m_Height(window->size().y / 6.0f) 19 | { 20 | auto sw = m_pWindow->size().x; 21 | auto sh = m_pWindow->size().y; 22 | auto dh = m_Height; 23 | m_fActiveY = 0.0f; 24 | m_fInactiveY = -dh; 25 | m_Drop.stop(m_fInactiveY); 26 | 27 | vec2 margin = vec2( 28 | m_pWindow->size().x / 32.0f, 29 | m_pWindow->size().y / 32.0f 30 | ); 31 | 32 | m_pCanvas = make_shared( 33 | m_pWindow->size().x, 34 | sh / 6.0f 35 | ); 36 | add(m_pCanvas); 37 | m_pTextCanvas = make_shared( 38 | m_pWindow->size().x + margin.x, 39 | sh / 6.0f 40 | ); 41 | add(m_pTextCanvas); 42 | add(m_pScrollSound = make_shared("scroll.wav", m_pResources)); 43 | m_pScrollSound->source()->flags |= Audio::Source::F_LOOP; 44 | m_pTextCanvas->context()->move_to(margin.x, margin.y); 45 | auto layout = m_pTextCanvas->layout(); 46 | layout->set_width((m_pWindow->size().x - margin.x * 2.0f) * Pango::SCALE); 47 | 48 | //m_Sounds["select"] = make_shared("select.wav", resources); 49 | //for(auto&& snd: m_Sounds) 50 | // add(snd.second); 51 | } 52 | 53 | void TextScroller :: logic_self(Freq::Time t) 54 | { 55 | m_Drop.logic(t); 56 | m_Timer.logic(t); 57 | m_AutoSkip.poll(); 58 | 59 | if(m_Drop.get() > m_fInactiveY + K_EPSILON) { 60 | // clear black 61 | auto cairo = m_pCanvas->context(); 62 | cairo->set_source_rgb(0.0f, 0.0f, 0.0f); 63 | cairo->paint(); 64 | 65 | // clear transparent 66 | auto ctext = m_pTextCanvas->context(); 67 | ctext->save(); 68 | ctext->set_operator(Cairo::OPERATOR_SOURCE); 69 | //ctext->set_source_rgba(1.0f, 0.0f, 1.0f, 1.0f); 70 | ctext->set_source_rgba(0.0f, 0.0f, 0.0f, 1.0f); 71 | ctext->paint(); 72 | ctext->restore(); 73 | 74 | if(m_Drop.elapsed() && !m_Messages.empty()) { 75 | if(m_AutoSkip.fraction() < 0.25f){ 76 | if(not m_pScrollSound->source()->playing()) 77 | m_pScrollSound->play(); 78 | }else 79 | m_pScrollSound->source()->stop(); 80 | 81 | auto layout = m_pTextCanvas->layout(); 82 | layout->set_wrap(Pango::WRAP_WORD); 83 | 84 | layout->set_text(m_Messages.front().msg.substr( 85 | 0, 86 | kit::saturate( 87 | m_AutoSkip.fraction()*4.0f 88 | ) * m_Messages.front().msg.size() 89 | )); 90 | auto fontdesc = Pango::FontDescription(( 91 | boost::format("%s %s") % m_Font % 92 | kit::round_int(m_pCanvas->size().y / 6.0f) 93 | ).str()); 94 | layout->set_font_description(fontdesc); 95 | ctext->set_source_rgba(1.0, 1.0, 1.0, 0.75); 96 | layout->show_in_cairo_context(ctext); 97 | } 98 | 99 | if(!m_Messages.empty()) 100 | { 101 | bool advance = false; 102 | if(m_Mode == WAIT) 103 | advance = m_pController->button("select").consume(); 104 | //else if(m_Mode == TIMED) 105 | // advance = m_AutoSkip.elapsed(); 106 | 107 | if(advance) 108 | next_page(); 109 | } 110 | position(glm::vec3(0.0f, m_Drop.get(), position().z)); 111 | m_pCanvas->dirty(false); 112 | m_pTextCanvas->dirty(true); 113 | } 114 | } 115 | 116 | 117 | void TextScroller :: load_portrait() 118 | { 119 | auto msg = m_Messages.front(); 120 | vec2 margin = vec2( 121 | m_pWindow->size().x / 32.0f, 122 | m_pWindow->size().y / 32.0f 123 | ); 124 | if(m_PortraitName != msg.portrait) 125 | { 126 | // load new portrait 127 | m_PortraitName = msg.portrait; 128 | m_pPortraitNode = make_shared( 129 | msg.portrait, 130 | m_pResources 131 | ); 132 | float ratio = (m_pPortraitNode->size().x*1.0f) / (m_pPortraitNode->size().y*1.0f); 133 | //LOGf("ratio: %s", to_string(ratio)); 134 | m_pPortraitNode->size(glm::uvec2( 135 | kit::round_int(m_Height * ratio), m_Height 136 | )); 137 | m_pPortraitNode->offset_mesh(glm::vec2(0.0f)); 138 | m_pPortraitNode->move(glm::vec3(0.0f, 0.0f, 1.0f)); 139 | //m_pWindow->size().x - m_pPortraitNode->size().x, 140 | add(m_pPortraitNode); 141 | m_pPortraitNode->layer(layer()); // inherit this layer 142 | auto layout = m_pTextCanvas->layout(); 143 | m_pTextCanvas->context()->move_to( 144 | margin.x + m_pPortraitNode->size().x, margin.y 145 | ); 146 | layout->set_width(( 147 | m_pWindow->size().x - margin.x * 2.0f - m_pPortraitNode->size().x 148 | ) * Pango::SCALE); 149 | 150 | //m_pPortraitNode->swap_modifier( 151 | // m_pResources->cache_cast( 152 | // msg.portrait 153 | // ); 154 | //); 155 | } 156 | if(msg.portrait.empty()) 157 | m_pTextCanvas->context()->move_to(margin.x, margin.y); 158 | } 159 | 160 | void TextScroller :: next_page() 161 | { 162 | if(m_Messages.empty()) 163 | return; 164 | 165 | auto msg = m_Messages.front(); 166 | m_Messages.pop(); 167 | try{ 168 | msg.on_end(); 169 | }catch(const std::bad_function_call&){} 170 | //m_Sounds["select"]->source()->play(); 171 | 172 | if(m_Messages.empty()){ 173 | clear(); 174 | }else{ 175 | msg = m_Messages.front(); 176 | load_portrait(); 177 | if(msg.on_show) 178 | msg.on_show(); 179 | if(m_Mode == TIMED) { 180 | m_AutoSkip.set(m_AutoSkipTime); 181 | if(not m_Messages.empty()) 182 | m_AutoSkip.connect([this](){ 183 | next_page(); 184 | }); 185 | }else if(m_Mode == WAIT) { 186 | m_AutoSkip.set(m_AutoSkipTime); 187 | } 188 | } 189 | } 190 | 191 | void TextScroller :: write( 192 | std::string portrait, 193 | std::string msg, 194 | std::function show, 195 | std::function end, 196 | std::function tick 197 | 198 | ){ 199 | m_Messages.emplace(Message{ 200 | std::move(portrait), 201 | std::move(msg), 202 | std::move(show), 203 | std::move(end), 204 | std::move(tick) 205 | }); 206 | 207 | if(m_Messages.size() != 1) 208 | return; 209 | 210 | // animate down window 211 | m_Drop.abort(); 212 | m_Drop.frame(Frame( 213 | m_fActiveY, 214 | Freq::Time::seconds(1.0f), 215 | INTERPOLATE(in_sine), 216 | [this](){ 217 | load_portrait(); 218 | try{ 219 | m_Messages.front().on_show(); 220 | }catch(const std::bad_function_call&){} 221 | if(m_Mode == TIMED) { 222 | m_AutoSkip.set(m_AutoSkipTime); 223 | if(not m_Messages.empty()) 224 | m_AutoSkip.connect([this](){ 225 | next_page(); 226 | }); 227 | } else if(m_Mode == WAIT) { 228 | m_AutoSkip.set(m_AutoSkipTime); 229 | } 230 | } 231 | )); 232 | } 233 | 234 | void TextScroller :: clear() 235 | { 236 | kit::clear(m_Messages); 237 | m_Drop.abort(); 238 | m_Drop.frame(Frame( 239 | m_fInactiveY, 240 | Freq::Time::seconds(1.0f), 241 | INTERPOLATE(out_sine) 242 | )); 243 | } 244 | 245 | void TextScroller :: render_self(Pass* pass) const 246 | { 247 | } 248 | 249 | -------------------------------------------------------------------------------- /src/TextScroller.h: -------------------------------------------------------------------------------- 1 | #ifndef _TEXT_SCROLLER_H 2 | #define _TEXT_SCROLLER_H 3 | 4 | //#include "IRealtime.h" 5 | //#include "IRenderable.h" 6 | #include "Qor/Node.h" 7 | #include "Qor/Window.h" 8 | #include "Qor/Canvas.h" 9 | #include "Qor/Input.h" 10 | #include 11 | #include 12 | #include "Qor/kit/freq/animation.h" 13 | #include "Qor/Sound.h" 14 | #include "Qor/Sprite.h" 15 | 16 | class TextScroller: 17 | public Node 18 | //public IRealtime, 19 | //public IPipelineRenderable 20 | { 21 | public: 22 | 23 | enum Mode { 24 | WAIT = 0, 25 | TIMED 26 | }; 27 | 28 | TextScroller( 29 | Window* window, 30 | Controller* ctrl, 31 | std::string font, 32 | Cache* resources, 33 | Mode mode = WAIT 34 | ); 35 | virtual ~TextScroller () {} 36 | 37 | virtual void logic_self(Freq::Time) override; 38 | virtual void render_self(Pass* pass) const override; 39 | 40 | void write( 41 | std::string portrait, 42 | std::string msg, 43 | std::function show = 44 | std::function(), 45 | std::function end = 46 | std::function(), 47 | std::function tick = 48 | std::function() 49 | ); 50 | void write( 51 | std::string msg, 52 | std::function show = 53 | std::function(), 54 | std::function end = 55 | std::function(), 56 | std::function tick = 57 | std::function() 58 | ){ 59 | write("",msg,show,end,tick); 60 | } 61 | 62 | void clear(); 63 | 64 | bool empty() const { 65 | return m_Messages.empty(); 66 | } 67 | operator bool() const { 68 | return !m_Messages.empty(); 69 | } 70 | 71 | boost::signals2::signal on_clear; 72 | 73 | void next_page(); 74 | void load_portrait(); 75 | 76 | bool active() const { 77 | return not m_Messages.empty(); 78 | } 79 | 80 | private: 81 | 82 | Window* m_pWindow = nullptr; 83 | Controller* m_pController = nullptr; 84 | std::shared_ptr m_pCanvas; 85 | std::shared_ptr m_pTextCanvas; 86 | Cache* m_pResources; 87 | 88 | struct Message { 89 | std::string portrait; 90 | std::string msg; 91 | std::function on_show; 92 | std::function on_end; 93 | std::function on_tick; 94 | }; 95 | std::queue m_Messages; 96 | 97 | Mode m_Mode = WAIT; 98 | Freq::Alarm m_AutoSkip; 99 | Freq::Time m_AutoSkipTime = Freq::Time::seconds(3); 100 | Freq::Timeline m_Timer; 101 | 102 | bool m_bActive = false; 103 | 104 | Animation m_Drop; 105 | float m_fInactiveY = 0.0f; 106 | float m_fActiveY = 0.0f; 107 | int m_Height; 108 | //std::map> m_Portraits; 109 | std::shared_ptr m_pPortraitNode; 110 | std::shared_ptr m_pScrollSound; 111 | std::string m_PortraitName; 112 | 113 | std::string m_Font; 114 | 115 | //std::unordered_map> m_Sounds; 116 | }; 117 | 118 | #endif 119 | 120 | -------------------------------------------------------------------------------- /src/Thing.cpp: -------------------------------------------------------------------------------- 1 | #include "Thing.h" 2 | #include "World.h" 3 | #include "Qor/Filesystem.h" 4 | #include "Qor/Sound.h" 5 | #include 6 | using namespace std; 7 | 8 | const std::vector Thing :: s_TypeNames({ 9 | "", 10 | 11 | // monsters 12 | "raven", 13 | "skeleton", 14 | "zombie", 15 | "ghost", 16 | "meat_fly", 17 | "bighead", 18 | "severed_head", 19 | "mask_kid", 20 | "hooded_priest", 21 | "bernie", 22 | "fetus_maximus", 23 | 24 | // items 25 | "medkit", 26 | "key", 27 | "lock", 28 | "rifle", 29 | "grenade_launcher", 30 | "flamethrower", 31 | "shotgun", 32 | "bazooka", 33 | "uzis", 34 | "minigun", 35 | }); 36 | 37 | Thing :: Thing( 38 | const std::shared_ptr& config, 39 | MapTile* placeholder, 40 | World* world, 41 | TileMap* map, 42 | BasicPartitioner* partitioner, 43 | Cache* resources 44 | ): 45 | Node(config), 46 | m_pPlaceholder(placeholder), 47 | m_pPartitioner(partitioner), 48 | m_pWorld(world), 49 | m_pMap(map), 50 | m_pResources(resources), 51 | m_Identity(config->at("name","")), 52 | m_ThingID(get_id(config)) 53 | { 54 | } 55 | 56 | //Thing :: Thing( 57 | // const std::string& fn, 58 | // Cache* resources 59 | //): 60 | // Node(fn), 61 | // m_Identity(Filesystem::getFileNameNoExt(fn)), 62 | // m_pResources(resources) 63 | //{ 64 | // on_add.connect([this]{ 65 | // init_thing(); 66 | // }); 67 | //} 68 | 69 | void Thing :: init_thing() 70 | { 71 | // precond: thing attached, placeholder is node parent 72 | assert(m_pPartitioner); 73 | 74 | //LOGf("?: %s", m_pConfig->serialize(MetaFormat::JSON)); 75 | //LOGf("this: %s", this); 76 | 77 | m_Box = m_pPlaceholder->box(); 78 | 79 | if(is_monster()) 80 | { 81 | TRY(m_pConfig->merge(make_shared( 82 | m_pResources->transform(m_Identity+".json") 83 | ))); 84 | m_HP = m_pConfig->at("hp",1); 85 | LOGf("hp: %s", m_HP); 86 | m_pSprite = make_shared( 87 | m_pResources->transform(m_Identity+".json"), 88 | m_pResources 89 | ); 90 | add(m_pSprite); 91 | m_pSprite->set_state(0); 92 | if(m_pPlaceholder->tile_layer()->depth() || m_pConfig->has("depth")) 93 | m_pSprite->mesh()->set_geometry(m_pMap->tilted_tile_geometry()); 94 | collapse(); // detach from placeholder 95 | rescale(glm::vec3(1.0f)); 96 | position(m_pPlaceholder->position(Space::WORLD)); // inherit placeholder pos 97 | // adding a sprite will spawn its center on 0,0... 98 | // so we offset 99 | move(glm::vec3( 100 | m_pSprite->origin().x * m_pSprite->size().x, 101 | m_pSprite->origin().y * m_pSprite->size().y, 102 | 0.0f 103 | )); 104 | //m_pPlaceholder->detach(); // don't want to invalidate iterator 105 | m_pPlaceholder->mesh()->visible(false); // remove placeholder 106 | m_pPartitioner->register_object(m_pSprite->mesh(), World::THING); 107 | m_Solid = true; 108 | } 109 | else if(m_ThingID == LOCK) 110 | { 111 | // on top of doors 112 | m_pPlaceholder->move(glm::vec3(0.0f, 0.0f, 0.1f)); 113 | m_pPartitioner->register_object(shared_from_this(), World::THING); 114 | m_Solid = true; 115 | } 116 | 117 | // setup behavior (based on identity) 118 | } 119 | 120 | void Thing :: orient(glm::vec3 vec) 121 | { 122 | float s = atan2(vec.y, vec.x) / K_TAU; 123 | auto frame = kit::round_int(4.0f * s); 124 | //LOGf("turn %s / frame %s", s % frame); 125 | if(frame==0) 126 | m_pSprite->set_state("right"); 127 | else if(frame==1) 128 | m_pSprite->set_state("down"); 129 | else if(frame==2 || frame==-2) 130 | m_pSprite->set_state("left"); 131 | else if(frame==-1) 132 | m_pSprite->set_state("up"); 133 | } 134 | 135 | void Thing :: sound(const std::string& fn) 136 | { 137 | Sound::play(this, fn, m_pResources); 138 | } 139 | 140 | void Thing :: chase(Node* node) 141 | { 142 | auto vec = node->position(Space::WORLD) - position(Space::WORLD); 143 | vec.z = 0.0f; 144 | velocity(glm::normalize(vec) * 50.0f); 145 | orient(vec); 146 | } 147 | 148 | void Thing :: setup_player(const std::shared_ptr& player) 149 | { 150 | m_bFirstPlayer = true; 151 | if(is_monster()){ 152 | weak_ptr chrwp(player); 153 | auto* playerptr = player.get(); 154 | function on_activate = [this](){ 155 | sound(m_Identity + ".wav"); 156 | }; 157 | function active_tick = [this](Freq::Time t){ 158 | // ... 159 | }; 160 | function inactive_tick = [this, chrwp, active_tick, on_activate](Freq::Time t){ 161 | auto chr = chrwp.lock(); 162 | if(not chr) 163 | return; // TODO: remove this from on_tick signal 164 | float dist = glm::distance(position(Space::WORLD), chr->position(Space::WORLD)); 165 | m_pTarget = static_pointer_cast(chr->as_node()); 166 | if(dist <= 200.0f){ 167 | chase(chr.get()); 168 | on_tick.sync([this, active_tick, on_activate]{ 169 | on_tick.clear(); 170 | on_tick.connect(std::move(active_tick)); 171 | on_activate(); 172 | }); 173 | } 174 | }; 175 | m_pPartitioner->on_collision( 176 | World::find_mask(player->mesh(), "mask"), m_pSprite->mesh(), 177 | function(), 178 | function(), 179 | [this,playerptr](Node* a, Node* b){ // on enter 180 | if(playerptr->alive()){ 181 | if(alive()) 182 | { 183 | playerptr->damage(25); 184 | sound(playerptr->skin() + "-hurt.wav"); 185 | kill(); 186 | }else{ 187 | sound("squash.wav"); 188 | } 189 | } 190 | } 191 | ); 192 | on_tick.connect(inactive_tick); 193 | 194 | } else if (m_ThingID == MEDKIT) { 195 | auto* playerptr = player.get(); 196 | assert(m_pPartitioner); 197 | m_pPartitioner->on_collision( 198 | World::find_mask(player->mesh(), "mask"), m_pPlaceholder->mesh(), 199 | [this,playerptr](Node* a, Node* b){ 200 | if(playerptr->heal()){ 201 | sound("medkit.wav"); 202 | m_pPlaceholder->detach(); 203 | } 204 | } 205 | ); 206 | } else if (m_ThingID == KEY) { 207 | auto* playerptr = player.get(); 208 | assert(m_pPartitioner); 209 | m_pPartitioner->on_collision( 210 | World::find_mask(player->mesh(), "mask"), m_pPlaceholder->mesh(), 211 | [this,playerptr](Node* a, Node* b){ 212 | if(playerptr->give("key")) { 213 | sound("key.wav"); 214 | playerptr->say("Picked up " + m_Identity + "."); 215 | m_pPlaceholder->detach(); 216 | } 217 | } 218 | ); 219 | } else if (is_weapon()) { 220 | auto* playerptr = player.get(); 221 | assert(m_pPartitioner); 222 | m_pPartitioner->on_collision( 223 | World::find_mask(player->mesh(), "mask"), m_pPlaceholder->mesh(), 224 | [this,playerptr](Node* a, Node* b){ 225 | if(playerptr->give(m_Identity)) { 226 | sound("reload.wav"); 227 | playerptr->say("Picked up " + m_Identity + "."); 228 | m_pPlaceholder->detach(); 229 | } 230 | } 231 | ); 232 | } 233 | } 234 | 235 | void Thing :: setup_map(const std::shared_ptr& map) 236 | { 237 | } 238 | 239 | void Thing :: setup_other(const std::shared_ptr& thing) 240 | { 241 | } 242 | 243 | //unsigned Thing :: get_id(std::string identity) 244 | //{ 245 | // return 1; 246 | //} 247 | 248 | unsigned Thing :: get_id(const std::shared_ptr& config) 249 | { 250 | string name = config->at("name",""); 251 | 252 | if(name.empty()) 253 | return INVALID_THING; 254 | auto itr = std::find(ENTIRE(s_TypeNames),name); 255 | if(itr == s_TypeNames.end()) 256 | return INVALID_THING; 257 | 258 | return std::distance(s_TypeNames.begin(), itr); 259 | } 260 | 261 | void Thing :: logic_self(Freq::Time t) 262 | { 263 | if(is_monster()) 264 | { 265 | if(m_Dying && not m_Dead) 266 | { 267 | m_Dead = true; 268 | 269 | // do death animation stuff here 270 | sound(m_Identity + "-death.wav"); 271 | try{ 272 | m_pSprite->set_state("death"); 273 | //m_pSprite->on_cycle_done_once([this]{ 274 | // detach(); 275 | //}); 276 | }catch(const std::out_of_range&){ 277 | } 278 | } 279 | } 280 | } 281 | 282 | void Thing :: cb_to_player(Node* player_node, Node* thing_node) 283 | { 284 | //LOG("cb_to_player"); 285 | shared_ptr player; 286 | 287 | player = dynamic_pointer_cast(player_node->parent()->parent()->as_node()); 288 | assert(player); 289 | auto thing = find_thing(thing_node); 290 | assert(thing); 291 | 292 | if(thing->id() == LOCK) 293 | { 294 | //LOG("locked door coll"); 295 | if(player->has("key")) 296 | return; // no collide 297 | } 298 | if(thing->solid() && not thing->is_monster()) 299 | thing->world()->cb_to_static(player_node, thing_node, player.get()); 300 | } 301 | 302 | void Thing :: cb_to_static(Node* thing_node, Node* static_node) 303 | { 304 | auto thing = find_thing(thing_node); 305 | assert(thing); 306 | if(thing->solid() || thing->is_monster()) 307 | { 308 | glm::vec3 pos_before = thing->position(Space::WORLD); 309 | thing->world()->cb_to_static(thing_node, static_node, thing.get()); 310 | glm::vec3 pos_after = thing->position(Space::WORLD); 311 | if(thing->velocity() != glm::vec3(0.0f)){ 312 | glm::vec3 vel = thing->velocity(); 313 | if(pos_after.x != pos_before.x){ 314 | vel.x = -vel.x; 315 | thing->velocity(vel); 316 | thing->orient(vel); 317 | } 318 | if(pos_after.y != pos_before.y){ 319 | vel.y = -vel.y; 320 | thing->velocity(vel); 321 | thing->orient(vel); 322 | } 323 | 324 | } 325 | } 326 | } 327 | 328 | std::shared_ptr Thing :: find_thing(Node* n) 329 | { 330 | shared_ptr thing; 331 | thing = dynamic_pointer_cast(n->as_node()); 332 | if(not thing){ 333 | thing = dynamic_pointer_cast(n->parent()->as_node()); 334 | if(not thing) 335 | thing = dynamic_pointer_cast(n->parent()->parent()->as_node()); 336 | } 337 | return thing; 338 | } 339 | 340 | void Thing :: gib(Node* bullet) 341 | { 342 | int gib_idx = rand() % 3; 343 | auto gib = make_shared("gib"+to_string(gib_idx+1)+".png", m_pResources); 344 | auto dir = Angle::degrees(1.0f * (std::rand() % 360)).vector(); 345 | add(gib); 346 | gib->move(glm::vec3(0.0f, 0.0f, 0.1f)); 347 | gib->velocity(glm::vec3(dir, 0.0f) * 15.0f); 348 | auto life = make_shared(0.25f); 349 | auto gibptr = gib.get(); 350 | gib->on_tick.connect([gibptr, life](Freq::Time t){ 351 | *life -= t.s(); 352 | if(*life < 0.0f) 353 | gibptr->detach(); 354 | }); 355 | } 356 | 357 | void Thing :: cb_to_bullet(Node* thing_node, Node* bullet_node) 358 | { 359 | Thing* thing = (Thing*)thing_node->parent()->parent(); 360 | Node* bullet = bullet_node->parent(); 361 | //a->parent()->parent()->detach(); 362 | //b->parent()->detach(); 363 | if(thing->is_monster() && thing->alive()) 364 | { 365 | int gib_count = rand() % 3 + 1; 366 | for(int i=0; i < gib_count; ++i) 367 | thing->gib(bullet); 368 | 369 | if(thing->damage(bullet->config()->at("damage",1))) 370 | { 371 | //if(not bullet->config()->at("penetration",false)) 372 | bullet->on_tick.connect([bullet](Freq::Time){ 373 | bullet->detach(); 374 | }); 375 | } 376 | } 377 | } 378 | 379 | World* Thing :: world() 380 | { 381 | return m_pWorld; 382 | } 383 | 384 | bool Thing :: damage(int dmg) 385 | { 386 | if(m_HP <= 0 || dmg < 0) 387 | return false; 388 | m_HP = std::max(m_HP-dmg, 0); 389 | if(m_HP <= 0){ 390 | m_Dying = true; 391 | velocity(glm::vec3(0.0f)); 392 | } 393 | return true; 394 | } 395 | 396 | -------------------------------------------------------------------------------- /src/Thing.h: -------------------------------------------------------------------------------- 1 | #ifndef THING_H_MPZJ6QOR 2 | #define THING_H_MPZJ6QOR 3 | 4 | #include 5 | #include "Qor/Sprite.h" 6 | #include "Character.h" 7 | #include "Qor/TileMap.h" 8 | #include "Qor/BasicPartitioner.h" 9 | class World; 10 | 11 | class Thing: 12 | public Node 13 | { 14 | public: 15 | 16 | Thing( 17 | const std::shared_ptr& config, 18 | MapTile* placeholder, 19 | World* world, 20 | TileMap* map, 21 | BasicPartitioner* partitioner, 22 | Cache* resources 23 | ); 24 | //Thing( 25 | // const std::string& fn, 26 | // Cache* resources 27 | //); 28 | //Thing( 29 | // const std::shared_ptr& cfg, 30 | // Cache* resources 31 | //); 32 | 33 | virtual ~Thing() {} 34 | 35 | virtual void logic_self(Freq::Time t) override; 36 | 37 | //void partitioner(BasicPartitioner* p){ 38 | // m_pPartitioner = p; 39 | //} 40 | void setup_player(const std::shared_ptr& player); 41 | void setup_map(const std::shared_ptr& map); 42 | void setup_other(const std::shared_ptr& thing); 43 | 44 | // set placeholder tile used as placeholder 45 | //void set_placeholder(MapTile* t) {m_pPlaceholder=t;} 46 | 47 | static unsigned get_id(const std::shared_ptr& config); 48 | static bool is_thing(std::string name); 49 | 50 | enum Type 51 | { 52 | INVALID_THING = 0, 53 | 54 | MONSTERS, 55 | RAVEN = MONSTERS, 56 | SKELETON, 57 | ZOMBIE, 58 | GHOST, 59 | MEAT_FLY, 60 | BIGHEAD, 61 | SEVERED_HEAD, 62 | MASKKID, 63 | HOODED_PRIEST, 64 | BERNIE, 65 | FETUS_MAXIMUS, 66 | MONSTERS_END, 67 | 68 | ITEMS = MONSTERS_END, 69 | MEDKIT = ITEMS, 70 | KEY, 71 | LOCK, 72 | 73 | WEAPONS, 74 | RIFLE = WEAPONS, 75 | GRENADE_LAUNCHER, 76 | FLAMETHROWER, 77 | SHOTGUN, 78 | BAZOOKA, 79 | UZIS, 80 | MINIGUN, 81 | 82 | WEAPONS_END, 83 | ITEMS_END = WEAPONS_END 84 | }; 85 | const static std::vector s_TypeNames; 86 | 87 | bool is_monster() const { 88 | return m_ThingID >= MONSTERS && m_ThingID < MONSTERS_END; 89 | } 90 | bool is_item() const { 91 | return m_ThingID >= ITEMS && m_ThingID < ITEMS_END; 92 | } 93 | bool is_weapon() const { 94 | return m_ThingID >= WEAPONS && m_ThingID < WEAPONS_END; 95 | } 96 | 97 | 98 | void init_thing(); 99 | void orient(glm::vec3 vec); 100 | 101 | void sound(const std::string& fn); 102 | void chase(Node* node); 103 | 104 | static void cb_to_player(Node* player_node, Node* thing_node); 105 | static void cb_to_static(Node* thing_node, Node* static_node); 106 | static void cb_to_bullet(Node* thing_node, Node* bullet_node); 107 | 108 | World* world(); 109 | 110 | void kill() 111 | { 112 | m_Dying = true; 113 | velocity(glm::vec3(0.0f)); 114 | } 115 | 116 | // ignore m_Dying when check alive 117 | bool alive() const { return not m_Dead; } 118 | 119 | bool damage(int dmg); 120 | 121 | void gib(Node* bullet); 122 | 123 | bool solid() const { return m_Solid; } 124 | unsigned id() const { return m_ThingID; } 125 | 126 | static std::shared_ptr find_thing(Node* n); 127 | 128 | private: 129 | 130 | int m_HP = 1; 131 | bool m_Dying = false; 132 | bool m_Dead = false; 133 | Cache* m_pResources = nullptr; 134 | std::weak_ptr m_pTarget; 135 | std::string m_Identity; 136 | unsigned m_ThingID = 0; 137 | bool m_bActive = false; 138 | bool m_bFirstPlayer = false; 139 | MapTile* m_pPlaceholder = nullptr; 140 | BasicPartitioner* m_pPartitioner = nullptr; 141 | World* m_pWorld = nullptr; 142 | TileMap* m_pMap = nullptr; 143 | 144 | bool m_Solid = false; 145 | 146 | std::shared_ptr m_pSprite; // optional for thing type 147 | 148 | }; 149 | 150 | #endif 151 | 152 | -------------------------------------------------------------------------------- /src/Weapon.h: -------------------------------------------------------------------------------- 1 | #ifndef WEAPON_H_V9PBVMZN 2 | #define WEAPON_H_V9PBVMZN 3 | 4 | #include 5 | #include "Qor/kit/freq/animation.h" 6 | #include "Qor/kit/meta/meta.h" 7 | #include "Qor/kit/cache/cache.h" 8 | 9 | class Weapon: 10 | public IRealtime 11 | { 12 | public: 13 | 14 | enum { 15 | DUAL = kit::bit(0) 16 | }; 17 | 18 | Weapon() = default; 19 | 20 | Weapon( 21 | std::string name, 22 | Cache* resources 23 | ){ 24 | //auto cfg = std::make_shared(resources->transform("weapons.json")); 25 | } 26 | Weapon( 27 | std::string name, 28 | std::string ammo_type, 29 | std::string bullet_gfx, 30 | std::shared_ptr ammo, 31 | int clip, 32 | int clip_size, 33 | int chamber_size, 34 | float range, 35 | float fire_speed, 36 | float reload_speed, 37 | float bullet_speed, 38 | float accuracy=1.0f, 39 | unsigned flags=0, 40 | int order = 0 41 | ): 42 | m_Name(name), 43 | m_AmmoType(ammo_type), 44 | m_BulletGFX(bullet_gfx), 45 | m_Clip(clip), 46 | m_ClipSize(clip_size), 47 | m_ChamberSize(chamber_size), 48 | m_Range(range), 49 | m_FireSpeed(fire_speed), 50 | m_ReloadSpeed(reload_speed), 51 | m_BulletSpeed(bullet_speed), 52 | m_Timeline(std::make_shared()), 53 | //m_FireDelay(Freq::Alarm(&m_Timeline)), 54 | //m_ReloadDelay(Freq::Alarm(&m_Timeline)), 55 | m_Ammo(ammo), 56 | m_Accuracy(accuracy), 57 | m_Flags(flags), 58 | m_Order(order) 59 | { 60 | m_FireDelay = Freq::Alarm(m_Timeline.get()); 61 | m_ReloadDelay = Freq::Alarm(m_Timeline.get()); 62 | m_FireDelay.set(Freq::Time::ms(0)); 63 | m_ReloadDelay.set(Freq::Time::ms(0)); 64 | } 65 | virtual ~Weapon() {} 66 | 67 | Weapon(const Weapon&) = default; 68 | Weapon(Weapon&&) = default; 69 | Weapon& operator=(const Weapon&) = default; 70 | Weapon& operator=(Weapon&&) = default; 71 | 72 | std::string name() const { 73 | return m_Name; 74 | } 75 | int ammo() const { 76 | return *m_Ammo; 77 | } 78 | std::string ammo_type() { 79 | return m_AmmoType; 80 | } 81 | void clip(int c) { 82 | m_Clip = c; 83 | } 84 | int clip() const { 85 | return m_Clip; 86 | } 87 | int clip_size() const { 88 | return m_ClipSize; 89 | } 90 | 91 | float range() const { 92 | return m_Range; 93 | } 94 | float bullet_speed() const { 95 | return m_BulletSpeed; 96 | } 97 | 98 | float accuracy() const { 99 | return m_Accuracy; 100 | } 101 | 102 | bool reload() { 103 | if(delayed()) 104 | return false; 105 | 106 | if(*m_Ammo) { 107 | auto ofs = std::min( 108 | std::min(*m_Ammo, m_ClipSize), 109 | m_ClipSize - m_Clip 110 | ); 111 | if(ofs) 112 | { 113 | *m_Ammo -= ofs; 114 | m_Clip += ofs; 115 | m_ReloadDelay.set(Freq::Time::seconds(1.0f / m_ReloadSpeed)); 116 | return true; 117 | } 118 | } 119 | return false; 120 | } 121 | 122 | bool delayed() const { 123 | //return false; 124 | return !m_FireDelay.elapsed() || !m_ReloadDelay.elapsed(); 125 | } 126 | 127 | bool can_fire() const { 128 | return !delayed() && m_Clip; 129 | } 130 | 131 | int shoot() 132 | { 133 | if(delayed()) 134 | return 0; 135 | 136 | if(m_Clip) 137 | { 138 | auto ofs = std::min(m_Clip, m_ChamberSize); 139 | m_Clip -= ofs; 140 | 141 | m_FireDelay.set(Freq::Time::seconds(1.0f / m_FireSpeed)); 142 | return ofs; 143 | } 144 | return 0; 145 | } 146 | 147 | virtual void logic(Freq::Time t) override { 148 | m_Timeline->logic(t); 149 | } 150 | 151 | std::string bullet_gfx() const{ 152 | return m_BulletGFX; 153 | } 154 | 155 | unsigned flags() const { 156 | return m_Flags; 157 | } 158 | 159 | private: 160 | 161 | std::string m_Name; 162 | std::string m_AmmoType; 163 | std::string m_BulletGFX; 164 | 165 | int m_ChamberSize = 1; 166 | int m_Clip = 1; 167 | int m_ClipSize = 1; 168 | int m_Order = 0; 169 | float m_Range = 1.0f; 170 | float m_FireSpeed = 1.0f; 171 | float m_ReloadSpeed = 1.0f; 172 | float m_BulletSpeed = 1.0f; 173 | float m_Accuracy = 1.0f; 174 | unsigned m_Flags = 0; 175 | 176 | std::shared_ptr m_Timeline; 177 | Freq::Alarm m_FireDelay; 178 | Freq::Alarm m_ReloadDelay; 179 | 180 | // common ammo type are shared with player inventory 181 | std::shared_ptr m_Ammo; 182 | }; 183 | 184 | #endif 185 | 186 | -------------------------------------------------------------------------------- /src/World.cpp: -------------------------------------------------------------------------------- 1 | #include "World.h" 2 | #include "Thing.h" 3 | #include "Qor/Sound.h" 4 | #include 5 | #include 6 | using namespace std; 7 | using namespace glm; 8 | namespace _ = std::placeholders; 9 | 10 | World :: World( 11 | Qor* qor, 12 | BasicPartitioner* partitioner 13 | ): 14 | m_pQor(qor), 15 | m_pResources(qor->resources()), 16 | m_pPartitioner(partitioner) 17 | { 18 | //m_pMap = m_pQor->make("modern.tmx"); 19 | // load map names 20 | auto cfg = make_shared(m_pResources->transform("maps.json")); 21 | 22 | // load first map 23 | m_pMap = m_pQor->make(cfg->meta("maps")->at(0) + ".tmx"); 24 | 25 | if(m_pMap) 26 | { 27 | add(m_pMap); 28 | 29 | vector>*> layer_types { 30 | &m_pMap->layers(), 31 | &m_pMap->object_layers() 32 | }; 33 | for(auto&& layers: layer_types) 34 | for(auto&& layer: *layers) 35 | { 36 | for(auto&& tile_ptr: layer->all_descendants()) 37 | { 38 | if(not tile_ptr) 39 | continue; 40 | auto tile = tile_ptr->as_node(); 41 | // read object properties and replace 42 | auto obj = std::dynamic_pointer_cast(tile); 43 | if(obj) 44 | { 45 | auto obj_cfg = obj->config(); 46 | obj->box() = obj->mesh()->box(); 47 | if(obj_cfg->at("name","")=="player_start") 48 | { 49 | obj->visible(false); 50 | obj->mesh()->visible(false); 51 | m_Spawns.push_back(obj.get()); 52 | continue; 53 | } 54 | else 55 | { 56 | if(Thing::get_id(obj_cfg)) 57 | { 58 | auto thing = make_shared( 59 | obj_cfg, 60 | obj.get(), 61 | this, 62 | m_pMap.get(), 63 | partitioner, 64 | qor->resources() 65 | ); 66 | obj->add(thing); 67 | //LOGf("mat: %s", Matrix::to_string(*obj->matrix())); 68 | ////obj->each([this](Node* n){ 69 | //// n->visible(false); 70 | ////}, Node::Each::RECURSIVE | Node::Each::INCLUDE_SELF); 71 | setup_thing(thing); 72 | continue; 73 | } 74 | } 75 | 76 | bool depth = layer->depth() || obj_cfg->has("depth"); 77 | if(depth) 78 | { 79 | auto n = make_shared(); 80 | n->name("mask"); 81 | auto mask = obj_cfg->at>("mask", shared_ptr()); 82 | bool hflip = obj->orientation() & (unsigned)MapTile::Orientation::H; 83 | bool vflip = obj->orientation() & (unsigned)MapTile::Orientation::V; 84 | if(obj_cfg->has("sidewall") && not mask) 85 | { 86 | hflip ^= obj_cfg->at("sidewall","")=="right"; 87 | mask = make_shared(); 88 | mask->append({0.0, 0.0, 0.25, 1.0}); 89 | } 90 | if(mask && mask->size()==4) 91 | { 92 | n->box() = Box( 93 | vec3(mask->at(0), mask->at(1), K_EPSILON * 5.0f), 94 | vec3(mask->at(2), mask->at(3), 0.5f) 95 | ); 96 | } 97 | else 98 | { 99 | n->box() = Box( 100 | vec3(0.0f, 0.5f, K_EPSILON * 5.0f), 101 | vec3(1.0f, 1.0f, 0.5f) 102 | ); 103 | } 104 | if(hflip){ 105 | n->box().min().x = 1.0f - n->box().min().x; 106 | n->box().max().x = 1.0f - n->box().max().x; 107 | } 108 | if(vflip){ 109 | n->box().min().y = 1.0f - n->box().min().y; 110 | n->box().max().y = 1.0f - n->box().max().y; 111 | } 112 | obj->mesh()->add(n); 113 | if(obj_cfg->has("door")) 114 | { 115 | m_pPartitioner->register_object(n, DOOR); 116 | }else{ 117 | m_pPartitioner->register_object(n, STATIC); 118 | obj_cfg->set("static", ""); 119 | } 120 | } 121 | else { 122 | m_pPartitioner->register_object(obj->mesh(), GROUND); 123 | obj->mesh()->config()->set("static", ""); 124 | } 125 | } 126 | } 127 | } 128 | 129 | m_pPartitioner->on_collision( 130 | CHARACTER, STATIC, 131 | std::bind(&World::cb_to_tile, this, _::_1, _::_2) 132 | ); 133 | m_pPartitioner->on_collision( 134 | THING, STATIC, 135 | std::bind(&Thing::cb_to_static, _::_1, _::_2) 136 | ); 137 | m_pPartitioner->on_collision( 138 | BULLET, STATIC, 139 | std::bind(&World::cb_bullet_to_static, this, _::_1, _::_2) 140 | ); 141 | m_pPartitioner->on_collision( 142 | THING, BULLET, 143 | std::bind(&Thing::cb_to_bullet, _::_1, _::_2) 144 | ); 145 | m_pPartitioner->on_collision( 146 | CHARACTER, THING, 147 | std::bind(&Thing::cb_to_player, _::_1, _::_2) 148 | ); 149 | m_pPartitioner->on_collision( 150 | CHARACTER, DOOR, 151 | std::bind(&World::cb_open_door, this, _::_1, _::_2), 152 | std::bind(&World::cb_not_on_door, this, _::_1, _::_2) 153 | ); 154 | 155 | m_pPartitioner->on_collision( 156 | CHARACTER_HEAD, GROUND, 157 | function(), 158 | function(), 159 | [this](Node* a, Node*){ 160 | //LOGf("a %s, b %s", string(a->world_box()) % string(b->world_box())); 161 | Character* playerptr = (Character*)(a->parent()->parent()); 162 | bool was_indoors = playerptr->indoors(); 163 | playerptr->indoors(true); 164 | if(was_indoors != playerptr->indoors()) // changed 165 | { 166 | //LOG("indoors"); 167 | } 168 | }, 169 | [this](Node* a, Node*){ 170 | //LOGf("a %s, b %s", string(a->world_box()) % string(b->world_box())); 171 | Character* playerptr = (Character*)(a->parent()->parent()); 172 | bool was_indoors = playerptr->indoors(); 173 | playerptr->indoors(false); 174 | if(was_indoors != playerptr->indoors()) // chnaged 175 | { 176 | //LOG("outdoors"); 177 | } 178 | } 179 | ); 180 | 181 | // TODO: bake each map layer independently 182 | //for(auto&& layers: layer_types) 183 | // for(auto&& layer: *layers) 184 | // { 185 | // Mesh::bake(layer, m_pQor->pipeline(), [](Node* n){ 186 | // LOG("baking") 187 | // return n->visible() && n->config()->has("static"); 188 | // }); 189 | // } 190 | } 191 | } 192 | 193 | void World :: setup_thing(std::shared_ptr thing) 194 | { 195 | thing->init_thing(); 196 | m_Things.push_back(thing); 197 | 198 | for(auto&& player: m_Characters) 199 | setup_player_to_thing(player,thing); 200 | } 201 | 202 | void World :: spawn_player(std::shared_ptr player) 203 | { 204 | try{ 205 | m_Spawns.at(0)->add(player); 206 | }catch(...){ 207 | WARNING("Map has no spawn points"); 208 | add(player); 209 | } 210 | player->collapse(Space::WORLD); 211 | player->rescale(vec3(1.0f)); 212 | auto pos = player->position(); 213 | player->position(vec3(pos.x, pos.y, floor(pos.z))); 214 | m_Characters.push_back(player); 215 | setup_player(player); 216 | } 217 | 218 | std::shared_ptr World :: find_mask(std::shared_ptr n, std::string mask_name) 219 | { 220 | auto mask_itr = find_if(ENTIRE(*n), 221 | [mask_name](const std::shared_ptr& n){ 222 | return n->name() == mask_name; 223 | } 224 | ); 225 | if(mask_itr == n->end()) 226 | return nullptr; 227 | return *mask_itr; 228 | } 229 | 230 | void World :: setup_player(std::shared_ptr player) 231 | { 232 | // create masks 233 | { 234 | auto n = make_shared(); 235 | n->name("mask"); 236 | n->box() = Box( 237 | vec3(0.25f, 0.5f, K_EPSILON * 5.0f), 238 | vec3(0.75f, 1.0f, 1.0f - K_EPSILON * 5.0f) 239 | ); 240 | player->mesh()->add(n); 241 | //LOGf("player box: %s", string(n->box())); 242 | //LOGf("player box: %s", string(n->world_box())); 243 | m_pPartitioner->register_object(n, CHARACTER); 244 | } 245 | { 246 | auto n = make_shared(); 247 | n->name("head_mask"); 248 | n->box() = Box( 249 | vec3(0.4f, 0.0f, 1.0f - K_EPSILON * 5.0f), 250 | vec3(0.6f, 0.1f, 10.0f - K_EPSILON * 5.0f) 251 | ); 252 | player->mesh()->add(n); 253 | m_pPartitioner->register_object(n, CHARACTER_HEAD); 254 | } 255 | 256 | //setup_player_to_map(player); 257 | for(auto&& thing: m_Things) 258 | setup_player_to_thing(player,thing); 259 | } 260 | 261 | void World :: setup_bullet(std::shared_ptr bullet) 262 | { 263 | m_pPartitioner->register_object(bullet->mesh(), BULLET); 264 | } 265 | 266 | void World :: setup_player_to_map(std::shared_ptr player) 267 | { 268 | if(not m_pMap) 269 | return; 270 | 271 | //auto player_mask = find_mask(player->mesh()); 272 | //auto player_head_mask = find_mask(player->mesh(), "head_mask"); 273 | //m_pPartitioner->register_object(player_head_mask, CHARACTER_HEAD); 274 | 275 | //vector>*> layer_types { 276 | // &m_pMap->layers(), 277 | // &m_pMap->object_layers() 278 | //}; 279 | 280 | 281 | //for(auto&& layers: layer_types) 282 | //for(auto&& layer: *layers) 283 | //{ 284 | //if(not layer->depth()) 285 | // continue; 286 | // for(auto&& tile: *layer) 287 | // { 288 | // auto obj = std::dynamic_pointer_cast(tile); 289 | // if(not obj || not obj->mesh() || not obj->visible()) 290 | // continue; 291 | 292 | // auto mask = find_mask(obj->mesh()); 293 | 294 | // if(player_mask && mask) 295 | // { 296 | // if(not obj->config()->has("door")) 297 | // { 298 | // m_pPartitioner->on_collision( 299 | // player_mask, mask, 300 | // std::bind(&World::cb_player_to_static, this, _::_1, _::_2) 301 | // ); 302 | // } else { 303 | // m_pPartitioner->on_collision( 304 | // player_mask, mask, 305 | // std::bind(&World::cb_player_to_door, this, _::_1, _::_2), 306 | // function(), 307 | // std::bind(&World::cb_player_enter_door, this, _::_1, _::_2), 308 | // std::bind(&World::cb_player_leave_door, this, _::_1, _::_2) 309 | // ); 310 | // } 311 | // } 312 | 313 | // if(player_head_mask) 314 | // { 315 | // //LOGf("mesh box: %s", string(obj->mesh()->world_box())); 316 | // auto playerptr = player.get(); 317 | // m_pPartitioner->on_collision( 318 | // // player_head_mask 319 | // player_head_mask, obj->mesh(), // use full object, not mask 320 | // function(), 321 | // function(), 322 | // [playerptr, this](Node* a, Node* b){ 323 | // //LOGf("a %s, b %s", string(a->world_box()) % string(b->world_box())); 324 | // bool was_indoors = playerptr->indoors(); 325 | // playerptr->indoors(true); 326 | // if(was_indoors != playerptr->indoors()) // changed 327 | // { 328 | // //LOG("indoors"); 329 | // } 330 | // }, 331 | // [playerptr, this](Node* a, Node* b){ 332 | // //LOGf("a %s, b %s", string(a->world_box()) % string(b->world_box())); 333 | // bool was_indoors = playerptr->indoors(); 334 | // playerptr->indoors(false); 335 | // if(was_indoors != playerptr->indoors()) // chnaged 336 | // { 337 | // //LOG("outdoors"); 338 | // } 339 | // } 340 | // ); 341 | // } 342 | // } 343 | //} 344 | } 345 | 346 | void World :: setup_player_to_thing(std::shared_ptr player, std::shared_ptr thing) 347 | { 348 | thing->setup_player(player); 349 | } 350 | 351 | void World :: setup_thing_to_map(std::shared_ptr thing) 352 | { 353 | thing->setup_map(m_pMap); 354 | } 355 | 356 | void World :: cb_to_static(Node* a, Node* b, Node* m) 357 | { 358 | if(not m) m = a; 359 | 360 | auto p = m->position(Space::PARENT); 361 | auto col = [this, a, b]() -> bool { 362 | return (not m_pPartitioner->get_collisions_for(a, STATIC).empty()) || 363 | a->world_box().collision(b->world_box()); 364 | }; 365 | 366 | Box overlap = a->world_box().intersect(b->world_box()); 367 | //LOGf("collision: %s", string(a->world_box())); 368 | //LOGf("collision: %s", string(b->world_box())); 369 | 370 | vec3 overlap_sz = overlap.size() + vec3(1.0f); 371 | if(not floatcmp(m->velocity().x, 0.0f)) 372 | { 373 | auto np = vec3(p.x - sgn(m->velocity().x) * overlap_sz.x, p.y, p.z); 374 | m->position(np); 375 | if(not col()) 376 | return; 377 | } 378 | if(not floatcmp(m->velocity().y, 0.0f)) 379 | { 380 | m->position(vec3(p.x, p.y - sgn(m->velocity().y) * overlap_sz.y, p.z)); 381 | if(not col()) 382 | return; 383 | } 384 | 385 | try{ 386 | auto old_pos = Matrix::translation(kit::safe_ptr(m->snapshot(0))->world_transform); 387 | m->position(vec3(old_pos.x, old_pos.y, p.z)); 388 | }catch(const kit::null_ptr_exception&){} 389 | } 390 | 391 | void World :: cb_to_tile(Node* a, Node* b) 392 | { 393 | cb_to_static(a, b, a->parent()->parent()); 394 | } 395 | 396 | void World :: cb_open_door(Node* a, Node* b) 397 | { 398 | Mesh* door = ((Mesh*)b->parent()); 399 | if(door->visible()){ 400 | door->visible(false); 401 | sound(door, "door.wav"); 402 | } 403 | } 404 | 405 | void World :: cb_not_on_door(Node* a, Node* b) 406 | { 407 | Mesh* door = ((Mesh*)b->parent()); 408 | if(not door->visible()) 409 | door->visible(true); 410 | } 411 | 412 | void World :: sound(Node* n, std::string fn) 413 | { 414 | shared_ptr snd; 415 | try{ 416 | snd = make_shared(fn, m_pResources); 417 | }catch(...){ 418 | WARNINGf("missing sound: %s", fn); 419 | return; // TEMP: ignore missing sounds 420 | } 421 | n->add(snd); 422 | snd->collapse(Space::WORLD); 423 | snd->play(); 424 | auto sndptr = snd.get(); 425 | snd->detach_on_done(); 426 | } 427 | 428 | void World :: cb_bullet_to_static(Node* a, Node* b) 429 | { 430 | Node* bullet = a->parent(); 431 | sound(bullet, "hit.wav"); 432 | if(not bullet->config()->at("penetration",false)) 433 | bullet->on_tick.connect([bullet](Freq::Time){ 434 | bullet->detach(); 435 | }); 436 | } 437 | 438 | //void World :: cb_player_enter_door(Node* a, Node* b) 439 | //{ 440 | // LOG("door"); 441 | // // if door closed and unlocked open door 442 | // ((Mesh*)b->parent())->visible(false); 443 | //} 444 | 445 | //void World :: cb_player_leave_door(Node* a, Node* b) 446 | //{ 447 | // // if door open shut door? 448 | // ((Mesh*)b->parent())->visible(true); 449 | //} 450 | 451 | void World :: setup_camera(Camera* camera) 452 | { 453 | camera->set_node_visible_func([this, camera](const Node* n, Node::LoopCtrl* lc){ 454 | auto npos = n->position(Space::WORLD).z; 455 | 456 | // indicators are always visible 457 | if(n->layer() == INDICATOR){ 458 | //if(lc) *lc = LC_SKIP; 459 | return true; 460 | } 461 | if(not camera->target()) 462 | return true; 463 | auto player = camera->target()->parent()->parent(); 464 | auto playerpos = player->position(Space::WORLD).z; 465 | Character* c = dynamic_cast(player); 466 | if(c->indoors()) 467 | { 468 | if(n->layer() == WEATHER){ 469 | //if(lc) *lc = LC_SKIP; 470 | return false; 471 | } 472 | return npos < playerpos + 1.0f - K_EPSILON || npos >= 49.0f; 473 | } 474 | return true; 475 | }); 476 | } 477 | 478 | -------------------------------------------------------------------------------- /src/World.h: -------------------------------------------------------------------------------- 1 | #ifndef WORLD_H_FJLWGZSZ 2 | #define WORLD_H_FJLWGZSZ 3 | 4 | #include 5 | #include "Qor/Qor.h" 6 | #include "Qor/Node.h" 7 | #include "Qor/BasicPartitioner.h" 8 | #include "Qor/TileMap.h" 9 | #include "Character.h" 10 | class Thing; 11 | 12 | class World: 13 | public Node 14 | { 15 | public: 16 | 17 | enum Layers { 18 | DEFAULT = 0, // clippable based on character's vision 19 | WEATHER = 1, 20 | INDICATOR = 2 21 | }; 22 | 23 | enum ObjectTypes { 24 | GROUND, 25 | STATIC, 26 | CHARACTER, 27 | CHARACTER_HEAD, 28 | DOOR, 29 | BULLET, 30 | THING 31 | }; 32 | 33 | World( 34 | Qor* qor, 35 | BasicPartitioner* partitioner 36 | ); 37 | virtual ~World() {} 38 | static World* world_of(Node* n) { 39 | while(n->parent()) 40 | n = n->parent(); 41 | return dynamic_cast(n); 42 | } 43 | 44 | static std::shared_ptr find_mask(std::shared_ptr n, std::string mask_name = "mask"); 45 | void setup_thing(std::shared_ptr thing); 46 | void setup_player(std::shared_ptr player); 47 | void setup_bullet(std::shared_ptr bullet); 48 | void spawn_player(std::shared_ptr player); 49 | void setup_player_to_map(std::shared_ptr player); 50 | void setup_player_to_thing(std::shared_ptr player, std::shared_ptr thing); 51 | void setup_thing_to_map(std::shared_ptr thing); 52 | 53 | void cb_to_static(Node* a, Node* b, Node* m = nullptr); 54 | void cb_to_tile(Node* a, Node* b); 55 | void cb_bullet_to_static(Node* a, Node* b); 56 | void cb_open_door(Node* a, Node* b); 57 | void cb_not_on_door(Node* a, Node* b); 58 | //void cb_player_enter_door(Node* a, Node* b); 59 | //void cb_player_leave_door(Node* a, Node* b); 60 | 61 | TileMap* map() {return m_pMap.get();} 62 | 63 | void setup_camera(Camera* camera); 64 | void sound(Node* n, std::string fn); 65 | 66 | //virtual void logic(Freq::Time t) override {} 67 | 68 | private: 69 | 70 | Qor* m_pQor; 71 | Cache* m_pResources; 72 | BasicPartitioner* m_pPartitioner = nullptr; 73 | std::vector m_Spawns; 74 | std::shared_ptr m_pMap; 75 | 76 | std::vector> m_Characters; 77 | std::vector> m_Things; 78 | //std::vector> m_Bullets; 79 | }; 80 | 81 | #endif 82 | 83 | --------------------------------------------------------------------------------