├── .gitignore ├── LICENSE ├── README.md ├── cpp ├── assets │ └── terminal16x16.png ├── build.bat ├── components │ ├── item.hpp │ ├── monster.hpp │ ├── player.hpp │ ├── position.hpp │ └── trap.hpp ├── design_mode.cpp ├── design_mode.hpp ├── leaderboards.cpp ├── leaderboards.hpp ├── level.hpp ├── level_select.cpp ├── level_select.hpp ├── main.cpp ├── main_menu.cpp ├── main_menu.hpp ├── modestack.cpp ├── modestack.hpp ├── play_mode.cpp ├── play_mode.hpp └── rltk │ ├── base_mode.cpp │ ├── base_mode.hpp │ ├── geometry.cpp │ ├── geometry.hpp │ ├── sprite.cpp │ ├── sprite.hpp │ ├── visibility.hpp │ ├── window.cpp │ └── window.hpp ├── node └── dankest-dungeon │ ├── app.js │ ├── bin │ └── www │ ├── package.json │ ├── public │ ├── dankest.data │ ├── dankest.js │ ├── dankest.js.mem │ ├── game.html │ ├── index.html │ ├── javascripts │ │ ├── game.js │ │ ├── leaderboards.js │ │ ├── login.js │ │ └── md5.js │ ├── leaderboards.html │ └── stylesheets │ │ ├── dank.css │ │ └── style.css │ ├── routes │ ├── database.js │ ├── index.js │ ├── tokens.js │ └── users.js │ └── views │ ├── error.jade │ ├── index.jade │ └── layout.jade └── postgres.sql /.gitignore: -------------------------------------------------------------------------------- 1 | cpp/bin/* 2 | .vscode 3 | node/dankest-dungeon/node_modules 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Bracket Productions (Herbert Wolverson) 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 | # Dankest Dungeon 2 | 3 | Dankest Dungeon is a 7-Day Roguelike, written in Emscripten (a C++ to JavaScript compiler) and NodeJS. It started out as a tech demo, but it has proven mildly popular - so I may well keep hacking away at it. 4 | 5 | You can play it on my server, [http://dankest.bracketproductions.com/](http://dankest.bracketproductions.com/) 6 | 7 | **How to play** 8 | 9 | Go to [http://dankest.bracketproductions.com/](http://dankest.bracketproductions.com/), and create an account (or login if you have one). The main menu should be self-explanatory (keys or mouse) - (1) to create a dungeon, (2) to play one. 10 | 11 | *Designing a Dungeon* 12 | 13 | The red options on the right are categories, which activate palette entries such as floors, walls and toadstools. Whatever is highlighted on the right pane will draw onto the map on the left when you click it. You *must* have an *Amulet of Winning* and a *Player* on the map (1 of each). Other than that, you can do whatever you like. It's a 100x100 canvas, so you should be able to build a convoluted dungeon if you want to. 14 | 15 | Map Elements: 16 | 17 | * Floors (`.`) are walkable. 18 | * Walls (`▒`), Toadstools (`♠`), Vegetation (`♣`) block visibility and progress and cannot be penetrated. 19 | * Pools (`~`) cannot be crossed, but do not block line-of-sight. 20 | * Doors (`+`) block line of sight, but can be destroyed with 1 action. 21 | * Iron Doors (`!`) block line of sight and progress, but can be destroyed IF you have an Iron Key (`∞`). 22 | * Gold Doors (`≡`) block line of sight and progress, but can be destroyed IF you have a Gold Key (`φ`). 23 | * Kobolds (`k`) are a nuisance. They hit for minimal damage, and can be easily slain. They path towards you if they can see you, otherwise they remain stationary. 24 | * Orcs (`o`) are more of a threat. They hit for moderate damage, but can be easily slain. They path towards you if they can see you, otherwise they remain stationary. 25 | * Dragons (`D`) kill you instantly if they get to you! They cannot be slain. They path towards you if they can see you, otherwise they remain stationary. 26 | * Pit Traps (`⌂`) and Blade Traps (`±`) inflict damage if you walk into them. They are invisible unless you fall into one, or WAIT for a turn with them in your visibility map. 27 | * Mapping Scrolls (`═`) reveal the entire map. 28 | * Healing potions (`σ`) give you some hit points back. 29 | 30 | When you are ready, hit *playtest* (or `.`). 31 | 32 | *Playtesting* 33 | 34 | Within playtest mode, you are playing the game. You can move around with the cursor keys (or vi keys, or numpad). Diagonal movement works. Press `.` to wait, which also reveals any traps within your visibile area. If you want to change your map, hit `ESC` - and return to the level editor. You can cycle between these modes as much as you like - but you will restart the dungeon each time you playtest. 35 | 36 | When you hit the amulet (¥), and win the game. It kicks you back to the designer with a message that you have beaten the dungeon and can save it by pressing `ESC`. This prompts you to name your dungeon, and it is now available for other players to try. 37 | 38 | *Playing People's Maps* 39 | 40 | Option 2 on the main menu presents you with a list of every submitted map. You can use page up/down if more than a screen-load arrive. Click a map to play it. Play is exactly the same as playtesting above, but when you finish you are asked to submit a rating from 0-5 for how much you liked the map. 41 | 42 | ## Setting up your own dungeon server 43 | 44 | The build scripts are all Windows-based, since that what I was using. You also need a PostgreSQL database server available. 45 | 46 | The file `postgres.sql` contains the database schema. You'll want to edit `node/dankest_dungeon/routes/database.js` to provide the URL to your database. 47 | 48 | Next, install [Emscripten](http://kripken.github.io/emscripten-site/). Take note of where it is installed, and modify `cpp/build.bat` to point to the appropriate path for `emsdk_env.bat`. 49 | 50 | You can now `cd` to the `cpp` directory and build with `build.bat`. This compiles the game, and copies the results into your `node/dankest_dungeon/public` folder. 51 | 52 | Finally, you can run the game by changing directory to `node/dankest_dungeon` and running `npm start`. (If you haven't already, run `npm install` first to grab packages). You can then go to your game at *http://localhost:9000/* 53 | -------------------------------------------------------------------------------- /cpp/assets/terminal16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebracket/dankestdungeon/e10fbc298efc2ec3652548633ad30ab79d2b9742/cpp/assets/terminal16x16.png -------------------------------------------------------------------------------- /cpp/build.bat: -------------------------------------------------------------------------------- 1 | @echo Setting Environment 2 | @call c:\users\herbert\Development\emscripten\emsdk_env.bat 3 | @echo Building 4 | @call emcc -std=c++1y -o3 -O3 --use-preload-plugins --preload-file assets -s FETCH=1 -s USE_SDL=2 -s USE_SDL_IMAGE=2 -o bin/dankest.js main.cpp rltk/window.cpp rltk/sprite.cpp rltk/base_mode.cpp modestack.cpp main_menu.cpp leaderboards.cpp design_mode.cpp play_mode.cpp rltk/geometry.cpp level_select.cpp 5 | @call copy bin\* ..\node\dankest-dungeon\public 6 | 7 | -------------------------------------------------------------------------------- /cpp/components/item.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "position.hpp" 4 | #include 5 | 6 | struct item_t { 7 | std::string name; 8 | position_t pos; 9 | }; 10 | -------------------------------------------------------------------------------- /cpp/components/monster.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "position.hpp" 4 | 5 | enum monster_type_t { KOBOLD, ORC, DRAGON }; 6 | 7 | struct monster_t { 8 | monster_type_t type; 9 | bool active = false; 10 | position_t pos; 11 | uint8_t glyph; 12 | uint8_t r; 13 | uint8_t g; 14 | uint8_t b; 15 | int hit_points = 1; 16 | }; -------------------------------------------------------------------------------- /cpp/components/player.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "position.hpp" 4 | 5 | struct player_t { 6 | position_t pos; 7 | int hit_points = 10; 8 | 9 | bool has_iron_key = false; 10 | bool has_gold_key = false; 11 | }; 12 | -------------------------------------------------------------------------------- /cpp/components/position.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct position_t { 4 | uint8_t x; 5 | uint8_t y; 6 | }; 7 | 8 | struct navigator_t { 9 | static inline int get_x(position_t pos) { return pos.x; } 10 | static inline int get_y(position_t pos) { return pos.y; } 11 | static inline position_t get_xy(int x, int y) { return position_t{static_cast(x), static_cast(y)}; } 12 | }; 13 | -------------------------------------------------------------------------------- /cpp/components/trap.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "position.hpp" 4 | 5 | enum trap_type_t { PIT, BLADE }; 6 | 7 | struct trap_t { 8 | trap_type_t type = PIT; 9 | bool revealed = false; 10 | position_t pos; 11 | }; 12 | -------------------------------------------------------------------------------- /cpp/design_mode.cpp: -------------------------------------------------------------------------------- 1 | #include "design_mode.hpp" 2 | #include "play_mode.hpp" 3 | #include "modestack.hpp" 4 | #include 5 | #include 6 | 7 | bool design_mode_t::tick(window_t * win) { 8 | win->cls(); 9 | win->print_center(0, "Build a dungeon for other players", 255, 255, 0); 10 | 11 | if (first_run) { 12 | initialize_dungeon(); 13 | first_run = false; 14 | } 15 | 16 | render_map(win); 17 | render_ui(win); 18 | handle_inputs(win); 19 | 20 | if (win->is_key_down(SDLK_ESCAPE) && game_exit_state == WON) { 21 | // Build it into a persistent format 22 | std::string level_data = ""; 23 | for (int i=0; iset(screen_x, screen_y, glyph, level.tiles[idx].r, level.tiles[idx].g, level.tiles[idx].b); 74 | if (glyph == '@') ++found_player; 75 | if (glyph == 157) ++found_amulet; 76 | } 77 | ++screen_x; 78 | } 79 | ++screen_y; 80 | } 81 | 82 | if (found_player == 0) win->print_center(terminal_height - 2, "Map must include a player!", 255, 0, 0); 83 | if (found_player > 1) win->print_center(terminal_height - 2, "Map must have only one player", 255, 0, 0); 84 | if (found_amulet == 0) win->print_center(terminal_height - 1, "Map must include the amulet of winning!", 255, 0, 0); 85 | if (found_amulet > 1) win->print_center(terminal_height - 1, "Map must have only one amulet of winning!", 255, 0, 0); 86 | 87 | if (game_exit_state == DEAD) { 88 | win->print_center(0, "Last attempt at the dungeon resulted in painful death! Tweak and try again.", 255, 0, 0); 89 | } else if (game_exit_state == WON) { 90 | win->print_center(0, "You have beaten the dungeon - press ESC to publish.", 0, 255, 0); 91 | } 92 | } 93 | 94 | void design_mode_t::render_ui(window_t * win) { 95 | const int panel_left = terminal_width - 19; 96 | 97 | win->print(panel_left, 1, "Arrow keys scroll", 128, 128, 128); 98 | 99 | if (mode == TERRAIN) { 100 | win->print(panel_left, 3, "Terrain", 255, 0, 0); 101 | } else { 102 | win->print(panel_left, 3, "(T)errain", 128, 0, 0); 103 | } 104 | if (mode == SPECIAL) { 105 | win->print(panel_left, 4, "Required", 255, 0, 0); 106 | } else { 107 | win->print(panel_left, 4, "(R)equired", 128, 0, 0); 108 | } 109 | if (mode == PUZZLE) { 110 | win->print(panel_left, 5, "Puzzle", 255, 0, 0); 111 | } else { 112 | win->print(panel_left, 5, "(P)uzzle", 128, 0, 0); 113 | } 114 | if (mode == MOB) { 115 | win->print(panel_left, 6, "Monster", 255, 0, 0); 116 | } else { 117 | win->print(panel_left, 6, "(M)onster", 128, 0, 0); 118 | } 119 | if (mode == TRAP) { 120 | win->print(panel_left, 7, "Trap", 255, 0, 0); 121 | } else { 122 | win->print(panel_left, 7, "(1)Trap", 128, 0, 0); 123 | } 124 | if (mode == PERKS) { 125 | win->print(panel_left, 8, "Perks", 255, 0, 0); 126 | } else { 127 | win->print(panel_left, 8, "(3)Perks", 128, 0, 0); 128 | } 129 | 130 | if (mode == TERRAIN) { 131 | render_submenu(win, &terrain_elements); 132 | } else if (mode == SPECIAL) { 133 | render_submenu(win, &special_elements); 134 | } else if (mode == PUZZLE) { 135 | render_submenu(win, &puzzle_elements); 136 | } else if (mode == MOB) { 137 | render_submenu(win, &mob_elements); 138 | } else if (mode == TRAP) { 139 | render_submenu(win, &trap_elements); 140 | } else if (mode == PERKS) { 141 | render_submenu(win, &perk_elements); 142 | } 143 | 144 | if (win->is_key_down(SDLK_t)) mode = TERRAIN; 145 | if (win->is_key_down(SDLK_r)) mode = SPECIAL; 146 | if (win->is_key_down(SDLK_p)) mode = PUZZLE; 147 | if (win->is_key_down(SDLK_m)) mode = MOB; 148 | if (win->is_key_down(SDLK_1)) mode = TRAP; 149 | if (win->is_key_down(SDLK_3)) mode = PERKS; 150 | if (win->clicked) { 151 | if (win->mouse_x > terminal_width - 20) { 152 | if (win->mouse_y == 3) mode = TERRAIN; 153 | if (win->mouse_y == 4) mode = SPECIAL; 154 | if (win->mouse_y == 5) mode = PUZZLE; 155 | if (win->mouse_y == 6) mode = MOB; 156 | if (win->mouse_y == 7) mode = TRAP; 157 | if (win->mouse_y == 8) mode = PERKS; 158 | } 159 | } 160 | 161 | win->print(panel_left, terminal_height-1, "Playtest >>>", 0, 255, 0); 162 | if (win->clicked) { 163 | if (win->mouse_x > terminal_width - 20) { 164 | if (win->mouse_y == terminal_height - 1) { 165 | modestack.emplace(std::make_unique(&level, true)); 166 | } 167 | } 168 | } 169 | if (win->is_key_down(SDLK_PERIOD)) { 170 | modestack.emplace(std::make_unique(&level, true)); 171 | } 172 | 173 | } 174 | 175 | void design_mode_t::render_submenu(window_t * win, std::vector * elements) { 176 | const int panel_left = terminal_width - 19; 177 | int y = 10; 178 | 179 | for (std::size_t i=0; isize(); ++i) { 180 | if (paint_glyph == elements->at(i).glyph) { 181 | win->print(panel_left, y, elements->at(i).title, 255, 255, 255); 182 | } else { 183 | win->print(panel_left, y, elements->at(i).title, 128, 128, 128); 184 | } 185 | 186 | if (win->is_key_down(elements->at(i).hotkey)) { 187 | paint_glyph = elements->at(i).glyph; 188 | paint_r = elements->at(i).r; 189 | paint_g = elements->at(i).g; 190 | paint_b = elements->at(i).b; 191 | } 192 | 193 | if (win->clicked) { 194 | if (win->mouse_x > terminal_width - 20) { 195 | if (win->mouse_y == y) { 196 | paint_glyph = elements->at(i).glyph; 197 | paint_r = elements->at(i).r; 198 | paint_g = elements->at(i).g; 199 | paint_b = elements->at(i).b; 200 | } 201 | } 202 | } 203 | 204 | ++y; 205 | } 206 | } 207 | 208 | void design_mode_t::handle_inputs(window_t * win) { 209 | // Process keyboard movement 210 | if (win->is_key_down(SDLK_LEFT)) { 211 | --cx; 212 | if (cx < 0) cx = 0; 213 | } 214 | if (win->is_key_down(SDLK_RIGHT)) { 215 | ++cx; 216 | if (cx > level_width - 1) cx = level_width - 1; 217 | } 218 | if (win->is_key_down(SDLK_UP)) { 219 | --cy; 220 | if (cy < 0) cy = 0; 221 | } 222 | if (win->is_key_down(SDLK_DOWN)) { 223 | ++cy; 224 | if (cy > level_height - 1) cy = level_height - 1; 225 | } 226 | 227 | if (win->clicked) { 228 | if (win->mouse_x < terminal_width - 20) { 229 | // We are in the map 230 | const int left_x = std::max(0, cx - (terminal_width/2)); 231 | const int top_y = std::max(0, cy - (terminal_height/2) - 1); 232 | const int click_x = left_x + win->mouse_x; 233 | const int click_y = top_y + win->mouse_y; 234 | 235 | if (click_x > 0 && click_x < level_width && click_y > 0 && click_y < level_height) { 236 | const int idx = level.idx(click_x, click_y); 237 | level.tiles[idx].glyph = paint_glyph; 238 | level.tiles[idx].r = paint_r; 239 | level.tiles[idx].g = paint_g; 240 | level.tiles[idx].b = paint_b; 241 | game_exit_state = IN_PROGRESS; 242 | } 243 | } 244 | } 245 | } -------------------------------------------------------------------------------- /cpp/design_mode.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "rltk/base_mode.hpp" 4 | #include "level.hpp" 5 | #include 6 | 7 | enum design_mode_e { TERRAIN, SPECIAL, PUZZLE, MOB, TRAP, PERKS }; 8 | 9 | struct design_element_t { 10 | uint8_t glyph; 11 | uint8_t r; 12 | uint8_t g; 13 | uint8_t b; 14 | std::string title; 15 | int hotkey; 16 | }; 17 | 18 | class design_mode_t : public game_mode_t { 19 | public: 20 | virtual bool tick(window_t * win); 21 | 22 | private: 23 | bool first_run = true; 24 | level_t level; 25 | design_mode_e mode = TERRAIN; 26 | uint8_t paint_glyph = '.'; 27 | uint8_t paint_r = 255; 28 | uint8_t paint_g = 255; 29 | uint8_t paint_b = 255; 30 | 31 | void initialize_dungeon(); 32 | void render_map(window_t * win); 33 | void render_ui(window_t * win); 34 | void handle_inputs(window_t * win); 35 | 36 | void render_submenu(window_t * win, std::vector * elements); 37 | 38 | int cx = level_width / 2; 39 | int cy = level_height / 2; 40 | 41 | std::vector terrain_elements = { 42 | design_element_t{'.', 128, 128, 128, "(F)loor", SDLK_f}, 43 | design_element_t{176, 128, 128, 128, "(W)all", SDLK_w}, 44 | design_element_t{6, 230, 10, 238, "(T)oadstool", SDLK_t}, 45 | design_element_t{5, 0, 255, 0, "(V)egetation", SDLK_p}, 46 | design_element_t{'+', 128, 128, 128, "(D)oor - normal", SDLK_d}, 47 | design_element_t{'~', 0, 0, 255, "(P)Pool", SDLK_p} 48 | }; 49 | std::vector special_elements = { 50 | design_element_t{'@', 255, 255, 0, "(P)layer", SDLK_p}, 51 | design_element_t{157, 0, 255, 0, "(A)mulet of Winning", SDLK_a} 52 | }; 53 | std::vector puzzle_elements = { 54 | design_element_t{'!', 255, 255, 255, "(I)ron Door, Locked", SDLK_i}, 55 | design_element_t{236, 255, 255, 255, "(O)Iron Key", SDLK_o}, 56 | design_element_t{240, 255, 255, 0, "(G)old Door, Locked", SDLK_g}, 57 | design_element_t{237, 255, 255, 0, "(H)Gold Key", SDLK_h}, 58 | }; 59 | std::vector mob_elements = { 60 | design_element_t{'k', 255, 0, 0, "(K)obold", SDLK_k}, 61 | design_element_t{'o', 255, 0, 0, "(O)rc", SDLK_o}, 62 | design_element_t{'D', 255, 128, 128, "(D)ragon", SDLK_d} 63 | }; 64 | std::vector trap_elements = { 65 | design_element_t{127, 255, 255, 0, "(P)it trap", SDLK_p}, 66 | design_element_t{241, 0, 255, 0, "(B)lade trap", SDLK_b} 67 | }; 68 | std::vector perk_elements = { 69 | design_element_t{205, 0, 255, 0, "(M)apping Scroll", SDLK_m}, 70 | design_element_t{229, 0, 255, 0, "(H)ealing Potion", SDLK_h} 71 | }; 72 | }; 73 | -------------------------------------------------------------------------------- /cpp/leaderboards.cpp: -------------------------------------------------------------------------------- 1 | #include "leaderboards.hpp" 2 | 3 | bool leaderboards_t::tick(window_t * win) { 4 | win->cls(); 5 | win->print_center(1, "Leaderboards", 255, 255, 0); 6 | win->print_center(2, "ESC to return to main menu", 128, 128, 128); 7 | 8 | if (win->is_key_down(SDLK_ESCAPE)) { 9 | return true; 10 | } 11 | return false; 12 | } 13 | -------------------------------------------------------------------------------- /cpp/leaderboards.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "rltk/base_mode.hpp" 4 | 5 | class leaderboards_t : public game_mode_t { 6 | public: 7 | virtual bool tick(window_t * win); 8 | }; 9 | -------------------------------------------------------------------------------- /cpp/level.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | constexpr int level_width = 100; 6 | constexpr int level_height = 100; 7 | constexpr int level_tiles = level_width * level_height; 8 | 9 | struct tile_t { 10 | uint8_t glyph = 176; 11 | uint8_t r = 128; 12 | uint8_t g = 128; 13 | uint8_t b = 128; 14 | }; 15 | 16 | struct level_t { 17 | std::array tiles; 18 | std::string name; 19 | std::string creator; 20 | int id; 21 | 22 | inline int idx(const int &x, const int &y) { 23 | return (y * level_width) + x; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /cpp/level_select.cpp: -------------------------------------------------------------------------------- 1 | #include "level_select.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "modestack.hpp" 7 | #include "play_mode.hpp" 8 | 9 | bool got_levels = false; 10 | 11 | struct available_level_t { 12 | int id; 13 | std::string name; 14 | std::string creator; 15 | }; 16 | 17 | std::vector available_levels; 18 | 19 | level_t level; 20 | 21 | std::vector split(const std::string& str, const std::string& delim) 22 | { 23 | using namespace std; 24 | vector tokens; 25 | size_t prev = 0, pos = 0; 26 | do 27 | { 28 | pos = str.find(delim, prev); 29 | if (pos == string::npos) pos = str.length(); 30 | string token = str.substr(prev, pos-prev); 31 | if (!token.empty()) tokens.push_back(token); 32 | prev = pos + delim.length(); 33 | } 34 | while (pos < str.length() && prev < str.length()); 35 | return tokens; 36 | } 37 | 38 | bool got_level = false; 39 | 40 | void gotLevel(emscripten_fetch_t *fetch) { 41 | if (got_level) return; 42 | std::fill(level.tiles.begin(), level.tiles.end(), tile_t{'.', 128, 128, 128}); 43 | 44 | std::string tmp = ""; 45 | for (int i=1; inumBytes-1; ++i) { 46 | tmp += fetch->data[i]; 47 | } 48 | 49 | int place = 0; 50 | int tilecount = 0; 51 | auto splits = split(tmp, "X"); 52 | for (const auto &tile : splits) { 53 | switch (place) { 54 | case 0 : level.tiles[tilecount].glyph = std::stoi(tile); break; 55 | case 1 : level.tiles[tilecount].r = std::stoi(tile); break; 56 | case 2 : level.tiles[tilecount].g = std::stoi(tile); break; 57 | case 3 : level.tiles[tilecount].b = std::stoi(tile); break; 58 | } 59 | ++place; 60 | if (place > 3) { 61 | place = 0; 62 | ++tilecount; 63 | } 64 | } 65 | 66 | got_level = true; 67 | modestack.emplace(std::make_unique(&level, false)); 68 | } 69 | 70 | bool level_select_t::tick(window_t * win) { 71 | if (got_level) { 72 | // If we've already loaded a level, skip us on the stack 73 | got_level = false; 74 | return true; 75 | } 76 | 77 | if (first_run) { 78 | first_run = false; 79 | get_levels(); 80 | } 81 | 82 | win->cls(); 83 | win->print_center(1, "Select Your Dungeon", 255, 255, 0); 84 | win->print_center(2, "ESC to return to main menu (PgUp/PgDn for more)", 128, 128, 128); 85 | 86 | if (got_levels) { 87 | int count = 0; 88 | int y = 4; 89 | for (const auto &lvl : available_levels) { 90 | if (count >= start && y < terminal_height-6) { 91 | std::string display = lvl.name + ", " + lvl.creator; 92 | if (win->mouse_y == y) { 93 | win->print_center(y, display, 255, 255, 0); 94 | } else { 95 | win->print_center(y, display); 96 | } 97 | 98 | if (win->clicked && win->mouse_y == y) { 99 | level.id = lvl.id; 100 | level.name = lvl.name; 101 | level.creator = lvl.creator; 102 | 103 | // Load the level via AJAX call 104 | std::string api_name = "/api/Dungeon/" + std::to_string(lvl.id); 105 | emscripten_fetch_attr_t attr; 106 | emscripten_fetch_attr_init(&attr); 107 | strcpy(attr.requestMethod, "GET"); 108 | attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY; 109 | attr.onsuccess = gotLevel; 110 | //attr.onerror = downloadFailed; 111 | emscripten_fetch(&attr, api_name.c_str()); 112 | // This will call into gotLevel when complete 113 | } 114 | } 115 | ++y; 116 | ++count; 117 | } 118 | } else { 119 | win->print_center(4, "Please wait - downloading levels"); 120 | } 121 | 122 | if (win->is_key_down(SDLK_ESCAPE)) { 123 | return true; 124 | } 125 | if (win->is_key_down(SDLK_PAGEDOWN)) { 126 | start += terminal_height-6; 127 | } 128 | if (win->is_key_down(SDLK_PAGEUP)) { 129 | start -= terminal_height-6; 130 | } 131 | return false; 132 | } 133 | 134 | void gotLevels(emscripten_fetch_t *fetch) { 135 | got_levels = true; 136 | 137 | std::string tmp = ""; 138 | for (int i=1; inumBytes-1; ++i) { 139 | tmp += fetch->data[i]; 140 | } 141 | 142 | int place = 0; 143 | auto splits = split(tmp, "\\n"); 144 | available_level_t lvl; 145 | for (const auto &line : splits) { 146 | if (!line.empty()) { 147 | if (place == 0) { 148 | lvl.id = std::stoi(line); 149 | } else if (place == 1) { 150 | lvl.name = line; 151 | } else { 152 | lvl.creator = line; 153 | available_levels.push_back(lvl); 154 | } 155 | } 156 | ++place; 157 | if (place > 2) place = 0; 158 | } 159 | } 160 | 161 | void level_select_t::get_levels() { 162 | available_levels.clear(); 163 | 164 | std::string api_name = "/api/DungeonList"; 165 | emscripten_fetch_attr_t attr; 166 | emscripten_fetch_attr_init(&attr); 167 | strcpy(attr.requestMethod, "GET"); 168 | attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY; 169 | attr.onsuccess = gotLevels; 170 | //attr.onerror = downloadFailed; 171 | emscripten_fetch(&attr, api_name.c_str()); 172 | } 173 | -------------------------------------------------------------------------------- /cpp/level_select.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "rltk/base_mode.hpp" 4 | 5 | class level_select_t : public game_mode_t { 6 | public: 7 | virtual bool tick(window_t * win); 8 | 9 | private: 10 | bool first_run = true; 11 | void get_levels(); 12 | int start = 0; 13 | }; 14 | -------------------------------------------------------------------------------- /cpp/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "rltk/window.hpp" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "modestack.hpp" 8 | #include "main_menu.hpp" 9 | #include 10 | 11 | static std::unique_ptr mainWindow; 12 | static int counter = 0; 13 | 14 | void loop_fn(void *arg) { 15 | if (!modestack.empty()) { 16 | mainWindow->poll(); 17 | const bool should_pop = modestack.top()->tick(mainWindow.get()); 18 | mainWindow->present(); 19 | 20 | if (should_pop) { 21 | modestack.pop(); 22 | } 23 | } 24 | } 25 | 26 | void downloadSucceeded(emscripten_fetch_t *fetch) { 27 | printf("Finished downloading %llu bytes from URL %s.\n", fetch->numBytes, fetch->url); 28 | // The data is now available at fetch->data[0] through fetch->data[fetch->numBytes-1]; 29 | emscripten_fetch_close(fetch); // Free data associated with the fetch. 30 | 31 | username = ""; 32 | for (int i=1; inumBytes-1; ++i) { 33 | username += fetch->data[i]; 34 | } 35 | 36 | modestack.emplace(std::make_unique()); 37 | emscripten_set_main_loop_arg(loop_fn, nullptr, -1, 1); 38 | } 39 | 40 | void downloadFailed(emscripten_fetch_t *fetch) { 41 | printf("Downloading %s failed, HTTP failure status code: %d.\n", fetch->url, fetch->status); 42 | emscripten_fetch_close(fetch); // Also free data on failure. 43 | } 44 | 45 | int main() 46 | { 47 | mainWindow = std::make_unique(); 48 | 49 | // Get the token ID# from local storage 50 | token = EM_ASM_INT( 51 | return localStorage.getItem("dankest_token"); 52 | ); 53 | 54 | std::string api_name = "/api/StartGame/" + std::to_string(token); 55 | emscripten_fetch_attr_t attr; 56 | emscripten_fetch_attr_init(&attr); 57 | strcpy(attr.requestMethod, "GET"); 58 | attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY; 59 | attr.onsuccess = downloadSucceeded; 60 | attr.onerror = downloadFailed; 61 | emscripten_fetch(&attr, api_name.c_str()); 62 | 63 | return 0; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /cpp/main_menu.cpp: -------------------------------------------------------------------------------- 1 | #include "main_menu.hpp" 2 | #include "leaderboards.hpp" 3 | #include "design_mode.hpp" 4 | #include "play_mode.hpp" 5 | #include "modestack.hpp" 6 | #include "level_select.hpp" 7 | 8 | bool main_menu_t::tick(window_t * win) { 9 | const std::string title = std::string("Welcome to Dankest Dungeon"); 10 | const int bx = (terminal_width/2 - title.size()/2)-1; 11 | 12 | win->cls(); 13 | win->box(bx, 4, title.size()+1, 4, 255, 255, 255, true); 14 | win->print_center(5, "Welcome to Dankest Dungeon", 255, 255, 0); 15 | win->print_center(6, username); 16 | win->print_center(7, "A 7-Day Bracket Production", 255, 0, 255); 17 | win->print_center(terminal_height-2, "(c) 2018 Bracket Productions", 0, 0, 255); 18 | 19 | for (int i=0; imouse_y == 11+i) { 21 | selection = i; 22 | if (win->clicked) launch_item(menu_options[i].retcode); 23 | } 24 | 25 | if (selection == i) { 26 | win->print_center(11+i, menu_options[i].title, 255, 255, 255); 27 | if (win->is_key_down(SDLK_RETURN)) { 28 | launch_item(menu_options[i].retcode); 29 | } 30 | } else { 31 | win->print_center(11+i, menu_options[i].title, 255, 0, 0); 32 | } 33 | if (win->is_key_down(menu_options[i].hotkey)) { 34 | launch_item(menu_options[i].retcode); 35 | } 36 | } 37 | 38 | if (win->is_key_down(SDLK_UP)) { 39 | --selection; 40 | if (selection < 0) selection = menu_options.size()-1; 41 | } 42 | if (win->is_key_down(SDLK_DOWN)) { 43 | ++selection; 44 | if (selection > menu_options.size()-1) selection = 0; 45 | } 46 | 47 | return false; 48 | } 49 | 50 | void main_menu_t::launch_item(const int code) { 51 | switch (code) { 52 | case 1 : { 53 | modestack.emplace(std::make_unique()); 54 | } break; 55 | case 2 : { 56 | modestack.emplace(std::make_unique()); 57 | } break; 58 | } 59 | } -------------------------------------------------------------------------------- /cpp/main_menu.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "rltk/base_mode.hpp" 4 | #include 5 | #include 6 | 7 | struct menu_item_t { 8 | std::string title; 9 | int retcode; 10 | int hotkey; 11 | }; 12 | 13 | class main_menu_t : public game_mode_t { 14 | public: 15 | virtual bool tick(window_t * win); 16 | 17 | private: 18 | int selection = 0; 19 | std::array menu_options = { 20 | menu_item_t{"(1) - Create a Dungeon Challenge", 1, SDLK_1}, 21 | menu_item_t{"(2) - Play a Dungeon Challenge", 2, SDLK_2}, 22 | }; 23 | 24 | void launch_item(const int code); 25 | }; 26 | -------------------------------------------------------------------------------- /cpp/modestack.cpp: -------------------------------------------------------------------------------- 1 | #include "modestack.hpp" 2 | 3 | std::stack> modestack; 4 | return_state_t game_exit_state = IN_PROGRESS; 5 | int game_exit_turns = 0; 6 | std::string username; 7 | int token; 8 | -------------------------------------------------------------------------------- /cpp/modestack.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "rltk/base_mode.hpp" 4 | #include 5 | #include 6 | 7 | enum return_state_t { IN_PROGRESS, WON, DEAD }; 8 | 9 | extern std::stack> modestack; 10 | extern return_state_t game_exit_state; 11 | extern int game_exit_turns; 12 | extern std::string username; 13 | extern int token; 14 | -------------------------------------------------------------------------------- /cpp/play_mode.cpp: -------------------------------------------------------------------------------- 1 | #include "play_mode.hpp" 2 | #include "rltk/visibility.hpp" 3 | #include "components/position.hpp" 4 | #include 5 | #include 6 | #include "modestack.hpp" 7 | #include 8 | #include 9 | 10 | play_mode_t::play_mode_t(const level_t * level, bool testing) { 11 | // Playtest constructor - copy the level to the playable format. 12 | playtesting = testing; 13 | game_exit_state = IN_PROGRESS; 14 | game_exit_turns = 0; 15 | 16 | if (!testing) { 17 | levelId = level->id; 18 | levelName = level->name; 19 | levelCreator = level->creator; 20 | } 21 | 22 | int x = 0; 23 | int y = 0; 24 | for (std::size_t i=0; i < level_tiles; ++i) { 25 | tile_render[i] = level->tiles[i]; 26 | 27 | // Replace player with floor and note position 28 | if (tile_render[i].glyph == '@') { 29 | set_floor(i); 30 | player.pos.x = x; 31 | player.pos.y = y; 32 | } else if (tile_render[i].glyph == 157) { 33 | set_floor(i); 34 | amulet.name = "Amulet of Winning"; 35 | amulet.pos.x = x; 36 | amulet.pos.y = y; 37 | } else if (tile_render[i].glyph == '+') { 38 | set_door(i); // Doors 39 | } else if (tile_render[i].glyph == 236) { 40 | set_iron_key(i); 41 | } else if (tile_render[i].glyph == '!') { 42 | // TODO: Iron Door 43 | } else if (tile_render[i].glyph == 237) { 44 | set_gold_key(i); 45 | } else if (tile_render[i].glyph == 240) { 46 | // TODO: Gold Door 47 | } else if (tile_render[i].glyph == 'k') { 48 | set_monster(KOBOLD, i, x, y); 49 | } else if (tile_render[i].glyph == 'o') { 50 | set_monster(ORC, i, x, y); 51 | } else if (tile_render[i].glyph == 'D') { 52 | set_monster(DRAGON, i, x, y); 53 | } else if (tile_render[i].glyph == 127) { 54 | set_pit_trap(i, x, y); 55 | } else if (tile_render[i].glyph == 241) { 56 | set_blade_trap(i, x, y); 57 | } else if (tile_render[i].glyph == 205) { 58 | tile_render[i].glyph = 205; 59 | tile_render[i].r = 0; 60 | tile_render[i].g = 255; 61 | tile_render[i].b = 0; 62 | } else if (tile_render[i].glyph == 229) { 63 | tile_render[i].glyph = 229; 64 | tile_render[i].r = 0; 65 | tile_render[i].g = 255; 66 | tile_render[i].b = 0; 67 | } 68 | 69 | // Other info 70 | tile_visible[i] = false; 71 | tile_revealed[i] = false; 72 | tile_solid[i] = is_solid(tile_render[i].glyph); 73 | 74 | ++x; 75 | if (x >= level_width) { 76 | x = 0; 77 | ++y; 78 | } 79 | } 80 | 81 | cast_visibility(); 82 | log_entry("Welcome to the dungeon, we've got fun and games..."); 83 | } 84 | 85 | void play_mode_t::set_floor(const int &idx) { 86 | tile_render[idx].glyph = '.'; 87 | tile_render[idx].r = 128; 88 | tile_render[idx].g = 128; 89 | tile_render[idx].b = 128; 90 | } 91 | 92 | void play_mode_t::set_pit_trap(const int &idx, const int &x, const int &y) { 93 | tile_render[idx].glyph = '.'; 94 | tile_render[idx].r = 128; 95 | tile_render[idx].g = 128; 96 | tile_render[idx].b = 128; 97 | trap_t newtrap{ PIT, false, position_t{static_cast(x), static_cast(y)}}; 98 | traps.insert(std::make_pair(idx, newtrap)); 99 | } 100 | 101 | void play_mode_t::set_blade_trap(const int &idx, const int &x, const int &y) { 102 | tile_render[idx].glyph = '.'; 103 | tile_render[idx].r = 128; 104 | tile_render[idx].g = 128; 105 | tile_render[idx].b = 128; 106 | trap_t newtrap{ BLADE, false, position_t{static_cast(x), static_cast(y)}}; 107 | traps.insert(std::make_pair(idx, newtrap)); 108 | } 109 | 110 | void play_mode_t::set_door(const int &idx) { 111 | tile_render[idx].glyph = '+'; 112 | tile_render[idx].r = 128; 113 | tile_render[idx].g = 128; 114 | tile_render[idx].b = 128; 115 | tile_solid[idx] = true; 116 | } 117 | 118 | void play_mode_t::set_iron_key(const int &idx) { 119 | tile_render[idx].glyph = 236; 120 | tile_render[idx].r = 255; 121 | tile_render[idx].g = 255; 122 | tile_render[idx].b = 255; 123 | } 124 | 125 | void play_mode_t::set_gold_key(const int &idx) { 126 | tile_render[idx].glyph = 237; 127 | tile_render[idx].r = 255; 128 | tile_render[idx].g = 255; 129 | tile_render[idx].b = 0; 130 | } 131 | 132 | bool play_mode_t::is_solid(const uint8_t &glyph) { 133 | switch (glyph) { 134 | case '.': return false; 135 | case 236 : return false; 136 | case 237 : return false; 137 | case 229 : return false; 138 | case 205 : return false; 139 | case '~': return true; 140 | default: return true; 141 | } 142 | } 143 | 144 | void play_mode_t::set_monster(const monster_type_t &type, const int &idx, const int &x, const int &y) { 145 | tile_render[idx].glyph = '.'; 146 | tile_render[idx].r = 128; 147 | tile_render[idx].g = 128; 148 | tile_render[idx].b = 128; 149 | 150 | uint8_t glyph=0, r=255, g=0, b=0; 151 | int hp = 0; 152 | switch (type) { 153 | case KOBOLD : { glyph='k'; hp = 1; } break; 154 | case ORC : { glyph='o'; hp = 3; } break; 155 | case DRAGON : { glyph='D'; hp = 20; } break; 156 | default: printf("Unknown monster type!\n"); 157 | } 158 | 159 | monster_t newmonster{ type, false, position_t{static_cast(x), static_cast(y)}, glyph, r, g, b, hp }; 160 | monsters.insert(std::make_pair(idx, newmonster)); 161 | } 162 | 163 | void play_mode_t::cast_visibility() { 164 | std::fill(tile_visible.begin(), tile_visible.end(), false); 165 | rltk::visibility_sweep_2d(player.pos, 16, 166 | [this] (position_t pos) { 167 | if (pos.x > 0 && pos.x < level_width && pos.y > 0 && pos.y < level_height) { 168 | const int idx = this->mapidx(pos.x, pos.y); 169 | this->tile_revealed[idx] = true; 170 | this->tile_visible[idx] = true; 171 | } 172 | }, 173 | [this] (position_t pos) { 174 | if (pos.x > 0 && pos.x < level_width && pos.y > 0 && pos.y < level_height) { 175 | const int idx = this->mapidx(pos.x, pos.y); 176 | if (tile_render[idx].glyph == '~') return true; 177 | return !tile_solid[idx]; 178 | } else { 179 | return true; 180 | } 181 | } 182 | ); 183 | } 184 | 185 | bool play_mode_t::tick(window_t * win) { 186 | render_map(win); 187 | handle_input(win); 188 | 189 | // Temporary: DELETEME when exit conditions work. 190 | if (playtesting && win->is_key_down(SDLK_ESCAPE)) { 191 | return true; 192 | } 193 | // Check for win/loss conditions 194 | if (player.pos.x == amulet.pos.x && player.pos.y == amulet.pos.y) { 195 | printf("Player was won the game in %d turns.\n", turn); 196 | game_exit_state = WON; 197 | game_exit_turns = turn; 198 | 199 | if (!playtesting) { 200 | EM_ASM( 201 | winGame($0, $1); 202 | , game_exit_turns, levelId); 203 | } 204 | 205 | return true; 206 | } 207 | if (player.hit_points < 1) { 208 | printf("Player is dead\n"); 209 | game_exit_state = DEAD; 210 | game_exit_turns = turn; 211 | 212 | EM_ASM( 213 | loseGame($0, $1); 214 | , game_exit_turns, levelId); 215 | 216 | return true; 217 | } 218 | 219 | return false; 220 | } 221 | 222 | void play_mode_t::handle_input(window_t * win) { 223 | input_type_t input_type = NONE; 224 | for (const auto &im : input_mapper) { 225 | if (win->is_key_down(im.code)) input_type = im.input_type; 226 | } 227 | 228 | if (input_type != NONE) { 229 | do_turn(input_type); 230 | } 231 | } 232 | 233 | bool play_mode_t::check_attack(const int &x, const int &y) { 234 | std::vector to_erase; 235 | bool retval = false; 236 | 237 | for (auto &mob : monsters) { 238 | if (mob.second.pos.x == x && mob.second.pos.y == y) { 239 | if (mob.second.type != DRAGON) mob.second.hit_points -= 5; 240 | if (mob.second.hit_points < 1) { 241 | to_erase.push_back(mob.first); 242 | switch (mob.second.type) { 243 | case KOBOLD : log_entry("You bash the kobold on the head, killing it."); break; 244 | case ORC : log_entry("With a mightly swing, you behead the orc."); break; 245 | case DRAGON : log_entry("You tickle the dragon with your sword. It doesn't like it."); break; 246 | } 247 | } 248 | 249 | retval = true; 250 | } 251 | } 252 | 253 | for (const auto &idx : to_erase) { 254 | monsters.erase(idx); 255 | } 256 | 257 | return retval; 258 | } 259 | 260 | void play_mode_t::do_turn(input_type_t &input) { 261 | // Player 262 | bool player_moved = false; 263 | if (input == LEFT && player.pos.x > 1) { 264 | if (!tile_solid[mapidx(player.pos.x - 1, player.pos.y)]) { 265 | if (!check_attack(player.pos.x - 1, player.pos.y)) { 266 | --player.pos.x; 267 | player_moved = true; 268 | } 269 | } else { 270 | position_t dest = player.pos; 271 | --dest.x; 272 | collide(dest); 273 | } 274 | } else if (input == RIGHT && player.pos.x < level_width - 1) { 275 | if (!tile_solid[mapidx(player.pos.x + 1, player.pos.y)]) { 276 | if (!check_attack(player.pos.x + 1, player.pos.y)) { 277 | ++player.pos.x; 278 | player_moved = true; 279 | } 280 | } else { 281 | position_t dest = player.pos; 282 | ++dest.x; 283 | collide(dest); 284 | } 285 | } else if (input == UP && player.pos.y > 1) { 286 | if (!tile_solid[mapidx(player.pos.x, player.pos.y-1)]) { 287 | if (!check_attack(player.pos.x, player.pos.y-1)) { 288 | --player.pos.y; 289 | player_moved = true; 290 | } 291 | } else { 292 | position_t dest = player.pos; 293 | --dest.y; 294 | collide(dest); 295 | } 296 | } else if (input == DOWN && player.pos.y < level_height - 1) { 297 | if (!tile_solid[mapidx(player.pos.x, player.pos.y+1)]) { 298 | if (!check_attack(player.pos.x, player.pos.y+1)) { 299 | ++player.pos.y; 300 | player_moved = true; 301 | } 302 | } else { 303 | position_t dest = player.pos; 304 | ++dest.y; 305 | collide(dest); 306 | } 307 | } else if (input == UPLEFT && player.pos.x > 1 && player.pos.y > 1) { 308 | if (!tile_solid[mapidx(player.pos.x-1, player.pos.y-1)]) { 309 | if (!check_attack(player.pos.x - 1, player.pos.y - 1)) { 310 | --player.pos.y; 311 | --player.pos.x; 312 | player_moved = true; 313 | } 314 | } else { 315 | position_t dest = player.pos; 316 | --dest.y; 317 | --dest.x; 318 | collide(dest); 319 | } 320 | } else if (input == UPRIGHT && player.pos.x < level_width-1 && player.pos.y > 1) { 321 | if (!tile_solid[mapidx(player.pos.x+1, player.pos.y-1)]) { 322 | if (!check_attack(player.pos.x+1, player.pos.y-1)) { 323 | --player.pos.y; 324 | ++player.pos.x; 325 | player_moved = true; 326 | } 327 | } else { 328 | position_t dest = player.pos; 329 | --dest.y; 330 | ++dest.x; 331 | collide(dest); 332 | } 333 | } else if (input == DOWNLEFT && player.pos.x > 1 && player.pos.y < level_height-1) { 334 | if (!tile_solid[mapidx(player.pos.x-1, player.pos.y+1)]) { 335 | if (!check_attack(player.pos.x - 1, player.pos.y+1)) { 336 | ++player.pos.y; 337 | --player.pos.x; 338 | player_moved = true; 339 | } 340 | } else { 341 | position_t dest = player.pos; 342 | ++dest.y; 343 | --dest.x; 344 | collide(dest); 345 | } 346 | } else if (input == DOWNRIGHT && player.pos.x < level_width-1 && player.pos.y < level_height-1) { 347 | if (!tile_solid[mapidx(player.pos.x+1, player.pos.y+1)]) { 348 | if (!check_attack(player.pos.x+1, player.pos.y+1)) { 349 | ++player.pos.y; 350 | ++player.pos.x; 351 | player_moved = true; 352 | } 353 | } else { 354 | position_t dest = player.pos; 355 | ++dest.y; 356 | ++dest.x; 357 | collide(dest); 358 | } 359 | } else if (input == WAIT) { 360 | for (auto &trap : traps) { 361 | if (tile_visible[mapidx(trap.second.pos.x, trap.second.pos.y)]) { 362 | trap.second.revealed = true; 363 | log_entry("You spotted a trap!"); 364 | } 365 | } 366 | } 367 | 368 | // Redo visibility after movement 369 | if (player_moved) { 370 | cast_visibility(); 371 | const int idx = mapidx(player.pos.x, player.pos.y); 372 | auto finder = traps.find(idx); 373 | if (finder != traps.end()) { 374 | player.hit_points -= 3; 375 | switch (finder->second.type) { 376 | case PIT : log_entry("You stumble into a pit trap, taking 3 points of damage."); break; 377 | case BLADE : log_entry("Snickety snick! Blades shoot out of the floor, hitting you for 3 points of damage."); break; 378 | } 379 | } 380 | } 381 | 382 | // Update counters 383 | ++turn; 384 | 385 | // Monsters 386 | for (auto &mob : monsters) { 387 | // Set to inactive 388 | mob.second.active = false; 389 | 390 | // If in range, can it see the player? 391 | const float mob_range = rltk::distance2d(player.pos.x, player.pos.y, mob.second.pos.x, mob.second.pos.y); 392 | if (mob_range < 10.0f) { 393 | rltk::visibility_sweep_2d(mob.second.pos, 8, [this, &mob] (position_t pos) { 394 | // Hit tile 395 | if (pos.x == this->player.pos.x && pos.y == this->player.pos.y) { 396 | mob.second.active = true; 397 | } 398 | }, [this] (position_t pos) { 399 | // Is solid 400 | if (pos.x > 0 && pos.x < level_width && pos.y > 0 && pos.y < level_height) { 401 | return !tile_solid[this->mapidx(pos.x, pos.y)]; 402 | } else { 403 | return true; 404 | } 405 | }); 406 | } 407 | 408 | // Are we active? 409 | if (mob.second.active) { 410 | if (mob_range < 1.5f) { 411 | // Adjacent - attack! 412 | switch (mob.second.type) { 413 | case KOBOLD : { 414 | log_entry("The kobold pokes you with a sharp stick (1 HP damage)."); 415 | --player.hit_points; 416 | } break; 417 | case ORC : { 418 | log_entry("The orc slices you with a rusty sword (3 HP damage)."); 419 | player.hit_points -= 3; 420 | } break; 421 | case DRAGON : { 422 | log_entry("The dragon swallows you whole."); 423 | player.hit_points -= 100; 424 | } break; 425 | } 426 | } else { 427 | position_t dest = mob.second.pos; 428 | if (mob.second.pos.x < player.pos.x) ++dest.x; 429 | if (mob.second.pos.x > player.pos.x) --dest.x; 430 | if (mob.second.pos.y < player.pos.y) ++dest.y; 431 | if (mob.second.pos.y > player.pos.y) --dest.y; 432 | const int mobidx = mapidx(dest.x, dest.y); 433 | if (!tile_solid[mobidx]) mob.second.pos = dest; 434 | } 435 | } 436 | } 437 | 438 | // Check for pick-ups 439 | if (tile_render[mapidx(player.pos.x, player.pos.y)].glyph == 236) { 440 | log_entry("You pick up the iron key."); 441 | player.has_iron_key = true; 442 | set_floor(mapidx(player.pos.x, player.pos.y)); 443 | } 444 | if (tile_render[mapidx(player.pos.x, player.pos.y)].glyph == 237) { 445 | log_entry("You pick up the gold key."); 446 | player.has_gold_key = true; 447 | set_floor(mapidx(player.pos.x, player.pos.y)); 448 | } 449 | if (tile_render[mapidx(player.pos.x, player.pos.y)].glyph == 229) { 450 | log_entry("You drink a lovely healing potion. It tastes of flower petals."); 451 | player.hit_points += 10; 452 | set_floor(mapidx(player.pos.x, player.pos.y)); 453 | } 454 | if (tile_render[mapidx(player.pos.x, player.pos.y)].glyph == 205) { 455 | log_entry("You read the map of the entire dungeon, and commit it to memory."); 456 | std::fill(tile_revealed.begin(), tile_revealed.end(), true); 457 | set_floor(mapidx(player.pos.x, player.pos.y)); 458 | } 459 | } 460 | 461 | void play_mode_t::collide(const position_t &pos) { 462 | const int idx = mapidx(pos.x, pos.y); 463 | if (tile_render[idx].glyph == '+') { 464 | tile_render[idx].glyph = '.'; 465 | tile_solid[idx] = false; 466 | log_entry("With a creak, the waterlogged door swings open. That's not closing again."); 467 | } else if (tile_render[idx].glyph == '!') { 468 | if (player.has_iron_key) { 469 | tile_render[idx].glyph = '.'; 470 | tile_solid[idx] = false; 471 | player.has_iron_key = false; 472 | log_entry("You unlock the iron door with the iron key. It slides into the floor."); 473 | } else { 474 | log_entry("This door is solid iron, and requires the iron key to open."); 475 | } 476 | } else if (tile_render[idx].glyph == 240) { 477 | if (player.has_gold_key) { 478 | tile_render[idx].glyph = '.'; 479 | tile_solid[idx] = false; 480 | player.has_gold_key = false; 481 | log_entry("You unlock the gold door with the gold key. It slides into the floor."); 482 | } else { 483 | log_entry("This door is solid gold, and requires the gold key to open."); 484 | } 485 | } else { 486 | log_entry("Ouch! That's a solid wall!"); 487 | } 488 | } 489 | 490 | void play_mode_t::render_map(window_t * win) { 491 | // Map render 492 | win->cls(); 493 | const int left_x = std::max(0, player.pos.x - (terminal_width/2)+15); 494 | const int top_y = std::max(0, player.pos.y - (terminal_height/2)); 495 | int screen_y = 1; 496 | for (int y=top_y; yset(screen_x, screen_y, glyph, r, g, b); 513 | 514 | if (x == amulet.pos.x && y == amulet.pos.y) { 515 | win->set(screen_x, screen_y, 157, 0, 255, 0); 516 | } 517 | auto trapfinder = traps.find(idx); 518 | if (trapfinder != traps.end() && trapfinder->second.revealed) { 519 | if (trapfinder->second.type == PIT) { 520 | win->set(screen_x, screen_y, 127, 0, 255, 0); 521 | } else if (trapfinder->second.type == BLADE) { 522 | win->set(screen_x, screen_y, 241, 0, 255, 0); 523 | } 524 | } 525 | 526 | for (const auto &mob : monsters) { 527 | if (mob.second.pos.x == x && mob.second.pos.y == y) { 528 | win->set(screen_x, screen_y, mob.second.glyph, mob.second.r, mob.second.g, mob.second.b); 529 | } 530 | } 531 | 532 | if (x == player.pos.x && y == player.pos.y) { 533 | win->set(screen_x, screen_y, '@', 255, 255, 0); 534 | } 535 | } 536 | } 537 | ++screen_x; 538 | } 539 | ++screen_y; 540 | } 541 | 542 | const int term_left = terminal_width - 30; 543 | if (playtesting) { 544 | win->print(term_left, 0, "Prove dungeon is beatable!"); 545 | win->print(term_left, 1, "ESCape back to the designer", 128, 128, 128); 546 | } else { 547 | win->print_center(0, levelName + std::string(" by ") + levelCreator, 255, 255, 0); 548 | } 549 | win->print(term_left, 3, std::string("Turn: ") + std::to_string(turn), 128, 128, 128); 550 | win->print(term_left, 4, std::string("HP: ") + std::to_string(player.hit_points), 128, 128, 128); 551 | win->set(term_left, 6, 157, 0, 255, 0); 552 | win->print(term_left+2, 6, "Find the Amulet of Winning", 128, 128, 128); 553 | win->print(term_left, 7, "Move with cursor keys,", 128, 128, 128); 554 | win->print(term_left, 8, "Numeric Keypad,", 128, 128, 128); 555 | win->print(term_left, 9, "or VI keys", 128, 128, 128); 556 | win->print(term_left, 10, ". Wait/Look for traps", 128, 128, 128); 557 | 558 | if (player.has_iron_key) { 559 | win->print(term_left, 12, "You have the iron key"); 560 | } 561 | if (player.has_gold_key) { 562 | win->print(term_left, 13, "You have the gold key"); 563 | } 564 | 565 | int y = terminal_height - 6; 566 | for (int i=0; i<5; ++i) { 567 | win->print(1, y + i, log[i], 128, 128, 128); 568 | } 569 | } 570 | 571 | void play_mode_t::log_entry(std::string msg) { 572 | log[4] = log[3]; 573 | log[3] = log[2]; 574 | log[2] = log[1]; 575 | log[1] = log[0]; 576 | log[0] = msg; 577 | } 578 | -------------------------------------------------------------------------------- /cpp/play_mode.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "rltk/base_mode.hpp" 4 | #include "level.hpp" 5 | #include "components/player.hpp" 6 | #include "components/item.hpp" 7 | #include "components/trap.hpp" 8 | #include "components/monster.hpp" 9 | #include 10 | #include 11 | #include 12 | 13 | enum input_type_t { NONE, LEFT, RIGHT, UP, DOWN, WAIT, UPLEFT, DOWNLEFT, UPRIGHT, DOWNRIGHT }; 14 | 15 | struct input_map_t { 16 | int code; 17 | input_type_t input_type; 18 | }; 19 | 20 | class play_mode_t : public game_mode_t { 21 | public: 22 | play_mode_t() {} 23 | play_mode_t(const level_t * level, bool testing); 24 | virtual bool tick(window_t * win); 25 | private: 26 | bool playtesting = false; 27 | 28 | int turn = 0; 29 | 30 | std::array tile_render; 31 | std::array tile_solid; 32 | std::array tile_revealed; 33 | std::array tile_visible; 34 | 35 | void set_floor(const int &idx); 36 | void set_door(const int &idx); 37 | void set_pit_trap(const int &idx, const int &x, const int &y); 38 | void set_blade_trap(const int &idx, const int &x, const int &y); 39 | void set_monster(const monster_type_t &type, const int &idx, const int &x, const int &y); 40 | void set_iron_key(const int &idx); 41 | void set_gold_key(const int &idx); 42 | bool is_solid(const uint8_t &glyph); 43 | 44 | player_t player; 45 | item_t amulet; 46 | std::map traps; 47 | std::map monsters; 48 | int levelId; 49 | std::string levelName; 50 | std::string levelCreator; 51 | 52 | void cast_visibility(); 53 | void render_map(window_t * win); 54 | void handle_input(window_t * win); 55 | void do_turn(input_type_t &input); 56 | void collide(const position_t &pos); 57 | bool check_attack(const int &x, const int &y); 58 | 59 | inline int mapidx(const int &x, const int &y) { 60 | return (y * level_width) + x; 61 | } 62 | 63 | std::vector log{ 64 | "", "", "", "", "" 65 | }; 66 | 67 | void log_entry(std::string msg); 68 | 69 | /* Quick input mapper. In a real app, this would be adjustable! */ 70 | std::vector input_mapper { 71 | { SDLK_LEFT, LEFT }, 72 | { SDLK_h, LEFT }, 73 | { SDLK_KP_4, LEFT }, 74 | 75 | { SDLK_RIGHT, RIGHT }, 76 | { SDLK_l, RIGHT }, 77 | { SDLK_KP_6, RIGHT }, 78 | 79 | { SDLK_UP, UP }, 80 | { SDLK_k, UP }, 81 | { SDLK_KP_8, UP }, 82 | 83 | { SDLK_DOWN, DOWN }, 84 | { SDLK_j, DOWN }, 85 | { SDLK_KP_2, DOWN }, 86 | 87 | { SDLK_KP_9, UPRIGHT}, 88 | { SDLK_u, UPRIGHT}, 89 | 90 | { SDLK_KP_7, UPLEFT}, 91 | { SDLK_y, UPLEFT}, 92 | 93 | { SDLK_KP_1, DOWNLEFT}, 94 | { SDLK_b, DOWNLEFT}, 95 | 96 | { SDLK_KP_3, DOWNRIGHT}, 97 | { SDLK_n, DOWNRIGHT}, 98 | 99 | { SDLK_PERIOD, WAIT }, 100 | { SDLK_KP_DECIMAL, WAIT } 101 | }; 102 | }; 103 | -------------------------------------------------------------------------------- /cpp/rltk/base_mode.cpp: -------------------------------------------------------------------------------- 1 | #include "base_mode.hpp" 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /cpp/rltk/base_mode.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "window.hpp" 4 | 5 | class game_mode_t { 6 | public: 7 | virtual bool tick(window_t * mainWindow)=0; 8 | }; 9 | 10 | -------------------------------------------------------------------------------- /cpp/rltk/geometry.cpp: -------------------------------------------------------------------------------- 1 | #include "geometry.hpp" 2 | #include 3 | #include 4 | 5 | namespace rltk { 6 | 7 | /* 8 | * From a given point x/y, project forward radius units (generally tiles) at an angle of degrees_radians degrees 9 | * (in radians). 10 | */ 11 | std::pair project_angle(const int &x, const int &y, const double &radius, const double °rees_radians) noexcept 12 | { 13 | return std::make_pair(static_cast(x + radius * std::cos(degrees_radians)), static_cast(y + radius * std::sin(degrees_radians))); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /cpp/rltk/geometry.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* RLTK (RogueLike Tool Kit) 1.00 4 | * Copyright (c) 2016-Present, Bracket Productions. 5 | * Licensed under the MIT license - see LICENSE file. 6 | * 7 | * Random number generator class. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace rltk { 15 | 16 | /* 17 | * From a given point x/y, project forward radius units (generally tiles) at an angle of degrees_radians degrees 18 | * (in radians). 19 | */ 20 | std::pair project_angle(const int &x, const int &y, const double &radius, const double °rees_radians) noexcept; 21 | 22 | /* 23 | * Provides a correct 2D distance between two points. 24 | */ 25 | inline float distance2d(const int &x1, const int &y1, const int &x2, const int &y2) noexcept { 26 | const float dx = (float)x1 - (float)x2; 27 | const float dy = (float)y1 - (float)y2; 28 | return std::sqrt((dx*dx) + (dy*dy)); 29 | } 30 | 31 | /* 32 | * Provides a fast 2D distance between two points, omitting the square-root; compare 33 | * with other squared distances. 34 | */ 35 | inline float distance2d_squared(const int &x1, const int &y1, const int &x2, const int &y2) noexcept { 36 | const float dx = (float)x1 - (float)x2; 37 | const float dy = (float)y1 - (float)y2; 38 | return (dx*dx) + (dy*dy); 39 | } 40 | 41 | /* 42 | * Provides 2D Manhattan distance between two points. 43 | */ 44 | inline float distance2d_manhattan(const int &x1, const int &y1, const int &x2, const int &y2) noexcept { 45 | const float dx = (float)x1 - (float)x2; 46 | const float dy = (float)y1 - (float)y2; 47 | return std::abs(dx) + std::abs(dy); 48 | } 49 | 50 | /* 51 | * Provides a correct 3D distance between two points. 52 | */ 53 | inline float distance3d(const int &x1, const int &y1, const int &z1, const int &x2, const int &y2, const int &z2) noexcept 54 | { 55 | const float dx = (float)x1 - (float)x2; 56 | const float dy = (float)y1 - (float)y2; 57 | const float dz = (float)z1 - (float)z2; 58 | return std::sqrt((dx*dx) + (dy*dy) + (dz*dz)); 59 | } 60 | 61 | /* 62 | * Provides a fast 3D distance between two points, omitting the square-root; compare 63 | * with other squared distances. 64 | */ 65 | inline float distance3d_squared(const int &x1, const int &y1, const int &z1, const int &x2, const int &y2, const int &z2) noexcept 66 | { 67 | float dx = (float)x1 - (float)x2; 68 | float dy = (float)y1 - (float)y2; 69 | float dz = (float)z1 - (float)z2; 70 | return (dx*dx) + (dy*dy) + (dz*dz); 71 | } 72 | 73 | /* 74 | * Provides Manhattan distance between two 3D points. 75 | */ 76 | inline float distance3d_manhattan(const int &x1, const int &y1, const int &z1, const int &x2, const int &y2, const int &z2) noexcept 77 | { 78 | const float dx = (float)x1 - (float)x2; 79 | const float dy = (float)y1 - (float)y2; 80 | const float dz = (float)z1 - (float)z2; 81 | return std::abs(dx) + std::abs(dy) + std::abs(dz); 82 | } 83 | 84 | /* 85 | * Perform a function for each line element between x1/y1 and x2/y2. We used to use Bresenham's line, 86 | * but benchmarking showed a simple float-based setup to be faster. 87 | */ 88 | inline void line_func(const int &x1, const int &y1, const int &x2, const int &y2, std::function &&func) noexcept 89 | { 90 | float x = static_cast(x1) + 0.5F; 91 | float y = static_cast(y1) + 0.5F; 92 | const float dest_x = static_cast(x2); 93 | const float dest_y = static_cast(y2); 94 | const float n_steps = distance2d(x1,y1,x2,y2); 95 | const int steps = static_cast(std::floor(n_steps) + 1); 96 | const float slope_x = (dest_x - x) / n_steps; 97 | const float slope_y = (dest_y - y) / n_steps; 98 | 99 | for (int i = 0; i < steps; ++i) { 100 | func(static_cast(x), static_cast(y)); 101 | x += slope_x; 102 | y += slope_y; 103 | } 104 | } 105 | 106 | /* 107 | * Perform a function for each line element between x1/y1/z1 and x2/y2/z2. Uses a 3D 108 | * implementation of Bresenham's line algorithm. 109 | * https://gist.github.com/yamamushi/5823518 110 | */ 111 | template 112 | void line_func_3d(const int &x1, const int &y1, const int &z1, const int &x2, const int &y2, const int &z2, F &&func) noexcept 113 | { 114 | float x = static_cast(x1)+0.5F; 115 | float y = static_cast(y1)+0.5F; 116 | float z = static_cast(z1)+0.5F; 117 | 118 | float length = distance3d(x1, y1, z1, x2, y2, z2); 119 | int steps = static_cast(std::floor(length)); 120 | float x_step = (x - x2) / length; 121 | float y_step = (y - y2) / length; 122 | float z_step = (z - z2) / length; 123 | 124 | for (int i=0; i(std::floor(x)), static_cast(std::floor(y)), static_cast(std::floor(z))); 129 | } 130 | } 131 | 132 | /* 133 | * Perform a function for each line element between x1/y1 and x2/y2. We used to use Bresenham's algorithm, 134 | * but benchmarking showed that a simple float based vector was faster. 135 | */ 136 | template 137 | inline void line_func_cancellable(const int &x1, const int &y1, const int &x2, const int &y2, F &&func) noexcept { 138 | float x = static_cast(x1) + 0.5F; 139 | float y = static_cast(y1) + 0.5F; 140 | const float dest_x = static_cast(x2); 141 | const float dest_y = static_cast(y2); 142 | const float n_steps = distance2d(x1,y1,x2,y2); 143 | const int steps = static_cast(std::floor(n_steps) + 1); 144 | const float slope_x = (dest_x - x) / n_steps; 145 | const float slope_y = (dest_y - y) / n_steps; 146 | 147 | for (int i = 0; i < steps; ++i) { 148 | if (!func(static_cast(x), static_cast(y))) return; 149 | x += slope_x; 150 | y += slope_y; 151 | } 152 | } 153 | 154 | /* 155 | * Perform a function for each line element between x1/y1/z1 and x2/y2/z2. Uses a 3D 156 | * implementation of Bresenham's line algorithm. 157 | * https://gist.github.com/yamamushi/5823518 158 | */ 159 | template 160 | void line_func_3d_cancellable(const int &x1, const int &y1, const int &z1, const int &x2, const int &y2, const int &z2, F &&func) noexcept 161 | { 162 | float x = static_cast(x1)+0.5F; 163 | float y = static_cast(y1)+0.5F; 164 | float z = static_cast(z1)+0.5F; 165 | 166 | float length = distance3d(x1, y1, z1, x2, y2, z2); 167 | int steps = static_cast(std::floor(length)); 168 | float x_step = (x - x2) / length; 169 | float y_step = (y - y2) / length; 170 | float z_step = (z - z2) / length; 171 | 172 | for (int i=0; i(std::floor(x)), static_cast(std::floor(y)), static_cast(std::floor(z))); 177 | if (!keep_going) return; 178 | } 179 | } 180 | } -------------------------------------------------------------------------------- /cpp/rltk/sprite.cpp: -------------------------------------------------------------------------------- 1 | #include "sprite.hpp" 2 | 3 | sprite_t::sprite_t(SDL_Renderer *renderer, const char * filename) { 4 | image = IMG_Load(filename); 5 | if (!image) 6 | { 7 | printf("IMG_Load Error: %s\n", IMG_GetError()); 8 | } else { 9 | tex = SDL_CreateTextureFromSurface(renderer, image); 10 | } 11 | } 12 | 13 | void sprite_t::display(SDL_Renderer *renderer) { 14 | SDL_Rect dest = {.x = 200, .y = 100, .w = 200, .h = 200}; 15 | SDL_RenderCopy (renderer, tex, nullptr, &dest); 16 | } 17 | 18 | void sprite_t::render_subsprite(SDL_Renderer *renderer, const int &width, const int &height, const int &x, const int &y, const int &src_x, const int &src_y, uint8_t red, uint8_t green, uint8_t blue) { 19 | SDL_Rect dest = {.x = x, .y = y, .w = width, .h = height}; 20 | SDL_Rect src = {.x = src_x, .y = src_y, .w = width, .h = height}; 21 | SDL_SetTextureColorMod(tex, red, green, blue); 22 | SDL_RenderCopy (renderer, tex, &src, &dest); 23 | } 24 | 25 | sprite_t::~sprite_t() { 26 | SDL_DestroyTexture (tex); 27 | SDL_FreeSurface (image); 28 | } 29 | -------------------------------------------------------------------------------- /cpp/rltk/sprite.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class sprite_t { 7 | public: 8 | sprite_t(SDL_Renderer *renderer, const char * filename); 9 | ~sprite_t(); 10 | void display(SDL_Renderer *renderer); 11 | void render_subsprite(SDL_Renderer *renderer, const int &width, const int &height, const int &x, const int &y, const int &src_x, const int &src_y, uint8_t red=255, uint8_t green=255, uint8_t blue=255); 12 | 13 | private: 14 | SDL_Texture *tex = nullptr; 15 | SDL_Surface *image = nullptr; 16 | }; 17 | -------------------------------------------------------------------------------- /cpp/rltk/visibility.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /* RLTK (RogueLike Tool Kit) 1.00 3 | * Copyright (c) 2016-Present, Bracket Productions. 4 | * Licensed under the MIT license - see LICENSE file. 5 | * 6 | * Provides a wrapper for bitmap fonts. 7 | */ 8 | 9 | #include 10 | #include "geometry.hpp" 11 | 12 | namespace rltk { 13 | 14 | namespace visibility_private { 15 | 16 | /* 17 | * Benchmark results from example 6 (on my desktop): 18 | * - Using line_func, it averages 0.3625 uS per line. 19 | * - Using line_func_cancellable, it averages 0.25 uS per line. 20 | * - Using a naieve float based slope, it averages 0.2 uS per line. 21 | */ 22 | 23 | template 24 | void internal_2d_sweep(const location_t_ &position, const int &range, std::function set_visible, 25 | std::function is_opaque, const std::pair offset) 26 | { 27 | bool blocked = false; 28 | const int start_x = navigator_t::get_x(position); 29 | const int start_y = navigator_t::get_y(position); 30 | const int end_x = start_x + offset.first; 31 | const int end_y = start_y + offset.second; 32 | 33 | line_func_cancellable(start_x, start_y, end_x, end_y, [&blocked, &is_opaque, &set_visible, &range, &position] (int X, int Y) { 34 | if (blocked) return false; 35 | float distance = distance2d(static_cast(position.x), static_cast(position.y), X, Y); 36 | if (distance <= range) { 37 | location_t_ pos = navigator_t::get_xy(X,Y); 38 | if (!blocked) set_visible(pos); 39 | if (!is_opaque(pos)) blocked = true; 40 | } 41 | return true; 42 | }); 43 | } 44 | 45 | } 46 | 47 | /* Simple all-direction visibility sweep in 2 dimensions. This requires that your location_t utilize an x and y 48 | * component. Parameters: 49 | * position - where you are sweeping from. 50 | * range - the number of tiles you can traverse. 51 | * set_visible - a callback (such as bool set_visible(location_t & loc)) to say "this is visible" 52 | * is_opaque - a callback to ask your map if you can see through a tile. 53 | * 54 | * You must provide a navigator_t, just like for path finding. It must support get_x, get_y, and get_xy. 55 | */ 56 | template 57 | void visibility_sweep_2d(const location_t_ &position, const int &range, std::function set_visible, 58 | std::function is_opaque) 59 | { 60 | // You can always see yourself 61 | set_visible(position); 62 | 63 | // Box-sweep 64 | for (int i=0-range; i(position, range, set_visible, is_opaque, std::make_pair(i, 0-range)); 66 | visibility_private::internal_2d_sweep(position, range, set_visible, is_opaque, std::make_pair(i, range)); 67 | visibility_private::internal_2d_sweep(position, range, set_visible, is_opaque, std::make_pair(0-range, i)); 68 | visibility_private::internal_2d_sweep(position, range, set_visible, is_opaque, std::make_pair(range, i)); 69 | } 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /cpp/rltk/window.cpp: -------------------------------------------------------------------------------- 1 | #include "window.hpp" 2 | #include 3 | 4 | window_t::window_t() { 5 | SDL_Init(SDL_INIT_VIDEO); 6 | SDL_CreateWindowAndRenderer(terminal_width * font_size, terminal_height * font_size, 0, &window, &renderer); 7 | SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); 8 | SDL_RenderClear(renderer); 9 | font = std::make_unique(renderer, "assets/terminal16x16.png"); 10 | 11 | cls(); 12 | } 13 | 14 | window_t::~window_t() { 15 | 16 | } 17 | 18 | void window_t::present() { 19 | SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); 20 | SDL_RenderClear(renderer); 21 | int x = 0; 22 | int y = 0; 23 | for (int i=0; irender_subsprite(renderer, font_size, font_size, x*font_size, y*font_size, src_x, src_y, red, green, blue ); 31 | 32 | ++x; 33 | if (x >= terminal_width) { 34 | x = 0; 35 | ++y; 36 | } 37 | } 38 | 39 | SDL_RenderPresent(renderer); 40 | } 41 | 42 | void window_t::poll() { 43 | keypress.clear(); 44 | 45 | SDL_Event event; 46 | while( SDL_PollEvent( &event ) ){ 47 | switch( event.type ){ 48 | case SDL_KEYDOWN: 49 | keypress.insert(event.key.keysym.sym); 50 | break; 51 | 52 | case SDL_MOUSEBUTTONDOWN: 53 | if (event.button.button == SDL_BUTTON_LEFT) { 54 | clicked = true; 55 | } break; 56 | 57 | case SDL_MOUSEBUTTONUP: 58 | if (event.button.button == SDL_BUTTON_LEFT) { 59 | clicked = false; 60 | } break; 61 | 62 | case SDL_MOUSEMOTION: { 63 | int mouseX = event.motion.x; 64 | int mouseY = event.motion.y; 65 | 66 | mouse_x = mouseX / font_size; 67 | mouse_y = mouseY / font_size; 68 | } break; 69 | 70 | default: 71 | break; 72 | } 73 | } 74 | } 75 | 76 | bool window_t::is_key_down(const int &symbol) { 77 | auto finder = keypress.find(symbol); 78 | if (finder == keypress.end()) return false; 79 | return true; 80 | } 81 | 82 | void window_t::cls() { 83 | std::fill(console.begin(), console.end(), ' '); 84 | std::fill(colors.begin(), colors.end(), 255); 85 | } 86 | 87 | int window_t::loc(const int &x, const int &y) { 88 | return (y * terminal_width) + x; 89 | } 90 | 91 | void window_t::set(const int &x, const int &y, const uint8_t &glyph, int r, int g, int b) { 92 | const int idx = loc(x,y); 93 | const int idx3 = idx*3; 94 | console[idx] = glyph; 95 | colors[idx3] = r; 96 | colors[idx3+1] = g; 97 | colors[idx3+2] = b; 98 | } 99 | 100 | void window_t::print(const int &x, const int &y, const std::string &msg, int r, int g, int b) { 101 | int idx = loc(x,y); 102 | for (int i=0; i 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "sprite.hpp" 10 | 11 | constexpr int font_size = 16; 12 | constexpr int terminal_width = 80; 13 | constexpr int terminal_height = 40; 14 | 15 | class window_t { 16 | public: 17 | window_t(); 18 | ~window_t(); 19 | void present(); 20 | void poll(); 21 | 22 | // Terminal commands 23 | void cls(); 24 | int loc(const int &x, const int &y); 25 | void print(const int &x, const int &y, const std::string &msg, int r=255, int g=255, int b=255); 26 | void print_center(const int &y, const std::string &msg, int r=255, int g=255, int b=255); 27 | void set(const int &x, const int &y, const uint8_t &glyph, int r=255, int g=255, int b=255); 28 | void box(const int x, const int y, const int w, const int h, int r=255, int g=255, int b=255, bool double_lines=false); 29 | 30 | // Keyboard input 31 | bool is_key_down(const int &symbol); 32 | int mouse_x = 0; 33 | int mouse_y = 0; 34 | bool clicked = false; 35 | 36 | private: 37 | SDL_Window *window; 38 | SDL_Renderer *renderer; 39 | std::unique_ptr font; 40 | 41 | std::array console; 42 | std::array colors; 43 | 44 | std::set keypress; 45 | }; 46 | -------------------------------------------------------------------------------- /node/dankest-dungeon/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | var favicon = require('serve-favicon'); 4 | var logger = require('morgan'); 5 | var cookieParser = require('cookie-parser'); 6 | var bodyParser = require('body-parser'); 7 | 8 | var routes = require('./routes/index'); 9 | var users = require('./routes/users'); 10 | 11 | var app = express(); 12 | 13 | // view engine setup 14 | app.set('views', path.join(__dirname, 'views')); 15 | app.set('view engine', 'jade'); 16 | app.use(bodyParser.json({limit: '5mb'})); 17 | app.use(bodyParser.urlencoded({limit: '5mb'})); 18 | 19 | // uncomment after placing your favicon in /public 20 | //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); 21 | app.use(logger('dev')); 22 | app.use(bodyParser.json()); 23 | app.use(bodyParser.urlencoded({ extended: false })); 24 | app.use(cookieParser()); 25 | app.use(express.static(path.join(__dirname, 'public'))); 26 | 27 | app.use('/', routes); 28 | app.use('/users', users); 29 | 30 | // catch 404 and forward to error handler 31 | app.use(function(req, res, next) { 32 | var err = new Error('Not Found'); 33 | err.status = 404; 34 | next(err); 35 | }); 36 | 37 | // error handlers 38 | 39 | // development error handler 40 | // will print stacktrace 41 | if (app.get('env') === 'development') { 42 | app.use(function(err, req, res, next) { 43 | res.status(err.status || 500); 44 | res.render('error', { 45 | message: err.message, 46 | error: err 47 | }); 48 | }); 49 | } 50 | 51 | // production error handler 52 | // no stacktraces leaked to user 53 | app.use(function(err, req, res, next) { 54 | res.status(err.status || 500); 55 | res.render('error', { 56 | message: err.message, 57 | error: {} 58 | }); 59 | }); 60 | 61 | 62 | module.exports = app; 63 | -------------------------------------------------------------------------------- /node/dankest-dungeon/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('dankest-dungeon:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '9000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /node/dankest-dungeon/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-fs6-server", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "supervisor ./bin/www" 7 | }, 8 | "dependencies": { 9 | "body-parser": "^1.17.2", 10 | "compression": "^1.7.0", 11 | "cookie-parser": "~1.4.3", 12 | "cors": "^2.8.4", 13 | "debug": "~2.6.3", 14 | "express": "~4.15.2", 15 | "jade": "~1.11.0", 16 | "morgan": "~1.8.1", 17 | "node-geocoder": "^3.18.0", 18 | "pg": "^6.4.2", 19 | "serve-favicon": "~2.4.2", 20 | "websocket": "^1.0.24" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /node/dankest-dungeon/public/dankest.data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebracket/dankestdungeon/e10fbc298efc2ec3652548633ad30ab79d2b9742/node/dankest-dungeon/public/dankest.data -------------------------------------------------------------------------------- /node/dankest-dungeon/public/dankest.js.mem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebracket/dankestdungeon/e10fbc298efc2ec3652548633ad30ab79d2b9742/node/dankest-dungeon/public/dankest.js.mem -------------------------------------------------------------------------------- /node/dankest-dungeon/public/game.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dankest Dungeon 1.0 - A 7 Day Bracket Production 5 | 6 | 7 | 8 | 9 | 16 | 17 | 18 | 19 | 20 |
21 |

22 | Welcome, . Can you build the Dankest Dungeon? 23 | View Leaderboards 24 |

25 | 26 | 27 | 28 |
29 | 30 | 36 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /node/dankest-dungeon/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dankest Dungeon 1.0 - A 7 Day Bracket Production 5 | 6 | 7 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 |

Dankest Dungeon 1.0

24 | 25 |

Dismayed by the state of politics in the land of Yonder, you decided to research political 26 | science. With no source material available beyond the sacred texts of Vulpes Nuntium, 27 | you conclude that only the dankest of memes can be used to win back sanity in your lands. Since 28 | dank things are generally found underground, you set forth into the Dankest Dungeon, 29 | seeking to find the Amulet of Winning - and restore political sanity to the land. 30 |

31 | 32 |

33 | This is a 7-Day Roguelike, created as an exercise in finding out what Emscripten 34 | can do these days. Please enjoy it, and provide feedback to /u/thebracket on Reddit. This program is 35 | provided as-is. No warranty, express or implied, is provided. Any resemblance to real-life characters 36 | is purely coincidental. In other words - have fun, please don't sue me, and remember that this was 37 | written for fun not security - so please don't give me any secrets to protect! 38 |

39 | 40 |

Brave Adventurer - Identify Yourself!

41 |

This game is built around designing dungeons, and sharing them with other adventurers. As such, you 42 | need a user account - so we know which dungeons are yours. Very little effort has been put into 43 | security, so please don't use a password that you care about, or use for anything important! 44 |

45 |

46 | You can also login as guest with the password guest if you 47 | feel uncomfortable with logins. This will prevent you from being attributed for any levels 48 | you create, but otherwise has full access. 49 |

50 |
51 | Returning Adventurer Login: 52 | 53 | 54 | 55 | 56 | 57 |
58 |
59 | New Adventurer Login: 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
68 |
69 | 70 | 76 | 77 | -------------------------------------------------------------------------------- /node/dankest-dungeon/public/javascripts/game.js: -------------------------------------------------------------------------------- 1 | function setup() { 2 | var token = localStorage.getItem("dankest_token"); 3 | var username = localStorage.getItem("dankest_user"); 4 | 5 | $("#greeting").html(username); 6 | 7 | $.ajax({ 8 | type: "POST", 9 | url: "api/CheckToken", 10 | data: {token: token}, 11 | success: function (data) { 12 | if (data != "OK") { 13 | localStorage.removeItem("dankest_token"); 14 | window.location = "index.html"; 15 | } 16 | }, 17 | dataType: "json" 18 | }); 19 | } 20 | 21 | function saveLevel(levelName, levelData) { 22 | if (levelName.length < 1) levelName = "Nameless"; 23 | $.ajax({ 24 | type: "POST", 25 | url: "api/SaveDungeon", 26 | data: {name: levelName, data: levelData, token: localStorage.getItem("dankest_token")}, 27 | success: function (data) { 28 | console.log(data); 29 | }, 30 | dataType: "json" 31 | }); 32 | } 33 | 34 | function getRating() { 35 | var rating = -1; 36 | while (rating <0 || rating > 5) { 37 | var temp = prompt("Please rate this level from 0 to 5 (5 being best)", "3"); 38 | try { 39 | rating = parseInt(temp); 40 | } catch (e) { 41 | rating = -1; 42 | } 43 | } 44 | return rating; 45 | } 46 | 47 | function winGame(turns, levelId) { 48 | var token = localStorage.getItem("dankest_token"); 49 | alert("Congratulations! You have beaten the dungeon, in " + turns + " turns."); 50 | var rating = getRating(); 51 | $.ajax({ 52 | type: "POST", 53 | url: "api/SaveRunthrough", 54 | data: {token: token, levelId: levelId, turns: turns, rating:rating, result: "WIN"}, 55 | success: function (data) { 56 | console.log(data); 57 | }, 58 | dataType: "json" 59 | }); 60 | } 61 | 62 | function loseGame(turns, levelId) { 63 | var token = localStorage.getItem("dankest_token"); 64 | alert("You are dead! You lasted " + turns + " turns. Hopefully you can do better next time."); 65 | var rating = getRating(); 66 | $.ajax({ 67 | type: "POST", 68 | url: "api/SaveRunthrough", 69 | data: {token: token, levelId: levelId, turns: turns, rating:rating, result: "LOSE"}, 70 | success: function (data) { 71 | console.log(data); 72 | }, 73 | dataType: "json" 74 | }); 75 | } -------------------------------------------------------------------------------- /node/dankest-dungeon/public/javascripts/leaderboards.js: -------------------------------------------------------------------------------- 1 | function setup() { 2 | var token = localStorage.getItem("dankest_token"); 3 | var username = localStorage.getItem("dankest_user"); 4 | 5 | $("#greeting").html(username); 6 | 7 | $.ajax({ 8 | type: "POST", 9 | url: "api/CheckToken", 10 | data: {token: token}, 11 | success: function (data) { 12 | if (data != "OK") { 13 | localStorage.removeItem("dankest_token"); 14 | window.location = "index.html"; 15 | } else { 16 | $.ajax({ 17 | type: "GET", 18 | url: "/api/Leaderboard", 19 | data: {token: localStorage.getItem("dankest_token")}, 20 | success: function (data) { 21 | var body = ""; 22 | 23 | body += "

Top Creators

" 24 | body += ""; 25 | for (var i=0; i"; 27 | } 28 | body += "
AdventurerLevels Created
" + data.creators[i].count + "
"; 29 | 30 | body += "

Top Levels

" 31 | body += ""; 32 | for (var i=0; i"; 34 | } 35 | body += "
LevelRating
" + data.topDungeons[i].rating + "
"; 36 | 37 | body += "

Most Frequent Players

" 38 | body += ""; 39 | for (var i=0; i"; 41 | } 42 | body += "
AdventurersPlays
" + data.frequent[i].count + "
"; 43 | 44 | 45 | $("#boards").html(body); 46 | }, 47 | dataType: "json" 48 | }); 49 | } 50 | }, 51 | dataType: "json" 52 | }); 53 | } 54 | -------------------------------------------------------------------------------- /node/dankest-dungeon/public/javascripts/login.js: -------------------------------------------------------------------------------- 1 | function setup() { 2 | var loginButton = document.getElementById('loginBtn'); 3 | var newUserButton = document.getElementById('newUserBtn'); 4 | 5 | var usernameField = document.getElementById('username'); 6 | 7 | // Do we have a cached user? 8 | var cachedUsername = localStorage.getItem("dankest_user"); 9 | usernameField.value = cachedUsername; 10 | 11 | // Set focus 12 | usernameField.focus(); 13 | 14 | loginButton.onclick = function(e) { 15 | var user = { 16 | username : document.getElementById("username").value, 17 | password: md5(document.getElementById("password").value) 18 | }; 19 | $.ajax({ 20 | type: "POST", 21 | url: "api/Login", 22 | data: user, 23 | success: function (data) { 24 | if (data == "Error: invalid username or password") { 25 | alert("Error: invalid username or password"); 26 | } else { 27 | localStorage.setItem("dankest_user", user.username); 28 | localStorage.setItem("dankest_token", data.token); 29 | window.location = "game.html"; 30 | } 31 | }, 32 | dataType: "json" 33 | }); 34 | }; 35 | newUserButton.onclick = function(e) { 36 | var newUser = { 37 | username : document.getElementById("nusername").value, 38 | password: document.getElementById("npassword").value, 39 | password2: document.getElementById("npassword2").value 40 | }; 41 | 42 | var valid = true; 43 | var errors = ""; 44 | if (newUser.username.length < 4) { 45 | valid = false; 46 | errors += "Please select a username of at least 4 characters in length." 47 | } 48 | if (newUser.password.length < 4) { 49 | valid = false; 50 | errors += "Please select a password of 7 characters or more." 51 | } 52 | if (newUser.password !== newUser.password2) { 53 | valid = false; 54 | errors += "Your new passwords don't match. You really do type as badly as me, sorry!"; 55 | } 56 | 57 | if (!valid) { 58 | alert(errors); 59 | return; 60 | } else { 61 | localStorage.setItem("dankest_user", newUser.username); 62 | delete newUser.password2; 63 | newUser.password = md5(newUser.password); 64 | 65 | $.ajax({ 66 | type: "POST", 67 | url: "/api/newUser", 68 | data: newUser, 69 | success: function (data) { 70 | console.log("Returned data", data); 71 | if (data == "Error: username is taken") { 72 | alert("Sorry - that username is taken. Try another one."); 73 | } else { 74 | localStorage.setItem("dankest_token", data.token); 75 | window.location = "game.html"; 76 | } 77 | }, 78 | dataType: "json" 79 | }); 80 | } 81 | }; 82 | } -------------------------------------------------------------------------------- /node/dankest-dungeon/public/javascripts/md5.js: -------------------------------------------------------------------------------- 1 | /** 2 | * [js-md5]{@link https://github.com/emn178/js-md5} 3 | * 4 | * @namespace md5 5 | * @version 0.7.3 6 | * @author Chen, Yi-Cyuan [emn178@gmail.com] 7 | * @copyright Chen, Yi-Cyuan 2014-2017 8 | * @license MIT 9 | */ 10 | (function () { 11 | 'use strict'; 12 | 13 | var ERROR = 'input is invalid type'; 14 | var WINDOW = typeof window === 'object'; 15 | var root = WINDOW ? window : {}; 16 | if (root.JS_MD5_NO_WINDOW) { 17 | WINDOW = false; 18 | } 19 | var WEB_WORKER = !WINDOW && typeof self === 'object'; 20 | var NODE_JS = !root.JS_MD5_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node; 21 | if (NODE_JS) { 22 | root = global; 23 | } else if (WEB_WORKER) { 24 | root = self; 25 | } 26 | var COMMON_JS = !root.JS_MD5_NO_COMMON_JS && typeof module === 'object' && module.exports; 27 | var AMD = typeof define === 'function' && define.amd; 28 | var ARRAY_BUFFER = !root.JS_MD5_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined'; 29 | var HEX_CHARS = '0123456789abcdef'.split(''); 30 | var EXTRA = [128, 32768, 8388608, -2147483648]; 31 | var SHIFT = [0, 8, 16, 24]; 32 | var OUTPUT_TYPES = ['hex', 'array', 'digest', 'buffer', 'arrayBuffer', 'base64']; 33 | var BASE64_ENCODE_CHAR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split(''); 34 | 35 | var blocks = [], buffer8; 36 | if (ARRAY_BUFFER) { 37 | var buffer = new ArrayBuffer(68); 38 | buffer8 = new Uint8Array(buffer); 39 | blocks = new Uint32Array(buffer); 40 | } 41 | 42 | if (root.JS_MD5_NO_NODE_JS || !Array.isArray) { 43 | Array.isArray = function (obj) { 44 | return Object.prototype.toString.call(obj) === '[object Array]'; 45 | }; 46 | } 47 | 48 | if (ARRAY_BUFFER && (root.JS_MD5_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) { 49 | ArrayBuffer.isView = function (obj) { 50 | return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer; 51 | }; 52 | } 53 | 54 | /** 55 | * @method hex 56 | * @memberof md5 57 | * @description Output hash as hex string 58 | * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash 59 | * @returns {String} Hex string 60 | * @example 61 | * md5.hex('The quick brown fox jumps over the lazy dog'); 62 | * // equal to 63 | * md5('The quick brown fox jumps over the lazy dog'); 64 | */ 65 | /** 66 | * @method digest 67 | * @memberof md5 68 | * @description Output hash as bytes array 69 | * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash 70 | * @returns {Array} Bytes array 71 | * @example 72 | * md5.digest('The quick brown fox jumps over the lazy dog'); 73 | */ 74 | /** 75 | * @method array 76 | * @memberof md5 77 | * @description Output hash as bytes array 78 | * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash 79 | * @returns {Array} Bytes array 80 | * @example 81 | * md5.array('The quick brown fox jumps over the lazy dog'); 82 | */ 83 | /** 84 | * @method arrayBuffer 85 | * @memberof md5 86 | * @description Output hash as ArrayBuffer 87 | * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash 88 | * @returns {ArrayBuffer} ArrayBuffer 89 | * @example 90 | * md5.arrayBuffer('The quick brown fox jumps over the lazy dog'); 91 | */ 92 | /** 93 | * @method buffer 94 | * @deprecated This maybe confuse with Buffer in node.js. Please use arrayBuffer instead. 95 | * @memberof md5 96 | * @description Output hash as ArrayBuffer 97 | * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash 98 | * @returns {ArrayBuffer} ArrayBuffer 99 | * @example 100 | * md5.buffer('The quick brown fox jumps over the lazy dog'); 101 | */ 102 | /** 103 | * @method base64 104 | * @memberof md5 105 | * @description Output hash as base64 string 106 | * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash 107 | * @returns {String} base64 string 108 | * @example 109 | * md5.base64('The quick brown fox jumps over the lazy dog'); 110 | */ 111 | var createOutputMethod = function (outputType) { 112 | return function (message) { 113 | return new Md5(true).update(message)[outputType](); 114 | }; 115 | }; 116 | 117 | /** 118 | * @method create 119 | * @memberof md5 120 | * @description Create Md5 object 121 | * @returns {Md5} Md5 object. 122 | * @example 123 | * var hash = md5.create(); 124 | */ 125 | /** 126 | * @method update 127 | * @memberof md5 128 | * @description Create and update Md5 object 129 | * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash 130 | * @returns {Md5} Md5 object. 131 | * @example 132 | * var hash = md5.update('The quick brown fox jumps over the lazy dog'); 133 | * // equal to 134 | * var hash = md5.create(); 135 | * hash.update('The quick brown fox jumps over the lazy dog'); 136 | */ 137 | var createMethod = function () { 138 | var method = createOutputMethod('hex'); 139 | if (NODE_JS) { 140 | method = nodeWrap(method); 141 | } 142 | method.create = function () { 143 | return new Md5(); 144 | }; 145 | method.update = function (message) { 146 | return method.create().update(message); 147 | }; 148 | for (var i = 0; i < OUTPUT_TYPES.length; ++i) { 149 | var type = OUTPUT_TYPES[i]; 150 | method[type] = createOutputMethod(type); 151 | } 152 | return method; 153 | }; 154 | 155 | var nodeWrap = function (method) { 156 | var crypto = eval("require('crypto')"); 157 | var Buffer = eval("require('buffer').Buffer"); 158 | var nodeMethod = function (message) { 159 | if (typeof message === 'string') { 160 | return crypto.createHash('md5').update(message, 'utf8').digest('hex'); 161 | } else { 162 | if (message === null || message === undefined) { 163 | throw ERROR; 164 | } else if (message.constructor === ArrayBuffer) { 165 | message = new Uint8Array(message); 166 | } 167 | } 168 | if (Array.isArray(message) || ArrayBuffer.isView(message) || 169 | message.constructor === Buffer) { 170 | return crypto.createHash('md5').update(new Buffer(message)).digest('hex'); 171 | } else { 172 | return method(message); 173 | } 174 | }; 175 | return nodeMethod; 176 | }; 177 | 178 | /** 179 | * Md5 class 180 | * @class Md5 181 | * @description This is internal class. 182 | * @see {@link md5.create} 183 | */ 184 | function Md5(sharedMemory) { 185 | if (sharedMemory) { 186 | blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = 187 | blocks[4] = blocks[5] = blocks[6] = blocks[7] = 188 | blocks[8] = blocks[9] = blocks[10] = blocks[11] = 189 | blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; 190 | this.blocks = blocks; 191 | this.buffer8 = buffer8; 192 | } else { 193 | if (ARRAY_BUFFER) { 194 | var buffer = new ArrayBuffer(68); 195 | this.buffer8 = new Uint8Array(buffer); 196 | this.blocks = new Uint32Array(buffer); 197 | } else { 198 | this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 199 | } 200 | } 201 | this.h0 = this.h1 = this.h2 = this.h3 = this.start = this.bytes = this.hBytes = 0; 202 | this.finalized = this.hashed = false; 203 | this.first = true; 204 | } 205 | 206 | /** 207 | * @method update 208 | * @memberof Md5 209 | * @instance 210 | * @description Update hash 211 | * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash 212 | * @returns {Md5} Md5 object. 213 | * @see {@link md5.update} 214 | */ 215 | Md5.prototype.update = function (message) { 216 | if (this.finalized) { 217 | return; 218 | } 219 | 220 | var notString, type = typeof message; 221 | if (type !== 'string') { 222 | if (type === 'object') { 223 | if (message === null) { 224 | throw ERROR; 225 | } else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) { 226 | message = new Uint8Array(message); 227 | } else if (!Array.isArray(message)) { 228 | if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) { 229 | throw ERROR; 230 | } 231 | } 232 | } else { 233 | throw ERROR; 234 | } 235 | notString = true; 236 | } 237 | var code, index = 0, i, length = message.length, blocks = this.blocks; 238 | var buffer8 = this.buffer8; 239 | 240 | while (index < length) { 241 | if (this.hashed) { 242 | this.hashed = false; 243 | blocks[0] = blocks[16]; 244 | blocks[16] = blocks[1] = blocks[2] = blocks[3] = 245 | blocks[4] = blocks[5] = blocks[6] = blocks[7] = 246 | blocks[8] = blocks[9] = blocks[10] = blocks[11] = 247 | blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; 248 | } 249 | 250 | if (notString) { 251 | if (ARRAY_BUFFER) { 252 | for (i = this.start; index < length && i < 64; ++index) { 253 | buffer8[i++] = message[index]; 254 | } 255 | } else { 256 | for (i = this.start; index < length && i < 64; ++index) { 257 | blocks[i >> 2] |= message[index] << SHIFT[i++ & 3]; 258 | } 259 | } 260 | } else { 261 | if (ARRAY_BUFFER) { 262 | for (i = this.start; index < length && i < 64; ++index) { 263 | code = message.charCodeAt(index); 264 | if (code < 0x80) { 265 | buffer8[i++] = code; 266 | } else if (code < 0x800) { 267 | buffer8[i++] = 0xc0 | (code >> 6); 268 | buffer8[i++] = 0x80 | (code & 0x3f); 269 | } else if (code < 0xd800 || code >= 0xe000) { 270 | buffer8[i++] = 0xe0 | (code >> 12); 271 | buffer8[i++] = 0x80 | ((code >> 6) & 0x3f); 272 | buffer8[i++] = 0x80 | (code & 0x3f); 273 | } else { 274 | code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff)); 275 | buffer8[i++] = 0xf0 | (code >> 18); 276 | buffer8[i++] = 0x80 | ((code >> 12) & 0x3f); 277 | buffer8[i++] = 0x80 | ((code >> 6) & 0x3f); 278 | buffer8[i++] = 0x80 | (code & 0x3f); 279 | } 280 | } 281 | } else { 282 | for (i = this.start; index < length && i < 64; ++index) { 283 | code = message.charCodeAt(index); 284 | if (code < 0x80) { 285 | blocks[i >> 2] |= code << SHIFT[i++ & 3]; 286 | } else if (code < 0x800) { 287 | blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3]; 288 | blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; 289 | } else if (code < 0xd800 || code >= 0xe000) { 290 | blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3]; 291 | blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; 292 | blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; 293 | } else { 294 | code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff)); 295 | blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3]; 296 | blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3]; 297 | blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; 298 | blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; 299 | } 300 | } 301 | } 302 | } 303 | this.lastByteIndex = i; 304 | this.bytes += i - this.start; 305 | if (i >= 64) { 306 | this.start = i - 64; 307 | this.hash(); 308 | this.hashed = true; 309 | } else { 310 | this.start = i; 311 | } 312 | } 313 | if (this.bytes > 4294967295) { 314 | this.hBytes += this.bytes / 4294967296 << 0; 315 | this.bytes = this.bytes % 4294967296; 316 | } 317 | return this; 318 | }; 319 | 320 | Md5.prototype.finalize = function () { 321 | if (this.finalized) { 322 | return; 323 | } 324 | this.finalized = true; 325 | var blocks = this.blocks, i = this.lastByteIndex; 326 | blocks[i >> 2] |= EXTRA[i & 3]; 327 | if (i >= 56) { 328 | if (!this.hashed) { 329 | this.hash(); 330 | } 331 | blocks[0] = blocks[16]; 332 | blocks[16] = blocks[1] = blocks[2] = blocks[3] = 333 | blocks[4] = blocks[5] = blocks[6] = blocks[7] = 334 | blocks[8] = blocks[9] = blocks[10] = blocks[11] = 335 | blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; 336 | } 337 | blocks[14] = this.bytes << 3; 338 | blocks[15] = this.hBytes << 3 | this.bytes >>> 29; 339 | this.hash(); 340 | }; 341 | 342 | Md5.prototype.hash = function () { 343 | var a, b, c, d, bc, da, blocks = this.blocks; 344 | 345 | if (this.first) { 346 | a = blocks[0] - 680876937; 347 | a = (a << 7 | a >>> 25) - 271733879 << 0; 348 | d = (-1732584194 ^ a & 2004318071) + blocks[1] - 117830708; 349 | d = (d << 12 | d >>> 20) + a << 0; 350 | c = (-271733879 ^ (d & (a ^ -271733879))) + blocks[2] - 1126478375; 351 | c = (c << 17 | c >>> 15) + d << 0; 352 | b = (a ^ (c & (d ^ a))) + blocks[3] - 1316259209; 353 | b = (b << 22 | b >>> 10) + c << 0; 354 | } else { 355 | a = this.h0; 356 | b = this.h1; 357 | c = this.h2; 358 | d = this.h3; 359 | a += (d ^ (b & (c ^ d))) + blocks[0] - 680876936; 360 | a = (a << 7 | a >>> 25) + b << 0; 361 | d += (c ^ (a & (b ^ c))) + blocks[1] - 389564586; 362 | d = (d << 12 | d >>> 20) + a << 0; 363 | c += (b ^ (d & (a ^ b))) + blocks[2] + 606105819; 364 | c = (c << 17 | c >>> 15) + d << 0; 365 | b += (a ^ (c & (d ^ a))) + blocks[3] - 1044525330; 366 | b = (b << 22 | b >>> 10) + c << 0; 367 | } 368 | 369 | a += (d ^ (b & (c ^ d))) + blocks[4] - 176418897; 370 | a = (a << 7 | a >>> 25) + b << 0; 371 | d += (c ^ (a & (b ^ c))) + blocks[5] + 1200080426; 372 | d = (d << 12 | d >>> 20) + a << 0; 373 | c += (b ^ (d & (a ^ b))) + blocks[6] - 1473231341; 374 | c = (c << 17 | c >>> 15) + d << 0; 375 | b += (a ^ (c & (d ^ a))) + blocks[7] - 45705983; 376 | b = (b << 22 | b >>> 10) + c << 0; 377 | a += (d ^ (b & (c ^ d))) + blocks[8] + 1770035416; 378 | a = (a << 7 | a >>> 25) + b << 0; 379 | d += (c ^ (a & (b ^ c))) + blocks[9] - 1958414417; 380 | d = (d << 12 | d >>> 20) + a << 0; 381 | c += (b ^ (d & (a ^ b))) + blocks[10] - 42063; 382 | c = (c << 17 | c >>> 15) + d << 0; 383 | b += (a ^ (c & (d ^ a))) + blocks[11] - 1990404162; 384 | b = (b << 22 | b >>> 10) + c << 0; 385 | a += (d ^ (b & (c ^ d))) + blocks[12] + 1804603682; 386 | a = (a << 7 | a >>> 25) + b << 0; 387 | d += (c ^ (a & (b ^ c))) + blocks[13] - 40341101; 388 | d = (d << 12 | d >>> 20) + a << 0; 389 | c += (b ^ (d & (a ^ b))) + blocks[14] - 1502002290; 390 | c = (c << 17 | c >>> 15) + d << 0; 391 | b += (a ^ (c & (d ^ a))) + blocks[15] + 1236535329; 392 | b = (b << 22 | b >>> 10) + c << 0; 393 | a += (c ^ (d & (b ^ c))) + blocks[1] - 165796510; 394 | a = (a << 5 | a >>> 27) + b << 0; 395 | d += (b ^ (c & (a ^ b))) + blocks[6] - 1069501632; 396 | d = (d << 9 | d >>> 23) + a << 0; 397 | c += (a ^ (b & (d ^ a))) + blocks[11] + 643717713; 398 | c = (c << 14 | c >>> 18) + d << 0; 399 | b += (d ^ (a & (c ^ d))) + blocks[0] - 373897302; 400 | b = (b << 20 | b >>> 12) + c << 0; 401 | a += (c ^ (d & (b ^ c))) + blocks[5] - 701558691; 402 | a = (a << 5 | a >>> 27) + b << 0; 403 | d += (b ^ (c & (a ^ b))) + blocks[10] + 38016083; 404 | d = (d << 9 | d >>> 23) + a << 0; 405 | c += (a ^ (b & (d ^ a))) + blocks[15] - 660478335; 406 | c = (c << 14 | c >>> 18) + d << 0; 407 | b += (d ^ (a & (c ^ d))) + blocks[4] - 405537848; 408 | b = (b << 20 | b >>> 12) + c << 0; 409 | a += (c ^ (d & (b ^ c))) + blocks[9] + 568446438; 410 | a = (a << 5 | a >>> 27) + b << 0; 411 | d += (b ^ (c & (a ^ b))) + blocks[14] - 1019803690; 412 | d = (d << 9 | d >>> 23) + a << 0; 413 | c += (a ^ (b & (d ^ a))) + blocks[3] - 187363961; 414 | c = (c << 14 | c >>> 18) + d << 0; 415 | b += (d ^ (a & (c ^ d))) + blocks[8] + 1163531501; 416 | b = (b << 20 | b >>> 12) + c << 0; 417 | a += (c ^ (d & (b ^ c))) + blocks[13] - 1444681467; 418 | a = (a << 5 | a >>> 27) + b << 0; 419 | d += (b ^ (c & (a ^ b))) + blocks[2] - 51403784; 420 | d = (d << 9 | d >>> 23) + a << 0; 421 | c += (a ^ (b & (d ^ a))) + blocks[7] + 1735328473; 422 | c = (c << 14 | c >>> 18) + d << 0; 423 | b += (d ^ (a & (c ^ d))) + blocks[12] - 1926607734; 424 | b = (b << 20 | b >>> 12) + c << 0; 425 | bc = b ^ c; 426 | a += (bc ^ d) + blocks[5] - 378558; 427 | a = (a << 4 | a >>> 28) + b << 0; 428 | d += (bc ^ a) + blocks[8] - 2022574463; 429 | d = (d << 11 | d >>> 21) + a << 0; 430 | da = d ^ a; 431 | c += (da ^ b) + blocks[11] + 1839030562; 432 | c = (c << 16 | c >>> 16) + d << 0; 433 | b += (da ^ c) + blocks[14] - 35309556; 434 | b = (b << 23 | b >>> 9) + c << 0; 435 | bc = b ^ c; 436 | a += (bc ^ d) + blocks[1] - 1530992060; 437 | a = (a << 4 | a >>> 28) + b << 0; 438 | d += (bc ^ a) + blocks[4] + 1272893353; 439 | d = (d << 11 | d >>> 21) + a << 0; 440 | da = d ^ a; 441 | c += (da ^ b) + blocks[7] - 155497632; 442 | c = (c << 16 | c >>> 16) + d << 0; 443 | b += (da ^ c) + blocks[10] - 1094730640; 444 | b = (b << 23 | b >>> 9) + c << 0; 445 | bc = b ^ c; 446 | a += (bc ^ d) + blocks[13] + 681279174; 447 | a = (a << 4 | a >>> 28) + b << 0; 448 | d += (bc ^ a) + blocks[0] - 358537222; 449 | d = (d << 11 | d >>> 21) + a << 0; 450 | da = d ^ a; 451 | c += (da ^ b) + blocks[3] - 722521979; 452 | c = (c << 16 | c >>> 16) + d << 0; 453 | b += (da ^ c) + blocks[6] + 76029189; 454 | b = (b << 23 | b >>> 9) + c << 0; 455 | bc = b ^ c; 456 | a += (bc ^ d) + blocks[9] - 640364487; 457 | a = (a << 4 | a >>> 28) + b << 0; 458 | d += (bc ^ a) + blocks[12] - 421815835; 459 | d = (d << 11 | d >>> 21) + a << 0; 460 | da = d ^ a; 461 | c += (da ^ b) + blocks[15] + 530742520; 462 | c = (c << 16 | c >>> 16) + d << 0; 463 | b += (da ^ c) + blocks[2] - 995338651; 464 | b = (b << 23 | b >>> 9) + c << 0; 465 | a += (c ^ (b | ~d)) + blocks[0] - 198630844; 466 | a = (a << 6 | a >>> 26) + b << 0; 467 | d += (b ^ (a | ~c)) + blocks[7] + 1126891415; 468 | d = (d << 10 | d >>> 22) + a << 0; 469 | c += (a ^ (d | ~b)) + blocks[14] - 1416354905; 470 | c = (c << 15 | c >>> 17) + d << 0; 471 | b += (d ^ (c | ~a)) + blocks[5] - 57434055; 472 | b = (b << 21 | b >>> 11) + c << 0; 473 | a += (c ^ (b | ~d)) + blocks[12] + 1700485571; 474 | a = (a << 6 | a >>> 26) + b << 0; 475 | d += (b ^ (a | ~c)) + blocks[3] - 1894986606; 476 | d = (d << 10 | d >>> 22) + a << 0; 477 | c += (a ^ (d | ~b)) + blocks[10] - 1051523; 478 | c = (c << 15 | c >>> 17) + d << 0; 479 | b += (d ^ (c | ~a)) + blocks[1] - 2054922799; 480 | b = (b << 21 | b >>> 11) + c << 0; 481 | a += (c ^ (b | ~d)) + blocks[8] + 1873313359; 482 | a = (a << 6 | a >>> 26) + b << 0; 483 | d += (b ^ (a | ~c)) + blocks[15] - 30611744; 484 | d = (d << 10 | d >>> 22) + a << 0; 485 | c += (a ^ (d | ~b)) + blocks[6] - 1560198380; 486 | c = (c << 15 | c >>> 17) + d << 0; 487 | b += (d ^ (c | ~a)) + blocks[13] + 1309151649; 488 | b = (b << 21 | b >>> 11) + c << 0; 489 | a += (c ^ (b | ~d)) + blocks[4] - 145523070; 490 | a = (a << 6 | a >>> 26) + b << 0; 491 | d += (b ^ (a | ~c)) + blocks[11] - 1120210379; 492 | d = (d << 10 | d >>> 22) + a << 0; 493 | c += (a ^ (d | ~b)) + blocks[2] + 718787259; 494 | c = (c << 15 | c >>> 17) + d << 0; 495 | b += (d ^ (c | ~a)) + blocks[9] - 343485551; 496 | b = (b << 21 | b >>> 11) + c << 0; 497 | 498 | if (this.first) { 499 | this.h0 = a + 1732584193 << 0; 500 | this.h1 = b - 271733879 << 0; 501 | this.h2 = c - 1732584194 << 0; 502 | this.h3 = d + 271733878 << 0; 503 | this.first = false; 504 | } else { 505 | this.h0 = this.h0 + a << 0; 506 | this.h1 = this.h1 + b << 0; 507 | this.h2 = this.h2 + c << 0; 508 | this.h3 = this.h3 + d << 0; 509 | } 510 | }; 511 | 512 | /** 513 | * @method hex 514 | * @memberof Md5 515 | * @instance 516 | * @description Output hash as hex string 517 | * @returns {String} Hex string 518 | * @see {@link md5.hex} 519 | * @example 520 | * hash.hex(); 521 | */ 522 | Md5.prototype.hex = function () { 523 | this.finalize(); 524 | 525 | var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3; 526 | 527 | return HEX_CHARS[(h0 >> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] + 528 | HEX_CHARS[(h0 >> 12) & 0x0F] + HEX_CHARS[(h0 >> 8) & 0x0F] + 529 | HEX_CHARS[(h0 >> 20) & 0x0F] + HEX_CHARS[(h0 >> 16) & 0x0F] + 530 | HEX_CHARS[(h0 >> 28) & 0x0F] + HEX_CHARS[(h0 >> 24) & 0x0F] + 531 | HEX_CHARS[(h1 >> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] + 532 | HEX_CHARS[(h1 >> 12) & 0x0F] + HEX_CHARS[(h1 >> 8) & 0x0F] + 533 | HEX_CHARS[(h1 >> 20) & 0x0F] + HEX_CHARS[(h1 >> 16) & 0x0F] + 534 | HEX_CHARS[(h1 >> 28) & 0x0F] + HEX_CHARS[(h1 >> 24) & 0x0F] + 535 | HEX_CHARS[(h2 >> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] + 536 | HEX_CHARS[(h2 >> 12) & 0x0F] + HEX_CHARS[(h2 >> 8) & 0x0F] + 537 | HEX_CHARS[(h2 >> 20) & 0x0F] + HEX_CHARS[(h2 >> 16) & 0x0F] + 538 | HEX_CHARS[(h2 >> 28) & 0x0F] + HEX_CHARS[(h2 >> 24) & 0x0F] + 539 | HEX_CHARS[(h3 >> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] + 540 | HEX_CHARS[(h3 >> 12) & 0x0F] + HEX_CHARS[(h3 >> 8) & 0x0F] + 541 | HEX_CHARS[(h3 >> 20) & 0x0F] + HEX_CHARS[(h3 >> 16) & 0x0F] + 542 | HEX_CHARS[(h3 >> 28) & 0x0F] + HEX_CHARS[(h3 >> 24) & 0x0F]; 543 | }; 544 | 545 | /** 546 | * @method toString 547 | * @memberof Md5 548 | * @instance 549 | * @description Output hash as hex string 550 | * @returns {String} Hex string 551 | * @see {@link md5.hex} 552 | * @example 553 | * hash.toString(); 554 | */ 555 | Md5.prototype.toString = Md5.prototype.hex; 556 | 557 | /** 558 | * @method digest 559 | * @memberof Md5 560 | * @instance 561 | * @description Output hash as bytes array 562 | * @returns {Array} Bytes array 563 | * @see {@link md5.digest} 564 | * @example 565 | * hash.digest(); 566 | */ 567 | Md5.prototype.digest = function () { 568 | this.finalize(); 569 | 570 | var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3; 571 | return [ 572 | h0 & 0xFF, (h0 >> 8) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 24) & 0xFF, 573 | h1 & 0xFF, (h1 >> 8) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 24) & 0xFF, 574 | h2 & 0xFF, (h2 >> 8) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 24) & 0xFF, 575 | h3 & 0xFF, (h3 >> 8) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 24) & 0xFF 576 | ]; 577 | }; 578 | 579 | /** 580 | * @method array 581 | * @memberof Md5 582 | * @instance 583 | * @description Output hash as bytes array 584 | * @returns {Array} Bytes array 585 | * @see {@link md5.array} 586 | * @example 587 | * hash.array(); 588 | */ 589 | Md5.prototype.array = Md5.prototype.digest; 590 | 591 | /** 592 | * @method arrayBuffer 593 | * @memberof Md5 594 | * @instance 595 | * @description Output hash as ArrayBuffer 596 | * @returns {ArrayBuffer} ArrayBuffer 597 | * @see {@link md5.arrayBuffer} 598 | * @example 599 | * hash.arrayBuffer(); 600 | */ 601 | Md5.prototype.arrayBuffer = function () { 602 | this.finalize(); 603 | 604 | var buffer = new ArrayBuffer(16); 605 | var blocks = new Uint32Array(buffer); 606 | blocks[0] = this.h0; 607 | blocks[1] = this.h1; 608 | blocks[2] = this.h2; 609 | blocks[3] = this.h3; 610 | return buffer; 611 | }; 612 | 613 | /** 614 | * @method buffer 615 | * @deprecated This maybe confuse with Buffer in node.js. Please use arrayBuffer instead. 616 | * @memberof Md5 617 | * @instance 618 | * @description Output hash as ArrayBuffer 619 | * @returns {ArrayBuffer} ArrayBuffer 620 | * @see {@link md5.buffer} 621 | * @example 622 | * hash.buffer(); 623 | */ 624 | Md5.prototype.buffer = Md5.prototype.arrayBuffer; 625 | 626 | /** 627 | * @method base64 628 | * @memberof Md5 629 | * @instance 630 | * @description Output hash as base64 string 631 | * @returns {String} base64 string 632 | * @see {@link md5.base64} 633 | * @example 634 | * hash.base64(); 635 | */ 636 | Md5.prototype.base64 = function () { 637 | var v1, v2, v3, base64Str = '', bytes = this.array(); 638 | for (var i = 0; i < 15;) { 639 | v1 = bytes[i++]; 640 | v2 = bytes[i++]; 641 | v3 = bytes[i++]; 642 | base64Str += BASE64_ENCODE_CHAR[v1 >>> 2] + 643 | BASE64_ENCODE_CHAR[(v1 << 4 | v2 >>> 4) & 63] + 644 | BASE64_ENCODE_CHAR[(v2 << 2 | v3 >>> 6) & 63] + 645 | BASE64_ENCODE_CHAR[v3 & 63]; 646 | } 647 | v1 = bytes[i]; 648 | base64Str += BASE64_ENCODE_CHAR[v1 >>> 2] + 649 | BASE64_ENCODE_CHAR[(v1 << 4) & 63] + 650 | '=='; 651 | return base64Str; 652 | }; 653 | 654 | var exports = createMethod(); 655 | 656 | if (COMMON_JS) { 657 | module.exports = exports; 658 | } else { 659 | /** 660 | * @method md5 661 | * @description Md5 hash function, export to global in browsers. 662 | * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash 663 | * @returns {String} md5 hashes 664 | * @example 665 | * md5(''); // d41d8cd98f00b204e9800998ecf8427e 666 | * md5('The quick brown fox jumps over the lazy dog'); // 9e107d9d372bb6826bd81d3542a419d6 667 | * md5('The quick brown fox jumps over the lazy dog.'); // e4d909c290d0fb1ca068ffaddf22cbd0 668 | * 669 | * // It also supports UTF-8 encoding 670 | * md5('中文'); // a7bac2239fcdcb3a067903d8077c4a07 671 | * 672 | * // It also supports byte `Array`, `Uint8Array`, `ArrayBuffer` 673 | * md5([]); // d41d8cd98f00b204e9800998ecf8427e 674 | * md5(new Uint8Array([])); // d41d8cd98f00b204e9800998ecf8427e 675 | */ 676 | root.md5 = exports; 677 | if (AMD) { 678 | define(function () { 679 | return exports; 680 | }); 681 | } 682 | } 683 | })(); 684 | -------------------------------------------------------------------------------- /node/dankest-dungeon/public/leaderboards.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dankest Dungeon 1.0 - A 7 Day Bracket Production 5 | 6 | 7 | 8 | 9 | 16 | 17 | 18 | 19 | 20 |
21 |

22 | Welcome, . Can you build the Dankest Dungeon? 23 | Play the Game 24 |

25 | 26 |
27 | 28 |
29 | 30 | 36 | 37 | -------------------------------------------------------------------------------- /node/dankest-dungeon/public/stylesheets/dank.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: black; 3 | font-family: 'VT323', monospace; 4 | font-size: 12pt; 5 | background:#111; 6 | color:#00ff00; 7 | padding-top:20px; 8 | padding-left:20px; 9 | } 10 | a { 11 | color: yellow; 12 | } 13 | a:visited { 14 | color: yellow; 15 | } 16 | h1 { 17 | text-align: center; 18 | } 19 | #body { 20 | width: 80%; 21 | margin-left: auto; 22 | margin-right: auto; 23 | } 24 | #footer { 25 | position: fixed; 26 | bottom: 0; 27 | width: 100%; 28 | } 29 | 30 | @keyframes flicker { 31 | 0% { 32 | opacity: 0.27861; 33 | } 34 | 5% { 35 | opacity: 0.34769; 36 | } 37 | 10% { 38 | opacity: 0.23604; 39 | } 40 | 15% { 41 | opacity: 0.90626; 42 | } 43 | 20% { 44 | opacity: 0.18128; 45 | } 46 | 25% { 47 | opacity: 0.83891; 48 | } 49 | 30% { 50 | opacity: 0.65583; 51 | } 52 | 35% { 53 | opacity: 0.67807; 54 | } 55 | 40% { 56 | opacity: 0.26559; 57 | } 58 | 45% { 59 | opacity: 0.84693; 60 | } 61 | 50% { 62 | opacity: 0.96019; 63 | } 64 | 55% { 65 | opacity: 0.08594; 66 | } 67 | 60% { 68 | opacity: 0.20313; 69 | } 70 | 65% { 71 | opacity: 0.71988; 72 | } 73 | 70% { 74 | opacity: 0.53455; 75 | } 76 | 75% { 77 | opacity: 0.37288; 78 | } 79 | 80% { 80 | opacity: 0.71428; 81 | } 82 | 85% { 83 | opacity: 0.70419; 84 | } 85 | 90% { 86 | opacity: 0.7003; 87 | } 88 | 95% { 89 | opacity: 0.36108; 90 | } 91 | 100% { 92 | opacity: 0.24387; 93 | } 94 | } 95 | @keyframes textShadow { 96 | 0% { 97 | text-shadow: 0.4389924193300864px 0 1px rgba(0,30,255,0.5), -0.4389924193300864px 0 1px rgba(255,0,80,0.3), 0 0 3px; 98 | } 99 | 5% { 100 | text-shadow: 2.7928974010788217px 0 1px rgba(0,30,255,0.5), -2.7928974010788217px 0 1px rgba(255,0,80,0.3), 0 0 3px; 101 | } 102 | 10% { 103 | text-shadow: 0.02956275843481219px 0 1px rgba(0,30,255,0.5), -0.02956275843481219px 0 1px rgba(255,0,80,0.3), 0 0 3px; 104 | } 105 | 15% { 106 | text-shadow: 0.40218538552878136px 0 1px rgba(0,30,255,0.5), -0.40218538552878136px 0 1px rgba(255,0,80,0.3), 0 0 3px; 107 | } 108 | 20% { 109 | text-shadow: 3.4794037899852017px 0 1px rgba(0,30,255,0.5), -3.4794037899852017px 0 1px rgba(255,0,80,0.3), 0 0 3px; 110 | } 111 | 25% { 112 | text-shadow: 1.6125630401149584px 0 1px rgba(0,30,255,0.5), -1.6125630401149584px 0 1px rgba(255,0,80,0.3), 0 0 3px; 113 | } 114 | 30% { 115 | text-shadow: 0.7015590085143956px 0 1px rgba(0,30,255,0.5), -0.7015590085143956px 0 1px rgba(255,0,80,0.3), 0 0 3px; 116 | } 117 | 35% { 118 | text-shadow: 3.896914047650351px 0 1px rgba(0,30,255,0.5), -3.896914047650351px 0 1px rgba(255,0,80,0.3), 0 0 3px; 119 | } 120 | 40% { 121 | text-shadow: 3.870905614848819px 0 1px rgba(0,30,255,0.5), -3.870905614848819px 0 1px rgba(255,0,80,0.3), 0 0 3px; 122 | } 123 | 45% { 124 | text-shadow: 2.231056963361899px 0 1px rgba(0,30,255,0.5), -2.231056963361899px 0 1px rgba(255,0,80,0.3), 0 0 3px; 125 | } 126 | 50% { 127 | text-shadow: 0.08084290417898504px 0 1px rgba(0,30,255,0.5), -0.08084290417898504px 0 1px rgba(255,0,80,0.3), 0 0 3px; 128 | } 129 | 55% { 130 | text-shadow: 2.3758461067427543px 0 1px rgba(0,30,255,0.5), -2.3758461067427543px 0 1px rgba(255,0,80,0.3), 0 0 3px; 131 | } 132 | 60% { 133 | text-shadow: 2.202193051050636px 0 1px rgba(0,30,255,0.5), -2.202193051050636px 0 1px rgba(255,0,80,0.3), 0 0 3px; 134 | } 135 | 65% { 136 | text-shadow: 2.8638780614874975px 0 1px rgba(0,30,255,0.5), -2.8638780614874975px 0 1px rgba(255,0,80,0.3), 0 0 3px; 137 | } 138 | 70% { 139 | text-shadow: 0.48874025155497314px 0 1px rgba(0,30,255,0.5), -0.48874025155497314px 0 1px rgba(255,0,80,0.3), 0 0 3px; 140 | } 141 | 75% { 142 | text-shadow: 1.8948491305757957px 0 1px rgba(0,30,255,0.5), -1.8948491305757957px 0 1px rgba(255,0,80,0.3), 0 0 3px; 143 | } 144 | 80% { 145 | text-shadow: 0.0833037308038857px 0 1px rgba(0,30,255,0.5), -0.0833037308038857px 0 1px rgba(255,0,80,0.3), 0 0 3px; 146 | } 147 | 85% { 148 | text-shadow: 0.09769827255241735px 0 1px rgba(0,30,255,0.5), -0.09769827255241735px 0 1px rgba(255,0,80,0.3), 0 0 3px; 149 | } 150 | 90% { 151 | text-shadow: 3.443339761481782px 0 1px rgba(0,30,255,0.5), -3.443339761481782px 0 1px rgba(255,0,80,0.3), 0 0 3px; 152 | } 153 | 95% { 154 | text-shadow: 2.1841838852799786px 0 1px rgba(0,30,255,0.5), -2.1841838852799786px 0 1px rgba(255,0,80,0.3), 0 0 3px; 155 | } 156 | 100% { 157 | text-shadow: 2.6208764473832513px 0 1px rgba(0,30,255,0.5), -2.6208764473832513px 0 1px rgba(255,0,80,0.3), 0 0 3px; 158 | } 159 | } 160 | .crt::after { 161 | content: " "; 162 | display: block; 163 | position: absolute; 164 | top: 0; 165 | left: 0; 166 | bottom: 0; 167 | right: 0; 168 | background: rgba(18, 16, 16, 0.1); 169 | opacity: 0; 170 | z-index: 2; 171 | pointer-events: none; 172 | /*animation: flicker 0.15s infinite;*/ 173 | } 174 | .crt::before { 175 | content: " "; 176 | display: block; 177 | position: absolute; 178 | top: 0; 179 | left: 0; 180 | bottom: 0; 181 | right: 0; 182 | background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06)); 183 | z-index: 2; 184 | background-size: 100% 2px, 3px 100%; 185 | pointer-events: none; 186 | } 187 | .crt { 188 | /*animation: textShadow 0.8s infinite;*/ 189 | } 190 | 191 | -------------------------------------------------------------------------------- /node/dankest-dungeon/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /node/dankest-dungeon/routes/database.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | connectionString : process.env.DATABASE_URL || 'postgres://postgres:postgres@172.16.10.193:5432/dankestdungeon', 3 | 4 | doSql : function(sql, params, callback) { 5 | console.log(sql); 6 | let pg = require('pg'); 7 | let path = require('path'); 8 | 9 | const results = []; 10 | pg.connect(this.connectionString, (err, client, done) => { 11 | // Handle connection errors 12 | if (err) { 13 | done(); 14 | console.log(err); 15 | return res.status(500).json({success: false, data: err}); 16 | } 17 | // SQL Query > Select Data 18 | const query = client.query(sql, params); 19 | // Stream results back one row at a time 20 | query.on('row', (row) => { 21 | results.push(row); 22 | }); 23 | // After all data is returned, close connection and return results 24 | query.on('end', () => { 25 | done(); 26 | callback(results); 27 | }); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /node/dankest-dungeon/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.render('index', { title: 'Express' }); 7 | }); 8 | 9 | module.exports = router; 10 | 11 | router.post('/api/newUser', (req, res, next) => { 12 | let db = require("./database"); 13 | let body = req.body; 14 | let tokens = require("./tokens"); 15 | 16 | // Step 1 - Check uniqueness 17 | db.doSql("SELECT COUNT(*) AS count FROM adventurers WHERE username=$1;", [body.username], (countrows) => { 18 | let count = countrows[0].count; 19 | if (count != 0) { 20 | res.json("Error: username is taken"); 21 | } else { 22 | // Step 2 - Do the insert 23 | db.doSql("INSERT INTO adventurers (username, password_hash) VALUES ($1, $2) RETURNING id;", [body.username, body.password], (raw) => { 24 | // Step 3 - Now we need to create a login token 25 | tokens.getNewToken({userId: raw[0].id}, (nt) => { 26 | res.json(nt); 27 | }); 28 | }); 29 | } 30 | }); 31 | }); 32 | 33 | router.post("/api/Login", (req, res, next) => { 34 | let db = require("./database"); 35 | let body = req.body; 36 | let tokens = require("./tokens"); 37 | 38 | // Check the username/password combo 39 | db.doSql("SELECT id FROM adventurers WHERE username=$1 AND password_hash=$2", [body.username, body.password], (rows) => { 40 | if (rows.length != 1) { 41 | res.json("Error: invalid username or password"); 42 | } else { 43 | tokens.getNewToken({userId: rows[0].id}, (nt) => { 44 | res.json(nt); 45 | }); 46 | } 47 | }); 48 | }); 49 | 50 | router.post("/api/CheckToken", (req, res, next) => { 51 | let db = require("./database"); 52 | let body = req.body; 53 | let tokens = require("./tokens"); 54 | 55 | tokens.expireTokens((r) => { 56 | db.doSql("SELECT * FROM tokens WHERE id=$1;", [body.token], (rows) => { 57 | if (rows.length != 1) { 58 | res.json("Invalid token"); 59 | } else { 60 | res.json("OK"); 61 | } 62 | }); 63 | }); 64 | }); 65 | 66 | router.get("/api/StartGame/:token", (req, res, next) => { 67 | let db = require("./database"); 68 | let body = req.body; 69 | let tokens = require("./tokens"); 70 | const token = req.params.token; 71 | 72 | tokens.expireTokens((r) => { 73 | db.doSql("SELECT username FROM adventurers INNER JOIN tokens ON (adventurers.id = tokens.adventurer_id) WHERE tokens.id=$1", [token], (rows) => { 74 | if (rows.length != 1) { 75 | res.json("Error"); 76 | } else { 77 | res.json(rows[0].username); 78 | } 79 | }); 80 | }); 81 | }); 82 | 83 | router.post("/api/SaveDungeon", (req, res, next) => { 84 | let db = require("./database"); 85 | let body = req.body; 86 | let tokens = require("./tokens"); 87 | const token = body.token; 88 | 89 | tokens.expireTokens((r) => { 90 | db.doSql("SELECT adventurer_id FROM tokens WHERE id=$1", [token], (tokenrows) => { 91 | if (tokenrows.length != 1) { 92 | res.json("Error - who are you?"); 93 | } else { 94 | var userId = tokenrows[0].adventurer_id; 95 | db.doSql("INSERT INTO dungeons (name, level_data, adventurer_id) VALUES ($1, $2, $3);", 96 | [body.name, body.data, userId], (tmp) => { 97 | res.json("Saved"); 98 | }); 99 | } 100 | }); 101 | }); 102 | }); 103 | 104 | router.get("/api/DungeonList", (req, res, next) => { 105 | let db = require("./database"); 106 | const sql = "SELECT dungeons.id AS id, COALESCE(name||' (Rating: '||(SELECT round(avg(rating),1) FROM result WHERE dungeon_id=dungeons.id)||', Plays: '||(select count(rating) FROM result WHERE dungeon_id=dungeons.id)||', T'||(select round(avg(turns)) FROM result WHERE dungeon_id=dungeons.id)||')', name) AS name, username FROM dungeons INNER JOIN adventurers a ON dungeons.adventurer_id = a.id ORDER BY id DESC;"; 107 | db.doSql(sql, [], (rows) => { 108 | let result = ""; 109 | for (var i=0; i { 119 | let db = require("./database"); 120 | const id = req.params.id; 121 | db.doSql("SELECT level_data FROM dungeons WHERE id=$1", [id], (rows) => { 122 | if (rows.length != 1) { 123 | console.log("Request for non-existent level."); 124 | res.json("Error"); 125 | } else { 126 | //console.log(rows[0].level_data); 127 | res.json(rows[0].level_data); 128 | } 129 | }); 130 | }); 131 | 132 | router.post("/api/SaveRunthrough", (req, res, next) => { 133 | let db = require("./database"); 134 | let body = req.body; 135 | let tokens = require("./tokens"); 136 | const token = body.token; 137 | const levelId = body.levelId; 138 | const turns = body.turns; 139 | const result = body.result; 140 | const rating = body.rating; 141 | const isWin = result == "WIN" ? true : false; 142 | 143 | tokens.expireTokens((r) => { 144 | db.doSql("SELECT adventurer_id FROM tokens WHERE id=$1", [token], (tokenrows) => { 145 | if (tokenrows.length != 1) { 146 | res.json("Error - who are you?"); 147 | } else { 148 | var userId = tokenrows[0].adventurer_id; 149 | db.doSql("INSERT INTO result (dungeon_id, is_win, turns, rating, adventurer_id) VALUES ($1, $2, $3, $4, $5);", [levelId, isWin, turns, rating, userId], (r) => { 150 | res.json("OK"); 151 | }); 152 | } 153 | }); 154 | }); 155 | }); 156 | 157 | router.get("/api/Leaderboard", (req, res, next) => { 158 | let db = require("./database"); 159 | const sqlCreators = "select username, count(adventurer_id) AS count FROM dungeons INNER JOIN adventurers ON dungeons.adventurer_id = adventurers.id GROUP BY username ORDER BY count DESC"; 160 | const topDungeons = "select name, round(avg(rating),1) AS rating FROM result INNER JOIN dungeons ON result.dungeon_id = dungeons.id GROUP BY name ORDER BY rating DESC;"; 161 | const frequentPlayers = "select username, count(*) FROM result INNER JOIN adventurers ON adventurers.id = result.adventurer_id GROUP BY username ORDER BY count DESC;"; 162 | 163 | let result = {}; 164 | db.doSql(sqlCreators, [], (creators) => { 165 | result.creators = creators; 166 | db.doSql(topDungeons, [], (topgun) => { 167 | result.topDungeons = topgun; 168 | db.doSql(frequentPlayers, [], (freq) => { 169 | result.frequent = freq; 170 | res.json(result); 171 | }); 172 | }) 173 | }) 174 | }); 175 | -------------------------------------------------------------------------------- /node/dankest-dungeon/routes/tokens.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | expireTokens : function(callback) { 3 | let db = require("./database"); 4 | const sql = "DELETE FROM tokens WHERE pointintime < (NOW() - interval '2 days')"; 5 | db.doSql(sql, [], () => { 6 | callback(); 7 | }); 8 | }, 9 | 10 | doesTokenExist : function(token) { 11 | let db = require("./database"); 12 | const sql = "SELECT COUNT(*) FROM tokens WHERE id=$1"; 13 | db.doSql(sql, [token], (rows) => { 14 | if (rows[0].count > 0) return true; 15 | return false; 16 | }); 17 | }, 18 | 19 | getNewToken : function(userInfo, callback) { 20 | let ok = false; 21 | let candidate = (Math.random() * 65536).toFixed(0); 22 | while (!ok) { 23 | ok = !this.doesTokenExist(candidate); 24 | if (!ok) candidate = (Math.random() * 65536).toFixed(0); 25 | } 26 | userInfo.token = candidate; 27 | 28 | let db = require("./database"); 29 | const sql = "INSERT INTO tokens (id, adventurer_id) VALUES ($1, $2);"; 30 | db.doSql(sql, [userInfo.token, userInfo.userId], () => { 31 | callback(userInfo); 32 | }); 33 | } 34 | } -------------------------------------------------------------------------------- /node/dankest-dungeon/routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /node/dankest-dungeon/views/error.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /node/dankest-dungeon/views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= title 5 | p Welcome to #{title} 6 | -------------------------------------------------------------------------------- /node/dankest-dungeon/views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | body 7 | block content 8 | -------------------------------------------------------------------------------- /postgres.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Name: adventurers; Type: TABLE; Schema: public; Owner: postgres; Tablespace: 3 | -- 4 | 5 | CREATE TABLE adventurers ( 6 | id bigint NOT NULL, 7 | username character varying DEFAULT 30 NOT NULL, 8 | password_hash character varying DEFAULT 64 NOT NULL 9 | ); 10 | 11 | 12 | ALTER TABLE adventurers OWNER TO postgres; 13 | 14 | -- 15 | -- Name: adventurers_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres 16 | -- 17 | 18 | CREATE SEQUENCE adventurers_id_seq 19 | START WITH 1 20 | INCREMENT BY 1 21 | NO MINVALUE 22 | NO MAXVALUE 23 | CACHE 1; 24 | 25 | 26 | ALTER TABLE adventurers_id_seq OWNER TO postgres; 27 | 28 | -- 29 | -- Name: adventurers_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres 30 | -- 31 | 32 | ALTER SEQUENCE adventurers_id_seq OWNED BY adventurers.id; 33 | 34 | 35 | -- 36 | -- Name: dungeons; Type: TABLE; Schema: public; Owner: postgres; Tablespace: 37 | -- 38 | 39 | CREATE TABLE dungeons ( 40 | id bigint NOT NULL, 41 | name character varying(64) NOT NULL, 42 | level_data text NOT NULL, 43 | adventurer_id bigint NOT NULL 44 | ); 45 | 46 | 47 | ALTER TABLE dungeons OWNER TO postgres; 48 | 49 | -- 50 | -- Name: dungeons_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres 51 | -- 52 | 53 | CREATE SEQUENCE dungeons_id_seq 54 | START WITH 1 55 | INCREMENT BY 1 56 | NO MINVALUE 57 | NO MAXVALUE 58 | CACHE 1; 59 | 60 | 61 | ALTER TABLE dungeons_id_seq OWNER TO postgres; 62 | 63 | -- 64 | -- Name: dungeons_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres 65 | -- 66 | 67 | ALTER SEQUENCE dungeons_id_seq OWNED BY dungeons.id; 68 | 69 | 70 | -- 71 | -- Name: result; Type: TABLE; Schema: public; Owner: postgres; Tablespace: 72 | -- 73 | 74 | CREATE TABLE result ( 75 | id bigint NOT NULL, 76 | dungeon_id bigint NOT NULL, 77 | is_win boolean NOT NULL, 78 | turns integer NOT NULL, 79 | rating integer NOT NULL, 80 | adventurer_id bigint NOT NULL 81 | ); 82 | 83 | 84 | ALTER TABLE result OWNER TO postgres; 85 | 86 | -- 87 | -- Name: result_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres 88 | -- 89 | 90 | CREATE SEQUENCE result_id_seq 91 | START WITH 1 92 | INCREMENT BY 1 93 | NO MINVALUE 94 | NO MAXVALUE 95 | CACHE 1; 96 | 97 | 98 | ALTER TABLE result_id_seq OWNER TO postgres; 99 | 100 | -- 101 | -- Name: result_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres 102 | -- 103 | 104 | ALTER SEQUENCE result_id_seq OWNED BY result.id; 105 | 106 | 107 | -- 108 | -- Name: tokens; Type: TABLE; Schema: public; Owner: postgres; Tablespace: 109 | -- 110 | 111 | CREATE TABLE tokens ( 112 | id bigint NOT NULL, 113 | adventurer_id bigint NOT NULL, 114 | pointintime timestamp without time zone DEFAULT now() 115 | ); 116 | 117 | 118 | ALTER TABLE tokens OWNER TO postgres; 119 | 120 | -- 121 | -- Name: id; Type: DEFAULT; Schema: public; Owner: postgres 122 | -- 123 | 124 | ALTER TABLE ONLY adventurers ALTER COLUMN id SET DEFAULT nextval('adventurers_id_seq'::regclass); 125 | 126 | 127 | -- 128 | -- Name: id; Type: DEFAULT; Schema: public; Owner: postgres 129 | -- 130 | 131 | ALTER TABLE ONLY dungeons ALTER COLUMN id SET DEFAULT nextval('dungeons_id_seq'::regclass); 132 | 133 | 134 | -- 135 | -- Name: id; Type: DEFAULT; Schema: public; Owner: postgres 136 | -- 137 | 138 | ALTER TABLE ONLY result ALTER COLUMN id SET DEFAULT nextval('result_id_seq'::regclass); 139 | 140 | 141 | -- 142 | -- Name: adventurers_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace: 143 | -- 144 | 145 | ALTER TABLE ONLY adventurers 146 | ADD CONSTRAINT adventurers_pkey PRIMARY KEY (id); 147 | 148 | 149 | -- 150 | -- Name: dungeons_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace: 151 | -- 152 | 153 | ALTER TABLE ONLY dungeons 154 | ADD CONSTRAINT dungeons_pkey PRIMARY KEY (id); 155 | 156 | 157 | -- 158 | -- Name: result_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace: 159 | -- 160 | 161 | ALTER TABLE ONLY result 162 | ADD CONSTRAINT result_pkey PRIMARY KEY (id); 163 | 164 | 165 | -- 166 | -- Name: tokens_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace: 167 | -- 168 | 169 | ALTER TABLE ONLY tokens 170 | ADD CONSTRAINT tokens_pkey PRIMARY KEY (id); 171 | 172 | 173 | -- 174 | -- Name: adventurers_id_uindex; Type: INDEX; Schema: public; Owner: postgres; Tablespace: 175 | -- 176 | 177 | CREATE UNIQUE INDEX adventurers_id_uindex ON adventurers USING btree (id); 178 | 179 | 180 | -- 181 | -- Name: adventurers_username_uindex; Type: INDEX; Schema: public; Owner: postgres; Tablespace: 182 | -- 183 | 184 | CREATE UNIQUE INDEX adventurers_username_uindex ON adventurers USING btree (username); 185 | 186 | 187 | -- 188 | -- Name: dungeons_adventurer_id_index; Type: INDEX; Schema: public; Owner: postgres; Tablespace: 189 | -- 190 | 191 | CREATE INDEX dungeons_adventurer_id_index ON dungeons USING btree (adventurer_id); 192 | 193 | 194 | -- 195 | -- Name: dungeons_id_uindex; Type: INDEX; Schema: public; Owner: postgres; Tablespace: 196 | -- 197 | 198 | CREATE UNIQUE INDEX dungeons_id_uindex ON dungeons USING btree (id); 199 | 200 | 201 | -- 202 | -- Name: result_adventurer_id_index; Type: INDEX; Schema: public; Owner: postgres; Tablespace: 203 | -- 204 | 205 | CREATE INDEX result_adventurer_id_index ON result USING btree (adventurer_id); 206 | 207 | 208 | -- 209 | -- Name: result_dungeon_id_index; Type: INDEX; Schema: public; Owner: postgres; Tablespace: 210 | -- 211 | 212 | CREATE INDEX result_dungeon_id_index ON result USING btree (dungeon_id); 213 | 214 | 215 | -- 216 | -- Name: result_id_uindex; Type: INDEX; Schema: public; Owner: postgres; Tablespace: 217 | -- 218 | 219 | CREATE UNIQUE INDEX result_id_uindex ON result USING btree (id); 220 | 221 | 222 | -- 223 | -- Name: result_rating_index; Type: INDEX; Schema: public; Owner: postgres; Tablespace: 224 | -- 225 | 226 | CREATE INDEX result_rating_index ON result USING btree (rating); 227 | 228 | 229 | -- 230 | -- Name: result_turns_index; Type: INDEX; Schema: public; Owner: postgres; Tablespace: 231 | -- 232 | 233 | CREATE INDEX result_turns_index ON result USING btree (turns); 234 | 235 | 236 | -- 237 | -- Name: tokens_adventurer_id_index; Type: INDEX; Schema: public; Owner: postgres; Tablespace: 238 | -- 239 | 240 | CREATE INDEX tokens_adventurer_id_index ON tokens USING btree (adventurer_id); 241 | 242 | 243 | -- 244 | -- Name: dungeons_adventurers_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: postgres 245 | -- 246 | 247 | ALTER TABLE ONLY dungeons 248 | ADD CONSTRAINT dungeons_adventurers_id_fk FOREIGN KEY (adventurer_id) REFERENCES adventurers(id); 249 | 250 | 251 | -- 252 | -- Name: result_adventurers_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: postgres 253 | -- 254 | 255 | ALTER TABLE ONLY result 256 | ADD CONSTRAINT result_adventurers_id_fk FOREIGN KEY (adventurer_id) REFERENCES adventurers(id); 257 | 258 | 259 | -- 260 | -- Name: result_dungeons_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: postgres 261 | -- 262 | 263 | ALTER TABLE ONLY result 264 | ADD CONSTRAINT result_dungeons_id_fk FOREIGN KEY (dungeon_id) REFERENCES dungeons(id); 265 | 266 | 267 | -- 268 | -- Name: tokens_adventurers_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: postgres 269 | -- 270 | 271 | ALTER TABLE ONLY tokens 272 | ADD CONSTRAINT tokens_adventurers_id_fk FOREIGN KEY (adventurer_id) REFERENCES adventurers(id); 273 | 274 | --------------------------------------------------------------------------------