├── .gitignore ├── .vscode └── launch.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── resources ├── backing.fs ├── backing.vs ├── console_no_bg.fs ├── console_no_bg.vs ├── console_with_bg.fs ├── console_with_bg.vs ├── scanlines.fs ├── scanlines.vs ├── terminal8x8.jpg └── vga8x16.jpg ├── screenshots ├── RustHelloWorld2.JPG ├── RustyRoguelike.gif ├── RustyRoguelike10.gif ├── RustyRoguelike11.gif ├── RustyRoguelike2.gif ├── RustyRoguelike3.gif ├── RustyRoguelike4.gif ├── RustyRoguelike5.gif ├── RustyRoguelike6.gif ├── RustyRoguelike7.gif ├── RustyRoguelike8.gif ├── RustyRoguelike9.gif └── posts │ ├── week01.md │ ├── week02.md │ ├── week03.md │ └── week04.md └── src ├── game ├── entity.rs ├── fighter.rs ├── gamestate.rs ├── gui.rs ├── inventory.rs ├── item.rs ├── item_effects.rs ├── map.rs ├── map_builder.rs ├── mob.rs ├── mod.rs ├── player.rs ├── random.rs ├── rect.rs ├── ticktype.rs ├── tiletype.rs └── vfx.rs └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | savegame.json 4 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(Windows) Launch", 9 | "type": "cppvsdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/target/debug/rltk.exe", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${workspaceFolder}", 15 | "environment": [], 16 | "externalConsole": true 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "adler32" 5 | version = "1.0.3" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | 8 | [[package]] 9 | name = "andrew" 10 | version = "0.2.1" 11 | source = "registry+https://github.com/rust-lang/crates.io-index" 12 | dependencies = [ 13 | "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 14 | "line_drawing 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 15 | "rusttype 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", 16 | "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", 17 | "xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 18 | "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 19 | ] 20 | 21 | [[package]] 22 | name = "android_glue" 23 | version = "0.2.3" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | 26 | [[package]] 27 | name = "approx" 28 | version = "0.3.2" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | dependencies = [ 31 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 32 | ] 33 | 34 | [[package]] 35 | name = "arrayvec" 36 | version = "0.4.11" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | dependencies = [ 39 | "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", 40 | ] 41 | 42 | [[package]] 43 | name = "autocfg" 44 | version = "0.1.5" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | 47 | [[package]] 48 | name = "bitflags" 49 | version = "1.1.0" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | 52 | [[package]] 53 | name = "block" 54 | version = "0.1.6" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | 57 | [[package]] 58 | name = "bresenham" 59 | version = "0.1.1" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | 62 | [[package]] 63 | name = "byteorder" 64 | version = "1.3.2" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | 67 | [[package]] 68 | name = "c2-chacha" 69 | version = "0.2.2" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | dependencies = [ 72 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 73 | "ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 74 | ] 75 | 76 | [[package]] 77 | name = "calloop" 78 | version = "0.4.4" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | dependencies = [ 81 | "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", 82 | "mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 83 | "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", 84 | ] 85 | 86 | [[package]] 87 | name = "cc" 88 | version = "1.0.40" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | 91 | [[package]] 92 | name = "cfg-if" 93 | version = "0.1.9" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | 96 | [[package]] 97 | name = "cgl" 98 | version = "0.2.3" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | dependencies = [ 101 | "gleam 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", 102 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 103 | ] 104 | 105 | [[package]] 106 | name = "cgmath" 107 | version = "0.17.0" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | dependencies = [ 110 | "approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 111 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 112 | "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 113 | ] 114 | 115 | [[package]] 116 | name = "cloudabi" 117 | version = "0.0.3" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | dependencies = [ 120 | "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 121 | ] 122 | 123 | [[package]] 124 | name = "cocoa" 125 | version = "0.18.4" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | dependencies = [ 128 | "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 129 | "block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 130 | "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", 131 | "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", 132 | "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 133 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 134 | "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 135 | ] 136 | 137 | [[package]] 138 | name = "color_quant" 139 | version = "1.0.1" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | 142 | [[package]] 143 | name = "core-foundation" 144 | version = "0.6.4" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | dependencies = [ 147 | "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", 148 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 149 | ] 150 | 151 | [[package]] 152 | name = "core-foundation-sys" 153 | version = "0.6.2" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | 156 | [[package]] 157 | name = "core-graphics" 158 | version = "0.17.3" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | dependencies = [ 161 | "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 162 | "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", 163 | "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 164 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 165 | ] 166 | 167 | [[package]] 168 | name = "core-video-sys" 169 | version = "0.1.3" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | dependencies = [ 172 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 173 | "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", 174 | "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", 175 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 176 | "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 177 | ] 178 | 179 | [[package]] 180 | name = "crc32fast" 181 | version = "1.2.0" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | dependencies = [ 184 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 185 | ] 186 | 187 | [[package]] 188 | name = "crossbeam-deque" 189 | version = "0.6.3" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | dependencies = [ 192 | "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 193 | "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", 194 | ] 195 | 196 | [[package]] 197 | name = "crossbeam-epoch" 198 | version = "0.7.2" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | dependencies = [ 201 | "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 202 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 203 | "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", 204 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 205 | "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 206 | "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 207 | ] 208 | 209 | [[package]] 210 | name = "crossbeam-queue" 211 | version = "0.1.2" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | dependencies = [ 214 | "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", 215 | ] 216 | 217 | [[package]] 218 | name = "crossbeam-utils" 219 | version = "0.6.6" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | dependencies = [ 222 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 223 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 224 | ] 225 | 226 | [[package]] 227 | name = "ctor" 228 | version = "0.1.9" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | dependencies = [ 231 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 232 | "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", 233 | ] 234 | 235 | [[package]] 236 | name = "deflate" 237 | version = "0.7.20" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | dependencies = [ 240 | "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 241 | "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 242 | ] 243 | 244 | [[package]] 245 | name = "derivative" 246 | version = "1.0.2" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | dependencies = [ 249 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 250 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 251 | "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", 252 | ] 253 | 254 | [[package]] 255 | name = "dispatch" 256 | version = "0.1.4" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | 259 | [[package]] 260 | name = "dlib" 261 | version = "0.4.1" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | dependencies = [ 264 | "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 265 | ] 266 | 267 | [[package]] 268 | name = "downcast-rs" 269 | version = "1.0.4" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | 272 | [[package]] 273 | name = "either" 274 | version = "1.5.2" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | 277 | [[package]] 278 | name = "erased-serde" 279 | version = "0.3.9" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | dependencies = [ 282 | "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", 283 | ] 284 | 285 | [[package]] 286 | name = "flate2" 287 | version = "1.0.9" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | dependencies = [ 290 | "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 291 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 292 | "miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 293 | "miniz_oxide_c_api 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 294 | ] 295 | 296 | [[package]] 297 | name = "foreign-types" 298 | version = "0.3.2" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | dependencies = [ 301 | "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 302 | ] 303 | 304 | [[package]] 305 | name = "foreign-types-shared" 306 | version = "0.1.1" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | 309 | [[package]] 310 | name = "fuchsia-cprng" 311 | version = "0.1.1" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | 314 | [[package]] 315 | name = "fuchsia-zircon" 316 | version = "0.3.3" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | dependencies = [ 319 | "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 320 | "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 321 | ] 322 | 323 | [[package]] 324 | name = "fuchsia-zircon-sys" 325 | version = "0.3.3" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | 328 | [[package]] 329 | name = "getrandom" 330 | version = "0.1.8" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | dependencies = [ 333 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 334 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 335 | ] 336 | 337 | [[package]] 338 | name = "ghost" 339 | version = "0.1.0" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | dependencies = [ 342 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 343 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 344 | "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", 345 | ] 346 | 347 | [[package]] 348 | name = "gif" 349 | version = "0.10.2" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | dependencies = [ 352 | "color_quant 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 353 | "lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 354 | ] 355 | 356 | [[package]] 357 | name = "gl_generator" 358 | version = "0.11.0" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | dependencies = [ 361 | "khronos_api 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 362 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 363 | "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 364 | ] 365 | 366 | [[package]] 367 | name = "gl_generator" 368 | version = "0.13.0" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | dependencies = [ 371 | "khronos_api 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 372 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 373 | "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 374 | ] 375 | 376 | [[package]] 377 | name = "gleam" 378 | version = "0.6.19" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | dependencies = [ 381 | "gl_generator 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", 382 | ] 383 | 384 | [[package]] 385 | name = "glutin" 386 | version = "0.22.0-alpha1" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | dependencies = [ 389 | "android_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 390 | "cgl 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 391 | "cocoa 0.18.4 (registry+https://github.com/rust-lang/crates.io-index)", 392 | "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", 393 | "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", 394 | "derivative 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 395 | "glutin_egl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 396 | "glutin_emscripten_sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 397 | "glutin_gles2_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 398 | "glutin_glx_sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 399 | "glutin_wgl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 400 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 401 | "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 402 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 403 | "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 404 | "osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 405 | "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 406 | "wayland-client 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)", 407 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 408 | "winit 0.20.0-alpha2 (registry+https://github.com/rust-lang/crates.io-index)", 409 | ] 410 | 411 | [[package]] 412 | name = "glutin_egl_sys" 413 | version = "0.1.3" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | dependencies = [ 416 | "gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 417 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 418 | ] 419 | 420 | [[package]] 421 | name = "glutin_emscripten_sys" 422 | version = "0.1.0" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | 425 | [[package]] 426 | name = "glutin_gles2_sys" 427 | version = "0.1.3" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | dependencies = [ 430 | "gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 431 | "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 432 | ] 433 | 434 | [[package]] 435 | name = "glutin_glx_sys" 436 | version = "0.1.5" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | dependencies = [ 439 | "gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 440 | "x11-dl 2.18.3 (registry+https://github.com/rust-lang/crates.io-index)", 441 | ] 442 | 443 | [[package]] 444 | name = "glutin_wgl_sys" 445 | version = "0.1.3" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | dependencies = [ 448 | "gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 449 | ] 450 | 451 | [[package]] 452 | name = "image" 453 | version = "0.22.1" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | dependencies = [ 456 | "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 457 | "gif 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", 458 | "jpeg-decoder 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", 459 | "lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 460 | "num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", 461 | "num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 462 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 463 | "png 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", 464 | "scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 465 | "tiff 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 466 | ] 467 | 468 | [[package]] 469 | name = "inflate" 470 | version = "0.4.5" 471 | source = "registry+https://github.com/rust-lang/crates.io-index" 472 | dependencies = [ 473 | "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 474 | ] 475 | 476 | [[package]] 477 | name = "inventory" 478 | version = "0.1.3" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | dependencies = [ 481 | "ctor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 482 | "ghost 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 483 | "inventory-impl 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 484 | ] 485 | 486 | [[package]] 487 | name = "inventory-impl" 488 | version = "0.1.3" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | dependencies = [ 491 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 492 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 493 | "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", 494 | ] 495 | 496 | [[package]] 497 | name = "iovec" 498 | version = "0.1.2" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | dependencies = [ 501 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 502 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 503 | ] 504 | 505 | [[package]] 506 | name = "itoa" 507 | version = "0.4.4" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | 510 | [[package]] 511 | name = "jpeg-decoder" 512 | version = "0.1.15" 513 | source = "registry+https://github.com/rust-lang/crates.io-index" 514 | dependencies = [ 515 | "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 516 | "rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 517 | ] 518 | 519 | [[package]] 520 | name = "kernel32-sys" 521 | version = "0.2.2" 522 | source = "registry+https://github.com/rust-lang/crates.io-index" 523 | dependencies = [ 524 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 525 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 526 | ] 527 | 528 | [[package]] 529 | name = "khronos_api" 530 | version = "3.1.0" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | 533 | [[package]] 534 | name = "lazy_static" 535 | version = "1.3.0" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | 538 | [[package]] 539 | name = "lazycell" 540 | version = "1.2.1" 541 | source = "registry+https://github.com/rust-lang/crates.io-index" 542 | 543 | [[package]] 544 | name = "libc" 545 | version = "0.2.61" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | 548 | [[package]] 549 | name = "libloading" 550 | version = "0.5.2" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | dependencies = [ 553 | "cc 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", 554 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 555 | ] 556 | 557 | [[package]] 558 | name = "line_drawing" 559 | version = "0.7.0" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | dependencies = [ 562 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 563 | ] 564 | 565 | [[package]] 566 | name = "lock_api" 567 | version = "0.2.0" 568 | source = "registry+https://github.com/rust-lang/crates.io-index" 569 | dependencies = [ 570 | "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 571 | ] 572 | 573 | [[package]] 574 | name = "log" 575 | version = "0.4.8" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | dependencies = [ 578 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 579 | ] 580 | 581 | [[package]] 582 | name = "lzw" 583 | version = "0.10.0" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | 586 | [[package]] 587 | name = "malloc_buf" 588 | version = "0.0.6" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | dependencies = [ 591 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 592 | ] 593 | 594 | [[package]] 595 | name = "memmap" 596 | version = "0.7.0" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | dependencies = [ 599 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 600 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 601 | ] 602 | 603 | [[package]] 604 | name = "memoffset" 605 | version = "0.5.1" 606 | source = "registry+https://github.com/rust-lang/crates.io-index" 607 | dependencies = [ 608 | "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 609 | ] 610 | 611 | [[package]] 612 | name = "miniz-sys" 613 | version = "0.1.12" 614 | source = "registry+https://github.com/rust-lang/crates.io-index" 615 | dependencies = [ 616 | "cc 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", 617 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 618 | ] 619 | 620 | [[package]] 621 | name = "miniz_oxide" 622 | version = "0.3.2" 623 | source = "registry+https://github.com/rust-lang/crates.io-index" 624 | dependencies = [ 625 | "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 626 | ] 627 | 628 | [[package]] 629 | name = "miniz_oxide_c_api" 630 | version = "0.2.3" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | dependencies = [ 633 | "cc 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", 634 | "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 635 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 636 | "miniz_oxide 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 637 | ] 638 | 639 | [[package]] 640 | name = "mio" 641 | version = "0.6.19" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | dependencies = [ 644 | "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 645 | "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 646 | "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 647 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 648 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 649 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 650 | "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 651 | "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 652 | "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 653 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 654 | ] 655 | 656 | [[package]] 657 | name = "mio-extras" 658 | version = "2.0.5" 659 | source = "registry+https://github.com/rust-lang/crates.io-index" 660 | dependencies = [ 661 | "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 662 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 663 | "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", 664 | "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 665 | ] 666 | 667 | [[package]] 668 | name = "miow" 669 | version = "0.2.1" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | dependencies = [ 672 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 673 | "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 674 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 675 | "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 676 | ] 677 | 678 | [[package]] 679 | name = "net2" 680 | version = "0.2.33" 681 | source = "registry+https://github.com/rust-lang/crates.io-index" 682 | dependencies = [ 683 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 684 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 685 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 686 | ] 687 | 688 | [[package]] 689 | name = "nix" 690 | version = "0.14.1" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | dependencies = [ 693 | "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 694 | "cc 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", 695 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 696 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 697 | "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 698 | ] 699 | 700 | [[package]] 701 | name = "nodrop" 702 | version = "0.1.13" 703 | source = "registry+https://github.com/rust-lang/crates.io-index" 704 | 705 | [[package]] 706 | name = "num-derive" 707 | version = "0.2.5" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | dependencies = [ 710 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 711 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 712 | "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", 713 | ] 714 | 715 | [[package]] 716 | name = "num-integer" 717 | version = "0.1.41" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | dependencies = [ 720 | "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 721 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 722 | ] 723 | 724 | [[package]] 725 | name = "num-iter" 726 | version = "0.1.39" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | dependencies = [ 729 | "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 730 | "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 731 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 732 | ] 733 | 734 | [[package]] 735 | name = "num-rational" 736 | version = "0.2.2" 737 | source = "registry+https://github.com/rust-lang/crates.io-index" 738 | dependencies = [ 739 | "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 740 | "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 741 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 742 | ] 743 | 744 | [[package]] 745 | name = "num-traits" 746 | version = "0.2.8" 747 | source = "registry+https://github.com/rust-lang/crates.io-index" 748 | dependencies = [ 749 | "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 750 | ] 751 | 752 | [[package]] 753 | name = "num_cpus" 754 | version = "1.10.1" 755 | source = "registry+https://github.com/rust-lang/crates.io-index" 756 | dependencies = [ 757 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 758 | ] 759 | 760 | [[package]] 761 | name = "objc" 762 | version = "0.2.6" 763 | source = "registry+https://github.com/rust-lang/crates.io-index" 764 | dependencies = [ 765 | "malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 766 | ] 767 | 768 | [[package]] 769 | name = "ordered-float" 770 | version = "1.0.2" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | dependencies = [ 773 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 774 | ] 775 | 776 | [[package]] 777 | name = "osmesa-sys" 778 | version = "0.1.2" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | dependencies = [ 781 | "shared_library 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 782 | ] 783 | 784 | [[package]] 785 | name = "parking_lot" 786 | version = "0.8.0" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | dependencies = [ 789 | "lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 790 | "parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", 791 | "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 792 | ] 793 | 794 | [[package]] 795 | name = "parking_lot_core" 796 | version = "0.5.0" 797 | source = "registry+https://github.com/rust-lang/crates.io-index" 798 | dependencies = [ 799 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 800 | "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 801 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 802 | "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 803 | "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", 804 | "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 805 | "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 806 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 807 | ] 808 | 809 | [[package]] 810 | name = "percent-encoding" 811 | version = "1.0.1" 812 | source = "registry+https://github.com/rust-lang/crates.io-index" 813 | 814 | [[package]] 815 | name = "pkg-config" 816 | version = "0.3.15" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | 819 | [[package]] 820 | name = "png" 821 | version = "0.15.0" 822 | source = "registry+https://github.com/rust-lang/crates.io-index" 823 | dependencies = [ 824 | "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 825 | "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 826 | "deflate 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", 827 | "inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", 828 | ] 829 | 830 | [[package]] 831 | name = "ppv-lite86" 832 | version = "0.2.5" 833 | source = "registry+https://github.com/rust-lang/crates.io-index" 834 | 835 | [[package]] 836 | name = "proc-macro2" 837 | version = "0.4.30" 838 | source = "registry+https://github.com/rust-lang/crates.io-index" 839 | dependencies = [ 840 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 841 | ] 842 | 843 | [[package]] 844 | name = "quote" 845 | version = "0.6.13" 846 | source = "registry+https://github.com/rust-lang/crates.io-index" 847 | dependencies = [ 848 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 849 | ] 850 | 851 | [[package]] 852 | name = "rand" 853 | version = "0.6.5" 854 | source = "registry+https://github.com/rust-lang/crates.io-index" 855 | dependencies = [ 856 | "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 857 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 858 | "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 859 | "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 860 | "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 861 | "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 862 | "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 863 | "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 864 | "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 865 | "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 866 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 867 | ] 868 | 869 | [[package]] 870 | name = "rand" 871 | version = "0.7.0" 872 | source = "registry+https://github.com/rust-lang/crates.io-index" 873 | dependencies = [ 874 | "getrandom 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 875 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 876 | "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 877 | "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", 878 | "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 879 | ] 880 | 881 | [[package]] 882 | name = "rand_chacha" 883 | version = "0.1.1" 884 | source = "registry+https://github.com/rust-lang/crates.io-index" 885 | dependencies = [ 886 | "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 887 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 888 | ] 889 | 890 | [[package]] 891 | name = "rand_chacha" 892 | version = "0.2.1" 893 | source = "registry+https://github.com/rust-lang/crates.io-index" 894 | dependencies = [ 895 | "c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 896 | "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", 897 | ] 898 | 899 | [[package]] 900 | name = "rand_core" 901 | version = "0.3.1" 902 | source = "registry+https://github.com/rust-lang/crates.io-index" 903 | dependencies = [ 904 | "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 905 | ] 906 | 907 | [[package]] 908 | name = "rand_core" 909 | version = "0.4.2" 910 | source = "registry+https://github.com/rust-lang/crates.io-index" 911 | 912 | [[package]] 913 | name = "rand_core" 914 | version = "0.5.0" 915 | source = "registry+https://github.com/rust-lang/crates.io-index" 916 | dependencies = [ 917 | "getrandom 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 918 | ] 919 | 920 | [[package]] 921 | name = "rand_hc" 922 | version = "0.1.0" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | dependencies = [ 925 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 926 | ] 927 | 928 | [[package]] 929 | name = "rand_hc" 930 | version = "0.2.0" 931 | source = "registry+https://github.com/rust-lang/crates.io-index" 932 | dependencies = [ 933 | "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", 934 | ] 935 | 936 | [[package]] 937 | name = "rand_isaac" 938 | version = "0.1.1" 939 | source = "registry+https://github.com/rust-lang/crates.io-index" 940 | dependencies = [ 941 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 942 | ] 943 | 944 | [[package]] 945 | name = "rand_jitter" 946 | version = "0.1.4" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | dependencies = [ 949 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 950 | "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 951 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 952 | ] 953 | 954 | [[package]] 955 | name = "rand_os" 956 | version = "0.1.3" 957 | source = "registry+https://github.com/rust-lang/crates.io-index" 958 | dependencies = [ 959 | "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 960 | "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 961 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 962 | "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 963 | "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 964 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 965 | ] 966 | 967 | [[package]] 968 | name = "rand_pcg" 969 | version = "0.1.2" 970 | source = "registry+https://github.com/rust-lang/crates.io-index" 971 | dependencies = [ 972 | "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 973 | "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 974 | ] 975 | 976 | [[package]] 977 | name = "rand_xorshift" 978 | version = "0.1.1" 979 | source = "registry+https://github.com/rust-lang/crates.io-index" 980 | dependencies = [ 981 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 982 | ] 983 | 984 | [[package]] 985 | name = "rand_xorshift" 986 | version = "0.2.0" 987 | source = "registry+https://github.com/rust-lang/crates.io-index" 988 | dependencies = [ 989 | "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", 990 | ] 991 | 992 | [[package]] 993 | name = "rayon" 994 | version = "1.1.0" 995 | source = "registry+https://github.com/rust-lang/crates.io-index" 996 | dependencies = [ 997 | "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", 998 | "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 999 | "rayon-core 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", 1000 | ] 1001 | 1002 | [[package]] 1003 | name = "rayon-core" 1004 | version = "1.5.0" 1005 | source = "registry+https://github.com/rust-lang/crates.io-index" 1006 | dependencies = [ 1007 | "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", 1008 | "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 1009 | "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", 1010 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 1011 | "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", 1012 | ] 1013 | 1014 | [[package]] 1015 | name = "rdrand" 1016 | version = "0.4.0" 1017 | source = "registry+https://github.com/rust-lang/crates.io-index" 1018 | dependencies = [ 1019 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 1020 | ] 1021 | 1022 | [[package]] 1023 | name = "redox_syscall" 1024 | version = "0.1.56" 1025 | source = "registry+https://github.com/rust-lang/crates.io-index" 1026 | 1027 | [[package]] 1028 | name = "rltk" 1029 | version = "0.2.5" 1030 | source = "git+https://github.com/thebracket/rltk_rs#1c9e1d7ea969ce4c9bd16fac8eae98baea848238" 1031 | dependencies = [ 1032 | "bresenham 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 1033 | "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 1034 | "cgmath 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", 1035 | "flate2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 1036 | "gl_generator 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", 1037 | "glutin 0.22.0-alpha1 (registry+https://github.com/rust-lang/crates.io-index)", 1038 | "image 0.22.1 (registry+https://github.com/rust-lang/crates.io-index)", 1039 | "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 1040 | "rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 1041 | "rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1042 | "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", 1043 | "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", 1044 | "winit 0.20.0-alpha2 (registry+https://github.com/rust-lang/crates.io-index)", 1045 | ] 1046 | 1047 | [[package]] 1048 | name = "rustc_version" 1049 | version = "0.2.3" 1050 | source = "registry+https://github.com/rust-lang/crates.io-index" 1051 | dependencies = [ 1052 | "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 1053 | ] 1054 | 1055 | [[package]] 1056 | name = "rusttype" 1057 | version = "0.7.7" 1058 | source = "registry+https://github.com/rust-lang/crates.io-index" 1059 | dependencies = [ 1060 | "approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 1061 | "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 1062 | "ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 1063 | "stb_truetype 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 1064 | ] 1065 | 1066 | [[package]] 1067 | name = "rusty_roguelike" 1068 | version = "0.2.0" 1069 | dependencies = [ 1070 | "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 1071 | "rltk 0.2.5 (git+https://github.com/thebracket/rltk_rs)", 1072 | "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", 1073 | "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", 1074 | "typetag 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 1075 | ] 1076 | 1077 | [[package]] 1078 | name = "ryu" 1079 | version = "1.0.0" 1080 | source = "registry+https://github.com/rust-lang/crates.io-index" 1081 | 1082 | [[package]] 1083 | name = "same-file" 1084 | version = "1.0.5" 1085 | source = "registry+https://github.com/rust-lang/crates.io-index" 1086 | dependencies = [ 1087 | "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 1088 | ] 1089 | 1090 | [[package]] 1091 | name = "scoped_threadpool" 1092 | version = "0.1.9" 1093 | source = "registry+https://github.com/rust-lang/crates.io-index" 1094 | 1095 | [[package]] 1096 | name = "scopeguard" 1097 | version = "1.0.0" 1098 | source = "registry+https://github.com/rust-lang/crates.io-index" 1099 | 1100 | [[package]] 1101 | name = "semver" 1102 | version = "0.9.0" 1103 | source = "registry+https://github.com/rust-lang/crates.io-index" 1104 | dependencies = [ 1105 | "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 1106 | ] 1107 | 1108 | [[package]] 1109 | name = "semver-parser" 1110 | version = "0.7.0" 1111 | source = "registry+https://github.com/rust-lang/crates.io-index" 1112 | 1113 | [[package]] 1114 | name = "serde" 1115 | version = "1.0.98" 1116 | source = "registry+https://github.com/rust-lang/crates.io-index" 1117 | dependencies = [ 1118 | "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", 1119 | ] 1120 | 1121 | [[package]] 1122 | name = "serde_derive" 1123 | version = "1.0.98" 1124 | source = "registry+https://github.com/rust-lang/crates.io-index" 1125 | dependencies = [ 1126 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 1127 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 1128 | "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", 1129 | ] 1130 | 1131 | [[package]] 1132 | name = "serde_json" 1133 | version = "1.0.40" 1134 | source = "registry+https://github.com/rust-lang/crates.io-index" 1135 | dependencies = [ 1136 | "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 1137 | "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 1138 | "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", 1139 | ] 1140 | 1141 | [[package]] 1142 | name = "shared_library" 1143 | version = "0.1.9" 1144 | source = "registry+https://github.com/rust-lang/crates.io-index" 1145 | dependencies = [ 1146 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 1147 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 1148 | ] 1149 | 1150 | [[package]] 1151 | name = "slab" 1152 | version = "0.4.2" 1153 | source = "registry+https://github.com/rust-lang/crates.io-index" 1154 | 1155 | [[package]] 1156 | name = "smallvec" 1157 | version = "0.6.10" 1158 | source = "registry+https://github.com/rust-lang/crates.io-index" 1159 | 1160 | [[package]] 1161 | name = "smithay-client-toolkit" 1162 | version = "0.6.3" 1163 | source = "registry+https://github.com/rust-lang/crates.io-index" 1164 | dependencies = [ 1165 | "andrew 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 1166 | "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1167 | "dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 1168 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 1169 | "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 1170 | "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", 1171 | "wayland-client 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)", 1172 | "wayland-protocols 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)", 1173 | ] 1174 | 1175 | [[package]] 1176 | name = "stb_truetype" 1177 | version = "0.2.6" 1178 | source = "registry+https://github.com/rust-lang/crates.io-index" 1179 | dependencies = [ 1180 | "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 1181 | ] 1182 | 1183 | [[package]] 1184 | name = "syn" 1185 | version = "0.15.44" 1186 | source = "registry+https://github.com/rust-lang/crates.io-index" 1187 | dependencies = [ 1188 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 1189 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 1190 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1191 | ] 1192 | 1193 | [[package]] 1194 | name = "tiff" 1195 | version = "0.3.1" 1196 | source = "registry+https://github.com/rust-lang/crates.io-index" 1197 | dependencies = [ 1198 | "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 1199 | "lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 1200 | "num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 1201 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 1202 | ] 1203 | 1204 | [[package]] 1205 | name = "typetag" 1206 | version = "0.1.3" 1207 | source = "registry+https://github.com/rust-lang/crates.io-index" 1208 | dependencies = [ 1209 | "erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 1210 | "inventory 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 1211 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 1212 | "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", 1213 | "typetag-impl 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 1214 | ] 1215 | 1216 | [[package]] 1217 | name = "typetag-impl" 1218 | version = "0.1.3" 1219 | source = "registry+https://github.com/rust-lang/crates.io-index" 1220 | dependencies = [ 1221 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 1222 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 1223 | "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", 1224 | ] 1225 | 1226 | [[package]] 1227 | name = "unicode-xid" 1228 | version = "0.1.0" 1229 | source = "registry+https://github.com/rust-lang/crates.io-index" 1230 | 1231 | [[package]] 1232 | name = "void" 1233 | version = "1.0.2" 1234 | source = "registry+https://github.com/rust-lang/crates.io-index" 1235 | 1236 | [[package]] 1237 | name = "walkdir" 1238 | version = "2.2.9" 1239 | source = "registry+https://github.com/rust-lang/crates.io-index" 1240 | dependencies = [ 1241 | "same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 1242 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 1243 | "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 1244 | ] 1245 | 1246 | [[package]] 1247 | name = "wayland-client" 1248 | version = "0.23.5" 1249 | source = "registry+https://github.com/rust-lang/crates.io-index" 1250 | dependencies = [ 1251 | "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1252 | "calloop 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 1253 | "downcast-rs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 1254 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 1255 | "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", 1256 | "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", 1257 | "wayland-commons 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)", 1258 | "wayland-scanner 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)", 1259 | "wayland-sys 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)", 1260 | ] 1261 | 1262 | [[package]] 1263 | name = "wayland-commons" 1264 | version = "0.23.5" 1265 | source = "registry+https://github.com/rust-lang/crates.io-index" 1266 | dependencies = [ 1267 | "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", 1268 | "wayland-sys 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)", 1269 | ] 1270 | 1271 | [[package]] 1272 | name = "wayland-protocols" 1273 | version = "0.23.5" 1274 | source = "registry+https://github.com/rust-lang/crates.io-index" 1275 | dependencies = [ 1276 | "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1277 | "wayland-client 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)", 1278 | "wayland-commons 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)", 1279 | "wayland-scanner 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)", 1280 | ] 1281 | 1282 | [[package]] 1283 | name = "wayland-scanner" 1284 | version = "0.23.5" 1285 | source = "registry+https://github.com/rust-lang/crates.io-index" 1286 | dependencies = [ 1287 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 1288 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 1289 | "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 1290 | ] 1291 | 1292 | [[package]] 1293 | name = "wayland-sys" 1294 | version = "0.23.5" 1295 | source = "registry+https://github.com/rust-lang/crates.io-index" 1296 | dependencies = [ 1297 | "dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 1298 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 1299 | ] 1300 | 1301 | [[package]] 1302 | name = "winapi" 1303 | version = "0.2.8" 1304 | source = "registry+https://github.com/rust-lang/crates.io-index" 1305 | 1306 | [[package]] 1307 | name = "winapi" 1308 | version = "0.3.7" 1309 | source = "registry+https://github.com/rust-lang/crates.io-index" 1310 | dependencies = [ 1311 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 1312 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 1313 | ] 1314 | 1315 | [[package]] 1316 | name = "winapi-build" 1317 | version = "0.1.1" 1318 | source = "registry+https://github.com/rust-lang/crates.io-index" 1319 | 1320 | [[package]] 1321 | name = "winapi-i686-pc-windows-gnu" 1322 | version = "0.4.0" 1323 | source = "registry+https://github.com/rust-lang/crates.io-index" 1324 | 1325 | [[package]] 1326 | name = "winapi-util" 1327 | version = "0.1.2" 1328 | source = "registry+https://github.com/rust-lang/crates.io-index" 1329 | dependencies = [ 1330 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 1331 | ] 1332 | 1333 | [[package]] 1334 | name = "winapi-x86_64-pc-windows-gnu" 1335 | version = "0.4.0" 1336 | source = "registry+https://github.com/rust-lang/crates.io-index" 1337 | 1338 | [[package]] 1339 | name = "winit" 1340 | version = "0.20.0-alpha2" 1341 | source = "registry+https://github.com/rust-lang/crates.io-index" 1342 | dependencies = [ 1343 | "android_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 1344 | "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1345 | "calloop 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 1346 | "cocoa 0.18.4 (registry+https://github.com/rust-lang/crates.io-index)", 1347 | "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", 1348 | "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", 1349 | "core-video-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 1350 | "derivative 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 1351 | "dispatch 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 1352 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 1353 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 1354 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 1355 | "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 1356 | "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 1357 | "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 1358 | "smithay-client-toolkit 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", 1359 | "wayland-client 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)", 1360 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 1361 | "x11-dl 2.18.3 (registry+https://github.com/rust-lang/crates.io-index)", 1362 | ] 1363 | 1364 | [[package]] 1365 | name = "ws2_32-sys" 1366 | version = "0.2.1" 1367 | source = "registry+https://github.com/rust-lang/crates.io-index" 1368 | dependencies = [ 1369 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 1370 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 1371 | ] 1372 | 1373 | [[package]] 1374 | name = "x11-dl" 1375 | version = "2.18.3" 1376 | source = "registry+https://github.com/rust-lang/crates.io-index" 1377 | dependencies = [ 1378 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 1379 | "libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", 1380 | "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 1381 | ] 1382 | 1383 | [[package]] 1384 | name = "xdg" 1385 | version = "2.2.0" 1386 | source = "registry+https://github.com/rust-lang/crates.io-index" 1387 | 1388 | [[package]] 1389 | name = "xml-rs" 1390 | version = "0.8.0" 1391 | source = "registry+https://github.com/rust-lang/crates.io-index" 1392 | 1393 | [metadata] 1394 | "checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" 1395 | "checksum andrew 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9b7f09f89872c2b6b29e319377b1fbe91c6f5947df19a25596e121cf19a7b35e" 1396 | "checksum android_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" 1397 | "checksum approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" 1398 | "checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" 1399 | "checksum autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "22130e92352b948e7e82a49cdb0aa94f2211761117f29e052dd397c1ac33542b" 1400 | "checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" 1401 | "checksum block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" 1402 | "checksum bresenham 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bfc1116225f66d2ea341a26503f83a6b1205070a6f7199ce1f1550ead91f6fd7" 1403 | "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" 1404 | "checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" 1405 | "checksum calloop 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7aa2097be53a00de9e8fc349fea6d76221f398f5c4fa550d420669906962d160" 1406 | "checksum cc 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "b548a4ee81fccb95919d4e22cfea83c7693ebfd78f0495493178db20b3139da7" 1407 | "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" 1408 | "checksum cgl 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "55e7ec0b74fe5897894cbc207092c577e87c52f8a59e8ca8d97ef37551f60a49" 1409 | "checksum cgmath 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "283944cdecc44bf0b8dd010ec9af888d3b4f142844fdbe026c20ef68148d6fe7" 1410 | "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 1411 | "checksum cocoa 0.18.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cf79daa4e11e5def06e55306aa3601b87de6b5149671529318da048f67cdd77b" 1412 | "checksum color_quant 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd" 1413 | "checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" 1414 | "checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" 1415 | "checksum core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)" = "56790968ab1c8a1202a102e6de05fc6e1ec87da99e4e93e9a7d13efbfc1e95a9" 1416 | "checksum core-video-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8dc065219542086f72d1e9f7aadbbab0989e980263695d129d502082d063a9d0" 1417 | "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" 1418 | "checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" 1419 | "checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" 1420 | "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" 1421 | "checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" 1422 | "checksum ctor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3b4c17619643c1252b5f690084b82639dd7fac141c57c8e77a00e0148132092c" 1423 | "checksum deflate 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)" = "707b6a7b384888a70c8d2e8650b3e60170dfc6a67bb4aa67b6dfca57af4bedb4" 1424 | "checksum derivative 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6073e9676dbebdddeabaeb63e3b7cefd23c86f5c41d381ee1237cc77b1079898" 1425 | "checksum dispatch 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e93ca78226c51902d7aa8c12c988338aadd9e85ed9c6be8aaac39192ff3605" 1426 | "checksum dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "77e51249a9d823a4cb79e3eca6dcd756153e8ed0157b6c04775d04bf1b13b76a" 1427 | "checksum downcast-rs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b92dfd5c2f75260cbf750572f95d387e7ca0ba5e3fbe9e1a33f23025be020f" 1428 | "checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" 1429 | "checksum erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3beee4bc16478a1b26f2e80ad819a52d24745e292f521a63c16eea5f74b7eb60" 1430 | "checksum flate2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "550934ad4808d5d39365e5d61727309bf18b3b02c6c56b729cb92e7dd84bc3d8" 1431 | "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 1432 | "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 1433 | "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 1434 | "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 1435 | "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 1436 | "checksum getrandom 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "34f33de6f0ae7c9cb5e574502a562e2b512799e32abb801cd1e79ad952b62b49" 1437 | "checksum ghost 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5297b71943dc9fea26a3241b178c140ee215798b7f79f7773fd61683e25bca74" 1438 | "checksum gif 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "86c2f2b597d6e05c86ee5947b2223bda468fe8dad3e88e2a6520869322aaf568" 1439 | "checksum gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39a23d5e872a275135d66895d954269cf5e8661d234eb1c2480f4ce0d586acbd" 1440 | "checksum gl_generator 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1c08ca9be9c177722189cd6a956c9e604563a9c689587b548a8cd7d1d865b022" 1441 | "checksum gleam 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "cae10d7c99d0e77b4766e850a60898a17c1abaf01075531f1066f03dc7dc5fc5" 1442 | "checksum glutin 0.22.0-alpha1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7302e7f7c3cbcf9938ba0ae60429469c87ed5be8bd8f4c4854de1d1e8987c74" 1443 | "checksum glutin_egl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "23f48987ab6cb2b61ad903b59e54a2fd0c380a7baff68cffd6826b69a73dd326" 1444 | "checksum glutin_emscripten_sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "245b3fdb08df6ffed7585365851f8404af9c7e2dd4b59f15262e968b6a95a0c7" 1445 | "checksum glutin_gles2_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "89996c30857ae1b4de4b5189abf1ea822a20a9fe9e1c93e5e7b862ff0bdd5cdf" 1446 | "checksum glutin_glx_sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1290a5ca5e46fcfa7f66f949cc9d9194b2cb6f2ed61892c8c2b82343631dba57" 1447 | "checksum glutin_wgl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f801bbc91efc22dd1c4818a47814fc72bf74d024510451b119381579bfa39021" 1448 | "checksum image 0.22.1 (registry+https://github.com/rust-lang/crates.io-index)" = "663a975007e0b49903e2e8ac0db2c432c465855f2d65f17883ba1476e85f0b42" 1449 | "checksum inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" 1450 | "checksum inventory 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21df85981fe094480bc2267723d3dc0fd1ae0d1f136affc659b7398be615d922" 1451 | "checksum inventory-impl 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8a877ae8bce77402d5e9ed870730939e097aad827b2a932b361958fa9d6e75aa" 1452 | "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" 1453 | "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" 1454 | "checksum jpeg-decoder 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c8b7d43206b34b3f94ea9445174bda196e772049b9bddbc620c9d29b2d20110d" 1455 | "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 1456 | "checksum khronos_api 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" 1457 | "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" 1458 | "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" 1459 | "checksum libc 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)" = "c665266eb592905e8503ba3403020f4b8794d26263f412ca33171600eca9a6fa" 1460 | "checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" 1461 | "checksum line_drawing 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5cc7ad3d82c845bdb5dde34ffdcc7a5fb4d2996e1e1ee0f19c33bc80e15196b9" 1462 | "checksum lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed946d4529956a20f2d63ebe1b69996d5a2137c91913fe3ebbeff957f5bca7ff" 1463 | "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 1464 | "checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" 1465 | "checksum malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" 1466 | "checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" 1467 | "checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f" 1468 | "checksum miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9e3ae51cea1576ceba0dde3d484d30e6e5b86dee0b2d412fe3a16a15c98202" 1469 | "checksum miniz_oxide 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7108aff85b876d06f22503dcce091e29f76733b2bfdd91eebce81f5e68203a10" 1470 | "checksum miniz_oxide_c_api 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6c675792957b0d19933816c4e1d56663c341dd9bfa31cb2140ff2267c1d8ecf4" 1471 | "checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" 1472 | "checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40" 1473 | "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" 1474 | "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" 1475 | "checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" 1476 | "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" 1477 | "checksum num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "eafd0b45c5537c3ba526f79d3e75120036502bebacbb3f3220914067ce39dbf2" 1478 | "checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" 1479 | "checksum num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "76bd5272412d173d6bf9afdf98db8612bbabc9a7a830b7bfc9c188911716132e" 1480 | "checksum num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2885278d5fe2adc2f75ced642d52d879bffaceb5a2e0b1d4309ffdfb239b454" 1481 | "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" 1482 | "checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" 1483 | "checksum objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "31d20fd2b37e07cf5125be68357b588672e8cefe9a96f8c17a9d46053b3e590d" 1484 | "checksum ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518" 1485 | "checksum osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" 1486 | "checksum parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa7767817701cce701d5585b9c4db3cdd02086398322c1d7e8bf5094a96a2ce7" 1487 | "checksum parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c" 1488 | "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" 1489 | "checksum pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c1d2cfa5a714db3b5f24f0915e74fcdf91d09d496ba61329705dda7774d2af" 1490 | "checksum png 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8422b27bb2c013dd97b9aef69e161ce262236f49aaf46a0489011c8ff0264602" 1491 | "checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" 1492 | "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" 1493 | "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" 1494 | "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" 1495 | "checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" 1496 | "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" 1497 | "checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" 1498 | "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 1499 | "checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" 1500 | "checksum rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "615e683324e75af5d43d8f7a39ffe3ee4a9dc42c5c701167a71dc59c3a493aca" 1501 | "checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" 1502 | "checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 1503 | "checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" 1504 | "checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" 1505 | "checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" 1506 | "checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" 1507 | "checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" 1508 | "checksum rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8" 1509 | "checksum rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a4b0186e22767d5b9738a05eab7c6ac90b15db17e5b5f9bd87976dd7d89a10a4" 1510 | "checksum rayon-core 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbe0df8435ac0c397d467b6cad6d25543d06e8a019ef3f6af3c384597515bd2" 1511 | "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 1512 | "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" 1513 | "checksum rltk 0.2.5 (git+https://github.com/thebracket/rltk_rs)" = "" 1514 | "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 1515 | "checksum rusttype 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)" = "654103d61a05074b268a107cf6581ce120f0fc0115f2610ed9dfea363bb81139" 1516 | "checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" 1517 | "checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" 1518 | "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" 1519 | "checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" 1520 | "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 1521 | "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 1522 | "checksum serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5626ac617da2f2d9c48af5515a21d5a480dbd151e01bb1c355e26a3e68113" 1523 | "checksum serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)" = "01e69e1b8a631f245467ee275b8c757b818653c6d704cdbcaeb56b56767b529c" 1524 | "checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704" 1525 | "checksum shared_library 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" 1526 | "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 1527 | "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" 1528 | "checksum smithay-client-toolkit 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8c23093ebdaac1ad67558c7aef153522c6b3be7e0257820909e3a25c4519e78d" 1529 | "checksum stb_truetype 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "69b7df505db8e81d54ff8be4693421e5b543e08214bd8d99eb761fcb4d5668ba" 1530 | "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" 1531 | "checksum tiff 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d7b7c2cfc4742bd8a32f2e614339dd8ce30dbcf676bb262bd63a2327bc5df57d" 1532 | "checksum typetag 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7573a67ebbc8696e879d902067bc278671b7502abd8604b7336d87efd47e5858" 1533 | "checksum typetag-impl 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef49605e1070ec194bd9d2c360a975d47ba164460de183cbc53aa1be8c4db6dc" 1534 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 1535 | "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 1536 | "checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e" 1537 | "checksum wayland-client 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)" = "80d909ba6616dd772686f421e111f039402a13acecf395bcb4fc665277a504e0" 1538 | "checksum wayland-commons 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c30463b995db5f8451e921134934319bec89c2458b0bc5c81967060252aa41d8" 1539 | "checksum wayland-protocols 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)" = "cca5f12d466bb6d060673d9d3f94c4ec72161b44d8123f1ef9d11d1054afd6e7" 1540 | "checksum wayland-scanner 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)" = "842bc498050e1e70ab3b4a09e2532e9039e371b4d2d19345bc1e8e38998fce62" 1541 | "checksum wayland-sys 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b7676aeac62796e1624d1f615c9073ddf8cdd951cbcb7b0bb555e4cdc1818114" 1542 | "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 1543 | "checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" 1544 | "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 1545 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1546 | "checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" 1547 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1548 | "checksum winit 0.20.0-alpha2 (registry+https://github.com/rust-lang/crates.io-index)" = "16df593cb202a335417762f283605e45222345a2eb94c9dd6d1f6f2ddba0b4e4" 1549 | "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 1550 | "checksum x11-dl 2.18.3 (registry+https://github.com/rust-lang/crates.io-index)" = "940586acb859ea05c53971ac231685799a7ec1dee66ac0bccc0e6ad96e06b4e3" 1551 | "checksum xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" 1552 | "checksum xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "541b12c998c5b56aa2b4e6f18f03664eef9a4fd0a246a55594efae6cc2d964b5" 1553 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rusty_roguelike" 3 | version = "0.2.0" 4 | authors = ["Herbert Wolverson "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | rltk = { git = "https://github.com/thebracket/rltk_rs", features = ["serialization"] } 9 | rand = "0.7.0-pre.1" 10 | serde= { version = "1.0.93", features = ["derive"] } 11 | serde_json = "1.0.39" 12 | typetag = "0.1.3" 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Herbert Wolverson (DBA Bracket Productions) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 4 | 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: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | 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. 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rusty Roguelike! 2 | 3 | The fine folks over at [/r/roguelikedev](https://www.reddit.com/r/roguelikedev/new/) on Reddit are running a summer of learning to write roguelikes. I've written quite a few, but wanted to learn Rust. So this repo is about my learning Rust. 4 | 5 | * There's an `rltk` folder containing a module, in which I'm using OpenGL to simulate a basic console renderer. 6 | * The `main.rs` file is the boilerplate required to get this to run. Still working on improving that. 7 | * `game` contains the actual game, broken into various files. 8 | 9 | It's cheating to build both the library AND the game - but I don't know a better way to learn the language. 10 | 11 | **Update**: Now that I've finished spinning it off into its own project, Rusty Roguelike uses `rltk_rs` for all the back-end features. You can see the library side of things [in the rltk_rs repo](https://github.com/thebracket/rltk_rs). 12 | 13 | **Progress** 14 | 15 | Hello world with an OpenGL console pretending to be CP437: 16 | 17 | ![Boring console image](/screenshots/RustHelloWorld2.JPG) 18 | 19 | Moving @ around a random map: 20 | 21 | ![Animated GIF](/screenshots/RustyRoguelike.gif) 22 | 23 | Generating a nicer map: 24 | 25 | ![Animated GIF](/screenshots/RustyRoguelike2.gif) 26 | 27 | Field-of-view and visibility: 28 | 29 | ![Animated GIF](/screenshots/RustyRoguelike3.gif) 30 | 31 | Mouse support and a variety of mobs: 32 | 33 | ![Animated GIF](/screenshots/RustyRoguelike4.gif) 34 | 35 | Dijkstra-flow map based pathfinding (A* will follow) for basic mob AI: 36 | 37 | ![Animated GIF](/screenshots/RustyRoguelike5.gif) 38 | 39 | A-Star based pathfinding, and the beginnings of a user interface, log and end-game: 40 | 41 | ![Animated GIF](/screenshots/RustyRoguelike6.gif) 42 | 43 | Nicer tooltips: 44 | 45 | ![Animated GIF](/screenshots/RustyRoguelike7.gif) 46 | 47 | Pick up and use healing potions: 48 | 49 | ![Animated GIF](/screenshots/RustyRoguelike8.gif) 50 | 51 | Load and Save the game, and a main menu (tutorial 10): 52 | 53 | ![Animated GIF](/screenshots/RustyRoguelike9.gif) 54 | 55 | Added in nicer wall graphics, using a bitmask to detect the correct tile 56 | 57 | ![Animated GIF](/screenshots/RustyRoguelike10.gif) 58 | 59 | Added in some basic particle effects to make things prettier. 60 | 61 | ![Animated GIF](/screenshots/RustyRoguelike11.gif) 62 | -------------------------------------------------------------------------------- /resources/backing.fs: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | out vec4 FragColor; 3 | 4 | in vec2 TexCoords; 5 | 6 | uniform sampler2D screenTexture; 7 | 8 | void main() 9 | { 10 | vec3 col = texture(screenTexture, TexCoords).rgb; 11 | FragColor = vec4(col, 1.0); 12 | } -------------------------------------------------------------------------------- /resources/backing.vs: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | layout (location = 0) in vec2 aPos; 3 | layout (location = 1) in vec2 aTexCoords; 4 | 5 | out vec2 TexCoords; 6 | 7 | void main() 8 | { 9 | TexCoords = aTexCoords; 10 | gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0); 11 | } 12 | -------------------------------------------------------------------------------- /resources/console_no_bg.fs: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | out vec4 FragColor; 3 | 4 | in vec3 ourColor; 5 | in vec2 TexCoord; 6 | in vec3 ourBackground; 7 | 8 | // texture sampler 9 | uniform sampler2D texture1; 10 | 11 | void main() 12 | { 13 | vec4 original = texture(texture1, TexCoord); 14 | if (original.r < 0.1f || original.g < 0.1f || original.b < 0.1f) discard; 15 | vec4 fg = original * vec4(ourColor, 1.f); 16 | FragColor = fg; 17 | } 18 | -------------------------------------------------------------------------------- /resources/console_no_bg.vs: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | layout (location = 0) in vec3 aPos; 3 | layout (location = 1) in vec3 aColor; 4 | layout (location = 2) in vec3 bColor; 5 | layout (location = 3) in vec2 aTexCoord; 6 | 7 | out vec3 ourColor; 8 | out vec3 ourBackground; 9 | out vec2 TexCoord; 10 | 11 | void main() 12 | { 13 | gl_Position = vec4(aPos, 1.0); 14 | ourColor = aColor; 15 | ourBackground = bColor; 16 | TexCoord = vec2(aTexCoord.x, aTexCoord.y); 17 | } -------------------------------------------------------------------------------- /resources/console_with_bg.fs: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | out vec4 FragColor; 3 | 4 | in vec3 ourColor; 5 | in vec2 TexCoord; 6 | in vec3 ourBackground; 7 | 8 | // texture sampler 9 | uniform sampler2D texture1; 10 | 11 | void main() 12 | { 13 | vec4 original = texture(texture1, TexCoord); 14 | vec4 fg = original.r > 0.1f || original.g > 0.1f || original.b > 0.1f ? original * vec4(ourColor, 1.f) : vec4(ourBackground, 1.f); 15 | FragColor = fg; 16 | } 17 | -------------------------------------------------------------------------------- /resources/console_with_bg.vs: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | layout (location = 0) in vec3 aPos; 3 | layout (location = 1) in vec3 aColor; 4 | layout (location = 2) in vec3 bColor; 5 | layout (location = 3) in vec2 aTexCoord; 6 | 7 | out vec3 ourColor; 8 | out vec3 ourBackground; 9 | out vec2 TexCoord; 10 | 11 | void main() 12 | { 13 | gl_Position = vec4(aPos, 1.0); 14 | ourColor = aColor; 15 | ourBackground = bColor; 16 | TexCoord = vec2(aTexCoord.x, aTexCoord.y); 17 | } -------------------------------------------------------------------------------- /resources/scanlines.fs: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | out vec4 FragColor; 3 | 4 | in vec2 TexCoords; 5 | 6 | uniform sampler2D screenTexture; 7 | uniform vec3 screenSize; 8 | uniform bool screenBurn; 9 | 10 | void main() 11 | { 12 | vec3 col = texture(screenTexture, TexCoords).rgb; 13 | float scanLine = mod(gl_FragCoord.y, 2.0) * 0.25; 14 | vec3 scanColor = col.rgb - scanLine; 15 | 16 | if (col.r < 0.1f && col.g < 0.1f && col.b < 0.1f) { 17 | if (screenBurn) { 18 | float dist = (1.0 - distance(vec2(gl_FragCoord.x / screenSize.x, gl_FragCoord.y / screenSize.y), vec2(0.5,0.5))) * 0.2; 19 | FragColor = vec4(0.0, dist, dist, 1.0); 20 | } else { 21 | FragColor = vec4(0.0, 0.0, 0.0, 1.0); 22 | } 23 | } else { 24 | FragColor = vec4(scanColor, 1.0); 25 | } 26 | } -------------------------------------------------------------------------------- /resources/scanlines.vs: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | layout (location = 0) in vec2 aPos; 3 | layout (location = 1) in vec2 aTexCoords; 4 | 5 | out vec2 TexCoords; 6 | 7 | void main() 8 | { 9 | TexCoords = aTexCoords; 10 | gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0); 11 | } 12 | -------------------------------------------------------------------------------- /resources/terminal8x8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebracket/rustyroguelike/06f6ef51829b07962e58896e0fad13a78bd1a92d/resources/terminal8x8.jpg -------------------------------------------------------------------------------- /resources/vga8x16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebracket/rustyroguelike/06f6ef51829b07962e58896e0fad13a78bd1a92d/resources/vga8x16.jpg -------------------------------------------------------------------------------- /screenshots/RustHelloWorld2.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebracket/rustyroguelike/06f6ef51829b07962e58896e0fad13a78bd1a92d/screenshots/RustHelloWorld2.JPG -------------------------------------------------------------------------------- /screenshots/RustyRoguelike.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebracket/rustyroguelike/06f6ef51829b07962e58896e0fad13a78bd1a92d/screenshots/RustyRoguelike.gif -------------------------------------------------------------------------------- /screenshots/RustyRoguelike10.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebracket/rustyroguelike/06f6ef51829b07962e58896e0fad13a78bd1a92d/screenshots/RustyRoguelike10.gif -------------------------------------------------------------------------------- /screenshots/RustyRoguelike11.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebracket/rustyroguelike/06f6ef51829b07962e58896e0fad13a78bd1a92d/screenshots/RustyRoguelike11.gif -------------------------------------------------------------------------------- /screenshots/RustyRoguelike2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebracket/rustyroguelike/06f6ef51829b07962e58896e0fad13a78bd1a92d/screenshots/RustyRoguelike2.gif -------------------------------------------------------------------------------- /screenshots/RustyRoguelike3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebracket/rustyroguelike/06f6ef51829b07962e58896e0fad13a78bd1a92d/screenshots/RustyRoguelike3.gif -------------------------------------------------------------------------------- /screenshots/RustyRoguelike4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebracket/rustyroguelike/06f6ef51829b07962e58896e0fad13a78bd1a92d/screenshots/RustyRoguelike4.gif -------------------------------------------------------------------------------- /screenshots/RustyRoguelike5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebracket/rustyroguelike/06f6ef51829b07962e58896e0fad13a78bd1a92d/screenshots/RustyRoguelike5.gif -------------------------------------------------------------------------------- /screenshots/RustyRoguelike6.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebracket/rustyroguelike/06f6ef51829b07962e58896e0fad13a78bd1a92d/screenshots/RustyRoguelike6.gif -------------------------------------------------------------------------------- /screenshots/RustyRoguelike7.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebracket/rustyroguelike/06f6ef51829b07962e58896e0fad13a78bd1a92d/screenshots/RustyRoguelike7.gif -------------------------------------------------------------------------------- /screenshots/RustyRoguelike8.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebracket/rustyroguelike/06f6ef51829b07962e58896e0fad13a78bd1a92d/screenshots/RustyRoguelike8.gif -------------------------------------------------------------------------------- /screenshots/RustyRoguelike9.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebracket/rustyroguelike/06f6ef51829b07962e58896e0fad13a78bd1a92d/screenshots/RustyRoguelike9.gif -------------------------------------------------------------------------------- /screenshots/posts/week01.md: -------------------------------------------------------------------------------- 1 | Since I've written a few roguelikes before (see my incredibly verbose Sharing Saturday posts!), I thought I'd use this as an excuse to learn Rust. Rather than use a `libtcod` port, I figured I'd write that, too. I have invented many wheels of varying degrees of roundness! 2 | 3 | Here is the git repo: [Rusty Roguelike](https://github.com/thebracket/rustyroguelike). If you have any interest in Rust, feel free to crib whatever you like from it. I'm a newbie to the language, so it's probably not great, idiomatic, or etc. 4 | 5 | I've gone a little further than this week, but you [browse to this commit](https://github.com/thebracket/rustyroguelike/tree/7000a15e6ccd96e430d32f51fef1dabf208ebdb8), it's right at the end of week 1. 2 major achievements: 6 | 7 | * [Hello Rusty World](https://raw.githubusercontent.com/thebracket/rustyroguelike/master/resources/RustHelloWorld2.JPG) 8 | * [Move an @ around](https://raw.githubusercontent.com/thebracket/rustyroguelike/master/resources/RustyRoguelike.gif) 9 | 10 | So, how did I get this far? This was my second ever Rust project (the first was throwing spaghetti at the wall, from console "hello world" to fibonacci sequences and threaded prime number generation; the stuff I do in every language to see if I have the slightest feel for how to plug it together) - so a lot of trial and error. 11 | 12 | * Creating a project was pretty simple with `cargo new rustyrogulike --bin`. This did more than I expected; it made a project skeleton (which you can test with `cargo build`, `cargo run`, `cargo check` and similar), made a `git` repo (so I just had to set the upstream to point it at the public Github). This gave me a canonical "hello world" on the console. 13 | * I'm comfortable with `glfw` from Nox Futura, so I figured I'd use it as a base. Getting the support for it was as easy as adding a few lines to `cargo.toml` (`cgmath = "0.16.1" glfw = "0.29.0" glm = "0.2.3" gl = "0.12.0" image = "0.19.0"`). One `cargo check` later and everything downloaded and compiled. Nice. 14 | * I swear by the tutorials on [https://learnopengl.com/](https://learnopengl.com/), so I was thrilled to see a [Rust port](https://github.com/bwasty/learn-opengl-rs). I spent a bit of time messing around getting a colored triangle onto the screen. 15 | * I figured out how to make a module! Create a folder named `rltk` (Roguelike Toolkit - the name of my C++ project that provides roguelike infrastructure), make a `mod.rs` file in it and put the module in there. I still didn't like having everything in one big file, so I broke it out into smaller ones. This proved troublesome. The magic incantation turned out to be that the `mod.rs` file had to include some lines for each file: `pub use self::color::Color` (for example; that exports my `Color` structure from the `color.rs` file without ridiculously huge namespace names) and `mod color` (to tell it to use the file). 16 | * I ended up with the `Rltk` structure that talks to OpenGL and gives me a window, and the `Console` structure that builds an array of glyph/color pairs, exposes some helpers like `cls` and `print`, and wraps the functionality to build vertex/index buffers describing my console in GL terms and with appropriate texture coordinates to output the characters. It also uses the `image` crate to load the terminal. I ran into a gotcha: loading a `png` file didn't work properly, so the font is a `jpg`. 17 | * This got me to the point of the "hello world" gif above. 18 | * I also spent a bit of time working on separating the boilerplate required to get things running (in `main.rs`) from the game logic (in `game.rs`). More on that below. 19 | * Handling keyboard input brought me into the wonderful world of *pattern matching*. This is probably my favorite feature of Rust, so far. The `glfw::flush_messages` command returns a big list of variants (union types) for events that might come from the OS/library. You loop through them and `match event` on each of them. The wildcard matching is *awesome*. so `glfw::WindowEvent::Key(_, KEY, Action::Press, _) => { self.key = Some(KEY); }` matches events that are a `Key` event, with the type set to `Press`. The `_` means "I don't care what you put in there", and the `KEY` means "fill a variable named KEY with whatever is in this slot". At this point, I just save what key was pressed - but wrapped in `Some`. "Key" is an *option* - so it either has `None` or `Some(data)`. That's similar to a C++ optional, but a bit easier to read (and saves a bool, I guess). 20 | 21 | So, the *main loop*. Unlike other languages, if you try and use mutable (changeable) global variables, Rust will complain bitterly and make you write the dreaded "unsafe" everywhere you use them. That's because it can't guaranty that threads will treat it properly. I'm not using threads, but Rust doesn't care about such trivialities. I also wanted to make the `rltk` portion reusable (maybe get to the point that a Rust version of RLTK is available), so it was important to me that the `rltk` library not know anything about how the actual game works. 22 | 23 | So I ended up with the following boilerplate: 24 | 25 | mod rltk; 26 | mod game; 27 | 28 | fn main() { 29 | let mut gs = game::State::new(); 30 | let mut console = rltk::Rltk::init_simple_console(80, 50, "Hello World".to_string()); 31 | let mut tick_func = |console : &mut rltk::Console| { 32 | gs.tick(console); 33 | }; 34 | console.main_loop(&mut tick_func); 35 | } 36 | 37 | That's actually more boilerplate than I'd like, but it works. It starts by saying "I'm going to use the modules rltk and game" (the `mod` statements). The main function initialises `gs`, my game state. Then it initializes an 80x50 console with "Hello World" as the title. The *ugly* `let mut tick_func` stuff is a *closure* - or a lambda in other languages. It defines `tick_func` to be a special function that captures its surrounding environment - in this case the game state. So when I call the `main_loop` function, it takes that as a parameter, and calls back into my code. This was a bit of a dance, and requried learning a lot of gotchas - but it works. 38 | 39 | So on each tick, the console calls my game's `tick` function, and - if anything changed - redraws the console. The tick function simply calls code to draw the map, draw an `@` at the player's location, and matches on the `key` variable I talked about above to see if I've used cursor keys - and moves the player if I have. 40 | 41 | Hope this helps someone! -------------------------------------------------------------------------------- /screenshots/posts/week02.md: -------------------------------------------------------------------------------- 1 | Continuing to learn Rust by implementing the tutorial and the bits of `libtcod` that I need! 2 | 3 | Here is the git repo: [Rusty Roguelike](https://github.com/thebracket/rustyroguelike). If you have any interest in Rust, feel free to crib whatever you like from it. I'm a newbie to the language, so it's probably not great, idiomatic, or etc. 4 | 5 | I've gone quite a bit further than part 2 (I'm in part 6 at time of writing), but if you [browse to this commit](https://github.com/thebracket/rustyroguelike/tree/e5cbfa830ee415cb594f5bc4d95b7997235768c1), it's right at the end of week 2 with a bit of week 3 mixed in. 6 | 7 | So the map with placement [looks like this](https://raw.githubusercontent.com/thebracket/rustyroguelike/e5cbfa830ee415cb594f5bc4d95b7997235768c1/resources/RustyRoguelike2.gif). 8 | 9 | I've also made some significant progress on the library side of things. I figured out enough about traits to reduce the main file boilerplate to: 10 | 11 | fn main() { 12 | let mut gs = game::State::new(); 13 | let mut rltk = rltk::init_with_simple_console(80, 50, "Rusty Roguelike"); 14 | rltk.main_loop(&mut gs); 15 | } 16 | 17 | The key is that gamestate now implements a trait, defined in RLTK: 18 | 19 | pub trait GameState { 20 | fn tick(&mut self, ctx : &mut Rltk); 21 | } 22 | 23 | This is pretty cool. So now, *any* type that implements that can be passed to the main loop to provide a tick function. The clunky closure/lambda stuff is gone, making for a much cleaner setup. I used the same system to allow the map type to expose some information about the map, without RLTK having to know how the map works; in particular, a `get_available_exits` function. 24 | 25 | On the library side, I've achieved (and will talk about in subsequent weeks as they come up): 26 | 27 | * Traits that provide point-to-index and index-to-point conversions for map access (so you can stride your arrays however you like). 28 | * Geometry functions that can provide distance and squared distance between two points (basic Euclid). 29 | * Field of view implementation that doesn't know about your map format (brute force). 30 | * Dijkstra flow map generation for path-finding. 31 | * A-star path generation. 32 | * Mouse support. 33 | 34 | Implementing the map for this week was pretty much a 1:1 port of the tutorial. The algorithm was pretty easy to port, no major issues there. I did return a vector or rooms, rather than keep it around - and then iterate the rooms (picking a center point) for mob placement. Mobs are a basic vector. I *didn't* put the player in the mobs list - so it's not a really generic list at this point (I haven't quite figured out how to do that efficiently in Rust, yet). -------------------------------------------------------------------------------- /screenshots/posts/week03.md: -------------------------------------------------------------------------------- 1 | Continuing to learn Rust by implementing the tutorial and the bits of `libtcod` that I need! 2 | 3 | Here is the git repo: [Rusty Roguelike](https://github.com/thebracket/rustyroguelike). If you have any interest in Rust, feel free to crib whatever you like from it. I'm a newbie to the language, so it's probably not great, idiomatic, or etc. 4 | 5 | I've now finished the tutorial in Rust, and am working on turning RLTK_RS into a Rust library - complete with examples and tutorials. It'll be a bit before that's ready. 6 | 7 | Week 3's tutorial went reasonably smoothly. I actually did parts of week 3 and 4 together, so it's a little harder to separate them than I'd like. You can browse the repo at the time when a random map is generated [here](https://github.com/thebracket/rustyroguelike/tree/c632e4c7c230fe536e01855428abf69f505f62cb). The mob stuff got mixed into a few later commits, but I did field-of-view first (trying to get my head around some Rust concepts). 8 | 9 | The actual map generation wasn't too bad at all: 10 | 11 | * I extended the `map` struct to implement `random_rooms_tut3`, which uses the same algorithm as the tutorial. This required that I learn the `rand` system from Rust (it works pretty well - word of warning, a new version is just coming out that changes the syntax quite a lot), and also made a `Rect` structure that implements `center` and `intersects` to assist. My implementation returns a vector of `Rect` structures for each room. 12 | * This version then iterates the vector of rects (`Rooms`), and places one mob in the center of each room. 13 | 14 | For mobs, I went with a super simple definition at this point: 15 | 16 | pub struct Mob { 17 | pub x : i32, 18 | pub y : i32, 19 | pub glyph: u8, 20 | pub fg : Color, 21 | } 22 | 23 | And I simply added a `Vec` to the game state. Since the goal is reusable code, I created a "trait" named `Renderable`. (Traits are like extension classes in other languages; you can implement a trait for most types, and then call the functions for that trait on the type. You setup a prototype/interface, and implement it on every type you want; you can also use polymorphism to store a mixed bag of structs that implement a trait - I did that a bit late in the game, more on that in coming weeks!). I also implemented `Renderable` for `Player`. So the render code simply calls `draw` on all mobs and the player. For now, the implementations are identical: put an ASCII glyph on the map in the right place, in the right color. 24 | 25 | My collision detection is relatively primitive. When I detect a player movement request, I calculate the tile on which they will land - and ask the map if that tile is walkable. If it is, then I iterate through the `mobs` list and see if their position matches my position. If it does, the move is cancelled and a message (on the console, since we don't have a GUI yet) says "you kick the monster". 26 | 27 | One fun discovery: structs don't have an equality operator by default, but you don't have to implement operator overloading to make it happen. You can tack `#[derive(Eq, PartialEq)]` onto a struct to have it do a member-wise comparison for equality purposes. Likewise, if you put `#[derive(Copy, Clone)]` into a stuct it no longer moves every time you assign it - it simply splats a copy out. This was especially useful for `Color` - rather than have references everywhere, it simply copies the values. Given that a `Color` is just three 32-bit floats, that's near instant since the compiler is smart enough to use registers for the purpose (and a pointer is 64-bits, so it's only slightly larger than a reference anyway). 28 | 29 | After week 3, I used this discovery to move all my `x,y` type structures into a `Point` struct. It looks cleaner that way! -------------------------------------------------------------------------------- /screenshots/posts/week04.md: -------------------------------------------------------------------------------- 1 | My continuing adventure to boldly go where quite a few Rust programmers have been before - and learn a lot doing it. :-) 2 | 3 | The repo for my Rust implementation of the tutorial is here: [https://github.com/thebracket/rustyroguelike](https://github.com/thebracket/rustyroguelike). As I've mentioned before, I've actually completed the tutorial now - so I'll write a bit about my implementation of tutorials 6 and 7. 4 | 5 | *Field of View Extension* 6 | 7 | I started out by extending my *Field of View* code to also give mobs a visibility list. It only updates when they move (lists tiles they can see, rather than contents), and is fast enough that calculating them isn't a big resource drain. I did tweak the implementation a bit to pre-allocate space rather than dynamically resizing vectors, which reduced the amount of work to be done. Then I gave mobs an FoV, and for their AI tick it simply checks the FoV list to see if the player is in it - and if they are, the mob activates. 8 | 9 | *Path Finding* 10 | 11 | The mob simply paths straight towards the player. I first implemented this with Dijkstra flow maps; I like to implement these whenever I'm learning a new language: they are *really* useful, really easy to get working on the basic level (with lots of room for improvement), and conceptually a lot easier than A-Star. I *highly* recommend [this article](http://www.roguebasin.com/index.php?title=The_Incredible_Power_of_Dijkstra_Maps) for reasons why you should implement them, too! 12 | 13 | Anyway, the basic implementation went well: pass in an array of "starting points" (distance 0 spots), it iterates the list running a search on each (only applying depth if the new depth is shorter than the old one) using the time-tested open/closed list. I did learn that Rust's `HashMap` implementation could use some work - it was faster to search a small vector than to use set membership for the closed list. 14 | 15 | It worked well, and mobs [chase the player](https://raw.githubusercontent.com/thebracket/rustyroguelike/master/screenshots/RustyRoguelike5.gif). 16 | 17 | I then went ahead and implemented A-Star anyway. I basically ported the version from *One Knight in the Dungeon* over, and it also works well. Speed wise, it's about the same as the C++ version. 18 | 19 | *Fighters* 20 | 21 | The tutorial makes a point of putting combat stuff in the *Fighter* component, so I did the same. I was surprised to discover that traits (Rust's polymorphism) don't do data members, so you end up implementing getters and setters. That felt like a step backwards, but ok. I also setup a `Combat` trait, and put the ability to bash things in there. This was pretty straight forward, and worked decently enough. 22 | 23 | *User Interface* 24 | 25 | I extended RLTK to include box drawing, horizontal/vertical progress bars (that work great for health bars) and a console. Since I was already passing text results around, it was a pretty straightforward task to put them into UI elements instead of sending them to the text console. The [results are quite nice](https://raw.githubusercontent.com/thebracket/rustyroguelike/master/screenshots/RustyRoguelike6.gif). 26 | 27 | I really wasn't enjoying my tooltips, so I hacked together a quick Cogmind-like setup (only less sophisticated and not as pretty). [Not bad for 10 minutes](https://raw.githubusercontent.com/thebracket/rustyroguelike/master/screenshots/RustyRoguelike7.gif). It's very primitive: it goes to the left or right of the cursor based on absolute screen position (so if you are in the right half of the screen, it goes left - and vice versa). 28 | 29 | I also went slightly out of order and [put a main menu in place](https://raw.githubusercontent.com/thebracket/rustyroguelike/master/screenshots/RustyRoguelike9.gif). I didn't get to save/load until later (the screenshot is from a slightly later build), but it felt like a good time to worry about look/feel. The Matrix effect isn't overly useful, nor does it really fit - but I had fun making it! 30 | 31 | **RLTK_RS - Rust Library** 32 | 33 | I've been hard at working taking the library code from RR, and turning it into a library: [https://github.com/thebracket/rltk_rs](https://github.com/thebracket/rltk_rs). I don't recommend *using* it until it hits version 0.2 - I'm still tweaking the API, so I'll potentially break anything you write at this point. 34 | 35 | * It's focused on providing a simple API: minimal bootstrapping, and then clear, consise calls to render code. 36 | * It's obsessively agnostic about how your code is arranged. Your map can implement the `Algorithm2D` trait, and teach it to translate x/y positions to whatever indexing scheme you are using behind the scenes. This then automatically provides distance and other geometry functions. Implement `BaseMap` (which requires that you provide an `is_opaque` function and a `get_available_exits` function - the latter listing directions you can travel from a tile) adapts path-finding (A-Star and Dijkstra) to your map format. 37 | * It supports multiple render layers (with or without transparency), which can optionally use different font files. So if you want your GUI in a nicely readable 8x16 VGA font, and your game in a square 8x8 font - it's got your back. It should also work with tilesets, but I haven't tested that yet. 38 | * There's a host of color support options. Colors are a basic `RGB` triple. I imported the X11 `rgb.txt` colorset (this is also used by the W3C for named colors in HTML/CSS), and setup helpers for them all. So if you want to use named colors, they are as simple as `RGB::named(rltk::YELLOW)`. It also knows `RGB::from_u8(255,255,0)`, `RGB::from_f32(1.0, 1.0, 0.0)`, and `RGB::from_hex("#FFFF00")`. There's various helpers such as `greyscale` (uses a fixed weighting to provide a quick to-grey function), `to_hsv` (turns it into a hue/saturation/value triplet), `lerp` (for stepping between colors), and basic math (add to/subtract from, multiply by, etc. between colors and colors and a float). 39 | * I had some fun with the Dijkstra code, and have been trying to produce my fastest Dijkstra code to date. It now outperforms the code in *One Knight* by a large margin (although I'm busily porting it back to UE4/C++ since that's the largest slowdown remaining in the project). If you have a large number of starting points, it batches them based on the number of CPUs you have available and calculates Dijkstra maps for each batch - and then recombines. On a *release* build, it can handle thousands of target nodes without slowing down from 60 FPS now. Rust really did make the parallelization easy. 40 | 41 | If you build it right now, it'll want you to have `cmake` and a full C++ toolchain available. That's because it goes out, downloads `glfw` (a C library) and builds it! I don't really like that at all - so I'm in the process of porting it over to `glutin` - a Rust-native OpenGL library. This is hard work, and I'm struggling to not change the API. It's in a branch (not sure if I've pushed that branch to github - been working on it locally), but it gets rid of the C toolchain requirement and is a little faster. In theory, it also supports web assembly (WASM) - untested, so far. 42 | 43 | -------------------------------------------------------------------------------- /src/game/entity.rs: -------------------------------------------------------------------------------- 1 | use crate::rltk; 2 | use rltk::{RGB, Rltk, Point, Console}; 3 | use super::{Map, Player, Combat, Mob, Item}; 4 | extern crate typetag; 5 | 6 | #[typetag::serde(tag = "BaseEntity")] 7 | pub trait BaseEntity { 8 | fn get_position(&self) -> Point; 9 | fn get_fg_color(&self) -> RGB; 10 | fn get_glyph(&self) -> u8; 11 | 12 | fn draw_to_map(&self, ctx : &mut Rltk, map : &Map) { 13 | if map.is_tile_visible(self.get_position()) { 14 | let pos = self.get_position(); 15 | ctx.set(pos.x, pos.y, self.get_fg_color(), RGB::named(rltk::BLACK), self.get_glyph()); 16 | } 17 | } 18 | 19 | fn as_player(&self) -> Option<&Player> { None } 20 | fn as_player_mut(&mut self) -> Option<&mut Player> { None } 21 | fn as_combat(&mut self) -> Option<&mut Combat> { None } 22 | fn as_mob_mut(&mut self) ->Option<&mut Mob> { None } 23 | fn as_item(&self) -> Option<&Item> { None } 24 | fn plot_visibility(&mut self, map : &Map); 25 | fn get_tooltip_text(&self) -> String; 26 | fn blocks_tile(&self) -> bool { false } 27 | fn can_be_attacked(&self) -> bool { false } 28 | fn is_dead(&self) -> bool { false } 29 | fn is_mob(&self) -> bool { false } 30 | fn get_name(&self) -> String; 31 | fn can_pickup(&self) -> bool { false } 32 | fn is_player(&self) -> bool { false } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /src/game/fighter.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::{min}; 2 | use super::{Player, Mob}; 3 | extern crate serde; 4 | use serde::{Serialize, Deserialize}; 5 | 6 | #[derive(Serialize, Deserialize, Clone)] 7 | pub struct Fighter { 8 | pub max_hp : i32, 9 | pub hp: i32, 10 | pub defense: i32, 11 | pub power: i32, 12 | pub dead: bool, 13 | pub xp_value : i32 14 | } 15 | 16 | impl Fighter { 17 | pub fn new(max_hp: i32, defense: i32, power: i32, xp:i32) -> Fighter { 18 | Fighter{ 19 | max_hp, 20 | hp: max_hp, 21 | defense, 22 | power, 23 | dead: false, 24 | xp_value : xp 25 | } 26 | } 27 | } 28 | 29 | pub trait Combat { 30 | fn get_power(&self)->i32; 31 | fn get_defense(&self)->i32; 32 | fn take_damage(&mut self, amount:i32); 33 | fn heal_damage(&mut self, amount:i32); 34 | fn get_name(&self)->String; 35 | fn get_hp(&self)->i32; 36 | fn kill(&mut self); 37 | fn xp_value(&self)->i32 { 0 } 38 | } 39 | 40 | pub fn attack(instigator_name: String, instigator_power : i32, target: &mut Combat) -> (i32, Vec) { 41 | let mut results = Vec::new(); 42 | let mut xp = 0; 43 | 44 | let damage = instigator_power - target.get_defense(); 45 | if damage > 0 { 46 | target.take_damage(damage); 47 | results.push(format!("{} attacks {}, for {} hit points of damage.", instigator_name, target.get_name(), damage)); 48 | results.push(format!("{} has {} remaining hit points.", target.get_name(), target.get_hp())); 49 | if target.get_hp() < 1 { 50 | results.push(format!("{} is dead.", target.get_name())); 51 | target.kill(); 52 | xp += target.xp_value(); 53 | } 54 | } else { 55 | results.push(format!("{} attacks {}, but lacks the power to do anything useful.", instigator_name, target.get_name())); 56 | } 57 | 58 | (xp, results) 59 | } 60 | 61 | impl Combat for Player { 62 | fn take_damage(&mut self, amount:i32) { 63 | self.fighter.hp -= amount; 64 | } 65 | 66 | fn heal_damage(&mut self, amount:i32) { 67 | self.fighter.hp = min(self.fighter.max_hp, self.fighter.hp + amount); 68 | } 69 | 70 | fn get_name(&self) -> String { 71 | "Player".to_string() 72 | } 73 | 74 | fn get_defense(&self) -> i32 { 75 | let mut defense = self.fighter.defense; 76 | for item in self.inventory.equipped.iter() { 77 | defense += item.equippable.unwrap().defense_bonus; 78 | } 79 | defense 80 | } 81 | 82 | fn get_power(&self) -> i32 { 83 | let mut power = self.fighter.power; 84 | for item in self.inventory.equipped.iter() { 85 | power += item.equippable.unwrap().power_bonus; 86 | } 87 | power 88 | } 89 | 90 | fn get_hp(&self) -> i32 { self.fighter.hp } 91 | fn kill(&mut self) { self.fighter.dead = true; } 92 | } 93 | 94 | impl Combat for Mob { 95 | fn take_damage(&mut self, amount:i32) { 96 | self.fighter.hp -= amount; 97 | } 98 | 99 | fn heal_damage(&mut self, amount:i32) { 100 | self.fighter.hp = min(self.fighter.max_hp, self.fighter.hp + amount); 101 | } 102 | 103 | fn get_name(&self) -> String { 104 | self.name.clone() 105 | } 106 | 107 | fn get_defense(&self) -> i32 { self.fighter.defense } 108 | fn get_power(&self) -> i32 { self.fighter.power } 109 | fn get_hp(&self) -> i32 { self.fighter.hp } 110 | fn kill(&mut self) { self.fighter.dead = true; } 111 | fn xp_value(&self)->i32 { self.fighter.xp_value } 112 | } -------------------------------------------------------------------------------- /src/game/gamestate.rs: -------------------------------------------------------------------------------- 1 | use super::{gui, TickType, inventory, Map, Player, map_builder, Combat, BaseEntity, GameState, rltk, player, mob, TileType, Particle, vfx}; 2 | use rltk::{Rltk, RGB, Point}; 3 | use serde::{Serialize, Deserialize}; 4 | use std::fs; 5 | use std::fs::File; 6 | use std::io::Write; 7 | use std::path::Path; 8 | 9 | #[derive(Serialize, Deserialize)] 10 | pub struct State { 11 | pub map : Map, 12 | pub game_state : TickType, 13 | pub log : Vec, 14 | pub entities : Vec>, 15 | pub target_cell : Point, 16 | pub targeting_item : i32, 17 | pub prev_mouse_for_targeting : Point, 18 | pub menu_state : gui::MenuState, 19 | pub vfx : Vec 20 | } 21 | 22 | impl GameState for State { 23 | fn tick(&mut self, ctx : &mut Rltk) { 24 | vfx::age_particles(self, ctx); 25 | if self.game_state != TickType::MainMenu { gui::render(self, ctx, &self.map); } 26 | 27 | match self.game_state { 28 | TickType::MainMenu => { 29 | let result = gui::display_main_menu(ctx, &mut self.menu_state); 30 | match result { 31 | gui::MainMenuResult::Quit => { ctx.quit() } 32 | gui::MainMenuResult::Continue => { 33 | let saved = State::load_saved(); 34 | self.map = saved.map; 35 | self.game_state = saved.game_state; 36 | self.log = saved.log; 37 | self.entities = saved.entities; 38 | self.target_cell = saved.target_cell; 39 | self.targeting_item = saved.targeting_item; 40 | self.prev_mouse_for_targeting = saved.prev_mouse_for_targeting; 41 | } 42 | gui::MainMenuResult::New => { 43 | let saved = State::new(0); 44 | self.map = saved.map; 45 | self.game_state = saved.game_state; 46 | self.log = saved.log; 47 | self.entities = saved.entities; 48 | self.target_cell = saved.target_cell; 49 | self.targeting_item = saved.targeting_item; 50 | self.prev_mouse_for_targeting = saved.prev_mouse_for_targeting; 51 | } 52 | _ => {} 53 | } 54 | } 55 | TickType::PlayersTurn => { 56 | let result = player::player_tick(self, ctx); 57 | if result == player::PlayerTickResult::NextMap { 58 | // Move to next level 59 | self.player_mut().dungeon_level += 1; 60 | let mut saved = State::new(self.player().dungeon_level); 61 | saved.player_mut().copy_from_other_player(self.player()); 62 | 63 | self.map = saved.map; 64 | self.entities = saved.entities; 65 | self.add_log_entry("You descend to the next level, and take a moment to rest.".to_string()); 66 | } 67 | } 68 | TickType::EnemyTurn => { 69 | mob::mob_tick(self); 70 | self.game_state = TickType::PlayersTurn; 71 | if self.player().fighter.dead { 72 | self.game_state = TickType::GameOver; 73 | if Path::new("./savegame.json").exists() { std::fs::remove_file("./savegame.json").expect("Unable to delete file"); } 74 | } 75 | } 76 | TickType::GameOver => { gui::display_game_over_and_handle_quit(ctx, self); } 77 | TickType::UseMenu => { inventory::use_item(self, ctx); } 78 | TickType::DropMenu => { inventory::drop_item(self, ctx); } 79 | TickType::WieldMenu => { inventory::wield_item(self, ctx); } 80 | TickType::UnequipMenu => { inventory::unequip_item(self, ctx); } 81 | TickType::TargetingItem => { inventory::item_targeting(self, ctx); } 82 | TickType::LevelUpMenu => { gui::handle_level_up(ctx, self); } 83 | TickType::CharacterMenu => { gui::display_character_info(ctx, self); } 84 | TickType::HelpMenu => { gui::display_help_info(ctx, self); } 85 | TickType::None => {} 86 | } 87 | } 88 | } 89 | 90 | impl State { 91 | pub fn new_menu() -> State { 92 | State{ 93 | map: Map::new(80, 43), 94 | game_state: TickType::MainMenu, 95 | log: Vec::new(), 96 | entities : Vec::new(), 97 | target_cell : Point::new(-1,-1), 98 | targeting_item : -1, 99 | prev_mouse_for_targeting : Point::new(-1,-1), 100 | menu_state: gui::MenuState::new(), 101 | vfx : Vec::new() 102 | } 103 | } 104 | 105 | pub fn load_saved() -> State { 106 | let data = fs::read_to_string("./savegame.json").expect("Unable to read file"); 107 | let loaded : State = serde_json::from_str(&data).unwrap(); 108 | std::fs::remove_file("./savegame.json").expect("Unable to delete file"); 109 | loaded 110 | } 111 | 112 | pub fn new(depth: i32) -> State { 113 | if Path::new("./savegame.json").exists() { std::fs::remove_file("./savegame.json").expect("Unable to delete file"); } 114 | 115 | let mut entities : Vec> = Vec::new(); 116 | let mut map = Map::new(80, 43); 117 | let rooms = map_builder::random_rooms_tut3(&mut map); 118 | let (player_x, player_y) = rooms[0].center(); 119 | let mobs = map_builder::spawn_mobs(&rooms, depth); 120 | let items = map_builder::spawn_items(&rooms, &mobs, depth); 121 | let mut player = Player::new(player_x, player_y, 64, RGB::named(rltk::YELLOW)); 122 | let stairs_pos = rooms[rooms.len()-1].center(); 123 | map.tiles[((stairs_pos.1 * 80) + stairs_pos.0) as usize] = TileType::Stairs; 124 | 125 | // Start with a viewshed 126 | player.plot_visibility(&map); 127 | map.set_visibility(&player.visible_tiles); 128 | 129 | entities.push(Box::new(player)); 130 | for m in mobs { 131 | entities.push(Box::new(m)); 132 | } 133 | for i in items { 134 | entities.push(Box::new(i)); 135 | } 136 | 137 | State{ 138 | map, 139 | game_state: TickType::PlayersTurn, 140 | log: Vec::new(), 141 | entities, 142 | target_cell : Point::new(-1,-1), 143 | targeting_item : -1, 144 | prev_mouse_for_targeting : Point::new(-1,-1), 145 | menu_state : gui::MenuState::new(), 146 | vfx : Vec::new() 147 | } 148 | } 149 | 150 | pub fn player(&self) -> &Player { 151 | self.entities[0].as_player().unwrap() 152 | } 153 | 154 | pub fn player_mut(&mut self) -> &mut Player { 155 | self.entities[0].as_player_mut().unwrap() 156 | } 157 | 158 | pub fn player_as_combat(&mut self) -> &mut Combat { 159 | self.entities[0].as_combat().unwrap() 160 | } 161 | 162 | pub fn update_visibility(&mut self) { 163 | for e in self.entities.iter_mut() { 164 | e.plot_visibility(&self.map); 165 | } 166 | 167 | let vt = self.player().visible_tiles.clone(); 168 | self.map.set_visibility(&vt); 169 | } 170 | 171 | pub fn add_log_entry(&mut self, line : String) { 172 | self.log.insert(0, line.clone()); 173 | while self.log.len() > 5 { self.log.remove(4); } 174 | } 175 | 176 | pub fn save(&self) { 177 | let data = serde_json::to_string(&self).unwrap(); 178 | let mut f = File::create("./savegame.json").expect("Unable to create file"); 179 | f.write_all(data.as_bytes()).expect("Unable to write data"); 180 | } 181 | } -------------------------------------------------------------------------------- /src/game/gui.rs: -------------------------------------------------------------------------------- 1 | use crate ::rltk; 2 | use crate ::rltk::Console; 3 | use rltk::{Rltk, Point, RGB, Algorithm2D, VirtualKeyCode}; 4 | use super::{Map, TileType, State, TickType}; 5 | use std::cmp::{max, min}; 6 | use serde::{Serialize, Deserialize}; 7 | use rand::Rng; 8 | use std::path::Path; 9 | 10 | pub enum ItemMenuResult { Cancel, NoResponse, Selected } 11 | 12 | pub fn render(gs : &State, ctx : &mut Rltk, map : &Map) { 13 | draw_map(ctx, map); 14 | draw_entities(gs, ctx, map); 15 | draw_user_interface(gs, ctx); 16 | draw_mouse_info(gs, ctx, map); 17 | for p in gs.vfx.iter() { 18 | p.render(ctx); 19 | } 20 | } 21 | 22 | fn draw_map(ctx : &mut Rltk, map : &Map) { 23 | ctx.cls(); 24 | 25 | let mut idx = 0; 26 | for y in 0 .. map.height { 27 | for x in 0 .. map.width { 28 | 29 | // You wouldn't normally make this mess - clean up! 30 | if map.revealed[idx] { 31 | if map.visible[idx] { 32 | match map.tiles[idx] { 33 | TileType::Floor => { ctx.print_color(x, y, RGB::named(rltk::DARK_GREEN), RGB::named(rltk::BLACK), ".") } 34 | TileType::Wall => { ctx.set(x, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), decorate_wall_tile(map, Point::new(x,y))) } 35 | TileType::Stairs => { ctx.print_color(x, y, RGB::named(rltk::MAGENTA), RGB::named(rltk::BLACK), ">") } 36 | } 37 | } else { 38 | match map.tiles[idx] { 39 | TileType::Floor => { ctx.print_color(x, y, RGB::named(rltk::GREY), RGB::named(rltk::BLACK), ".") } 40 | TileType::Wall => { ctx.set(x, y, RGB::named(rltk::GREY), RGB::named(rltk::BLACK), decorate_wall_tile(map, Point::new(x,y))) } 41 | TileType::Stairs => { ctx.print_color(x, y, RGB::named(rltk::GREY), RGB::named(rltk::BLACK), ">") } 42 | } 43 | } 44 | } 45 | 46 | idx += 1; 47 | } 48 | } 49 | } 50 | 51 | fn is_revealed_and_wall(map : &Map, coord: Point) -> bool { 52 | let idx = map.point2d_to_index(coord) as usize; 53 | map.tiles[idx] == TileType::Wall && map.revealed[idx] 54 | } 55 | 56 | fn decorate_wall_tile(map : &Map, coord: Point) -> u8 { 57 | if coord.x == 0 || coord.x == map.width || coord.y == 0 || coord.y == map.height { return 35; } 58 | let mut mask : u8 = 0; 59 | if is_revealed_and_wall(map, Point::new(coord.x, coord.y - 1)) { mask += 1; } 60 | if is_revealed_and_wall(map, Point::new(coord.x, coord.y + 1)) { mask += 2; } 61 | if is_revealed_and_wall(map, Point::new(coord.x - 1, coord.y)) { mask += 4; } 62 | if is_revealed_and_wall(map, Point::new(coord.x + 1, coord.y)) { mask += 8; } 63 | 64 | match mask { 65 | 0 => { 9 } // Pillar because we can't see neighbors 66 | 1 => { 186 } // Wall only to the north 67 | 2 => { 186 } // Wall only to the south 68 | 3 => { 186 } // Wall to the north and south 69 | 4 => { 205 } // Wall only to the west 70 | 5 => { 188 } // Wall to the north and west 71 | 6 => { 187 } // Wall to the south and west 72 | 7 => { 185 } // Wall to the north, south and west 73 | 8 => { 205 } // Wall only to the east 74 | 9 => { 200 } // Wall to the north and east 75 | 10 => { 201 } // Wall to the south and east 76 | 11 => { 204 } // Wall to the north, south and east 77 | 12 => { 205 } // Wall to the east and west 78 | 13 => { 202 } // Wall to the east, west, and south 79 | 14 => { 203 } // Wall to the east, west, and north 80 | _ => { 35 } // We missed one? 81 | } 82 | } 83 | 84 | fn draw_entities(gs: &State, ctx: &mut Rltk, map : &Map) { 85 | for e in gs.entities.iter() { 86 | e.draw_to_map(ctx, &map); 87 | } 88 | } 89 | 90 | fn draw_user_interface(gs: &State, ctx : &mut Rltk) { 91 | let mouse_pos = ctx.mouse_pos(); 92 | ctx.set_bg(mouse_pos.0, mouse_pos.1, RGB::named(rltk::MAGENTA)); 93 | ctx.draw_box(1, 43, 78, 6, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); 94 | 95 | let maplvl = format!("Depth: {} ", gs.player().dungeon_level); 96 | ctx.print_color(3, 43, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), &maplvl); 97 | 98 | let health = format!(" HP: {} / {} ", gs.player().fighter.hp, gs.player().fighter.max_hp); 99 | ctx.print_color(12, 43, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), &health); 100 | 101 | ctx.draw_bar_horizontal(28, 43, 51, gs.player().fighter.hp, gs.player().fighter.max_hp, RGB::named(rltk::RED), RGB::named(rltk::BLACK)); 102 | 103 | let mut y = 44; 104 | for s in gs.log.iter() { 105 | ctx.print(2, y, &s.to_string()); 106 | y += 1; 107 | } 108 | } 109 | 110 | fn draw_mouse_info(gs : &State, ctx : &mut Rltk, map: &Map) { 111 | let mouse_pos = ctx.mouse_pos(); 112 | if map.is_tile_visible(Point::new(mouse_pos.0, mouse_pos.1)) { 113 | let mut tooltip : Vec = Vec::new(); 114 | 115 | let tile_info = map.tile_description(Point::new(mouse_pos.0, mouse_pos.1)); 116 | tooltip.push(format!("Tile: {}", tile_info)); 117 | 118 | for e in gs.entities.iter() { 119 | if e.get_position() == Point::new(mouse_pos.0, mouse_pos.1) { 120 | tooltip.push(e.get_tooltip_text()); 121 | } 122 | } 123 | 124 | if !tooltip.is_empty() { 125 | let mut width :i32 = 0; 126 | for s in tooltip.iter() { 127 | if width < s.len() as i32 { width = s.len() as i32; } 128 | } 129 | width += 3; 130 | 131 | if mouse_pos.0 > 40 { 132 | let arrow_pos = Point::new(mouse_pos.0 - 2, mouse_pos.1); 133 | let left_x = mouse_pos.0 - width; 134 | let mut y = mouse_pos.1; 135 | for s in tooltip.iter() { 136 | ctx.print_color(left_x, y, RGB::named(rltk::WHITE), RGB::named(rltk::GREY), &s.to_string()); 137 | let padding = (width - s.len() as i32)-1; 138 | for i in 0..padding { 139 | ctx.print_color(arrow_pos.x - i, y, RGB::named(rltk::WHITE), RGB::named(rltk::GREY), &" ".to_string()); 140 | } 141 | y += 1; 142 | } 143 | ctx.print_color(arrow_pos.x, arrow_pos.y, RGB::named(rltk::WHITE), RGB::named(rltk::GREY), &"->".to_string()); 144 | } else { 145 | let arrow_pos = Point::new(mouse_pos.0 + 1, mouse_pos.1); 146 | let left_x = mouse_pos.0 +3; 147 | let mut y = mouse_pos.1; 148 | for s in tooltip.iter() { 149 | ctx.print_color(left_x, y, RGB::named(rltk::WHITE), RGB::named(rltk::GREY), &s.to_string()); 150 | let padding = (width - s.len() as i32)-1; 151 | for i in 0..padding { 152 | ctx.print_color(left_x + s.len() as i32 + i, y, RGB::named(rltk::WHITE), RGB::named(rltk::GREY), &" ".to_string()); 153 | } 154 | y += 1; 155 | } 156 | ctx.print_color(arrow_pos.x, arrow_pos.y, RGB::named(rltk::WHITE), RGB::named(rltk::GREY), &"<-".to_string()); 157 | } 158 | } 159 | } 160 | } 161 | 162 | #[allow(non_snake_case)] 163 | pub fn handle_item_menu(gs : &mut State, ctx: &mut Rltk, title: S) -> (ItemMenuResult, i32) { 164 | let count = gs.player().inventory.items.len(); 165 | let mut y = (25 - (count / 2)) as i32; 166 | 167 | ctx.draw_box(15, y-2, 31, (count+3) as i32, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); 168 | ctx.print_color(18, y-2, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), &title.to_string()); 169 | 170 | for (j,i) in gs.player().inventory.items.iter().enumerate() { 171 | ctx.set(17, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), 40); 172 | ctx.set(18, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), 97+j as u8); 173 | ctx.set(19, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), 41); 174 | 175 | ctx.print(21, y, &i.name.to_string()); 176 | y += 1; 177 | } 178 | 179 | match ctx.key { 180 | None => {} 181 | Some(KEY) => { 182 | match KEY { 183 | VirtualKeyCode::Escape => { return (ItemMenuResult::Cancel, 0) } 184 | _ => { 185 | let selection = rltk::letter_to_option(KEY); 186 | if selection > -1 && selection < gs.player().inventory.items.len() as i32 { 187 | return (ItemMenuResult::Selected, selection); 188 | } 189 | return (ItemMenuResult::NoResponse, 0); 190 | } 191 | } 192 | } 193 | } 194 | 195 | (ItemMenuResult::NoResponse, 0) 196 | } 197 | 198 | #[allow(non_snake_case)] 199 | pub fn handle_equippable_menu(gs : &mut State, ctx: &mut Rltk, title: S) -> (ItemMenuResult, i32) { 200 | let equippable = gs.player().inventory.get_equippable_items(); 201 | let count = equippable.len(); 202 | let mut y = (25 - (count / 2)) as i32; 203 | 204 | ctx.draw_box(15, y-2, 31, (count+3) as i32, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); 205 | ctx.print_color(18, y-2, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), &title.to_string()); 206 | 207 | for (j,i) in equippable.iter().enumerate() { 208 | ctx.set(17, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), 40); 209 | ctx.set(18, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), 97+j as u8); 210 | ctx.set(19, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), 41); 211 | 212 | ctx.print(21, y, &gs.player().inventory.items[*i as usize].name.to_string()); 213 | y += 1; 214 | } 215 | 216 | match ctx.key { 217 | None => {} 218 | Some(KEY) => { 219 | match KEY { 220 | VirtualKeyCode::Escape => { return (ItemMenuResult::Cancel, 0) } 221 | _ => { 222 | let selection = rltk::letter_to_option(KEY); 223 | if selection > -1 && selection < gs.player().inventory.items.len() as i32 { 224 | return (ItemMenuResult::Selected, equippable[selection as usize]); 225 | } 226 | return (ItemMenuResult::NoResponse, 0); 227 | } 228 | } 229 | } 230 | } 231 | 232 | (ItemMenuResult::NoResponse, 0) 233 | } 234 | 235 | #[allow(non_snake_case)] 236 | pub fn handle_equipped_menu(gs : &mut State, ctx: &mut Rltk, title: S) -> (ItemMenuResult, i32) { 237 | let count = gs.player().inventory.equipped.len(); 238 | let mut y = (25 - (count / 2)) as i32; 239 | 240 | ctx.draw_box(15, y-2, 31, (count+3) as i32, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); 241 | ctx.print_color(18, y-2, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), &title.to_string()); 242 | 243 | for (j,i) in gs.player().inventory.equipped.iter().enumerate() { 244 | ctx.set(17, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), 40); 245 | ctx.set(18, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), 97+j as u8); 246 | ctx.set(19, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), 41); 247 | 248 | ctx.print(21, y, &i.name.to_string()); 249 | y += 1; 250 | } 251 | 252 | match ctx.key { 253 | None => {} 254 | Some(KEY) => { 255 | match KEY { 256 | VirtualKeyCode::Escape => { return (ItemMenuResult::Cancel, 0) } 257 | _ => { 258 | let selection = rltk::letter_to_option(KEY); 259 | if selection > -1 && selection < gs.player().inventory.equipped.len() as i32 { 260 | return (ItemMenuResult::Selected, selection); 261 | } 262 | return (ItemMenuResult::NoResponse, 0); 263 | } 264 | } 265 | } 266 | } 267 | 268 | (ItemMenuResult::NoResponse, 0) 269 | } 270 | 271 | pub fn display_game_over_and_handle_quit(ctx : &mut Rltk, gs : &mut State) { 272 | ctx.cls(); 273 | ctx.print_color(33, 25, RGB::named(rltk::RED), RGB::named(rltk::BLACK), &"You are dead.".to_string()); 274 | ctx.print_color(28, 27, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), &"Press any key for the menu.".to_string()); 275 | if let Some(_) = ctx.key { gs.game_state = TickType::MainMenu } 276 | } 277 | 278 | #[allow(non_snake_case)] 279 | pub fn handle_item_targeting(gs : &mut State, ctx: &mut Rltk, title: S) -> ItemMenuResult { 280 | ctx.print_color(0,0, RGB::named(rltk::YELLOW), RGB::named(rltk::RED), &title.to_string()); 281 | let mouse_tuple = ctx.mouse_pos(); 282 | let mouse_pos = Point::new(mouse_tuple.0, mouse_tuple.1); 283 | let previous_mouse = gs.prev_mouse_for_targeting; 284 | 285 | if mouse_pos != previous_mouse && mouse_pos.x > 0 && mouse_pos.x < 79 && mouse_pos.y > 0 && mouse_pos.y < 40 { gs.target_cell = mouse_pos; } 286 | 287 | if gs.target_cell.x < 1 { gs.target_cell.x = 1; } 288 | if gs.target_cell.x > 79 { gs.target_cell.x = 79; } 289 | if gs.target_cell.y < 1 { gs.target_cell.y = 1; } 290 | if gs.target_cell.y > 39 { gs.target_cell.y = 39; } 291 | 292 | let possible = gs.map.is_tile_visible(gs.target_cell); 293 | 294 | if possible { 295 | ctx.set_bg(gs.target_cell.x, gs.target_cell.y, RGB::named(rltk::RED)); 296 | if ctx.left_click { 297 | return ItemMenuResult::Selected; 298 | } 299 | } 300 | 301 | match ctx.key { 302 | None => {} 303 | Some(KEY) => { 304 | match KEY { 305 | VirtualKeyCode::Escape => { return ItemMenuResult::Cancel } 306 | VirtualKeyCode::Return => { if possible { return ItemMenuResult::Selected } } 307 | VirtualKeyCode::Space => { if possible { return ItemMenuResult::Selected } } 308 | VirtualKeyCode::Left => { gs.target_cell.x = max(gs.target_cell.x-1, 1) } 309 | VirtualKeyCode::Right => { gs.target_cell.x = min(gs.target_cell.x+1, 79) } 310 | VirtualKeyCode::Up => { gs.target_cell.y = max(gs.target_cell.y-1, 1) } 311 | VirtualKeyCode::Down => { gs.target_cell.y = min(gs.target_cell.y+1, 40) } 312 | VirtualKeyCode::Numpad4 => { gs.target_cell.x = max(gs.target_cell.x-1, 1) } 313 | VirtualKeyCode::Numpad6 => { gs.target_cell.x = min(gs.target_cell.x+1, 79) } 314 | VirtualKeyCode::Numpad8 => { gs.target_cell.y = max(gs.target_cell.y-1, 1) } 315 | VirtualKeyCode::Numpad2 => { gs.target_cell.y = min(gs.target_cell.y+1, 40) } 316 | VirtualKeyCode::Numpad7 => { gs.target_cell = Point::new( max(gs.target_cell.x-1, 1), max(gs.target_cell.y-1, 1) ) } 317 | VirtualKeyCode::Numpad9 => { gs.target_cell = Point::new( min(gs.target_cell.x+1, 79), max(gs.target_cell.y-1, 1) ) } 318 | VirtualKeyCode::Numpad1 => { gs.target_cell = Point::new( max(gs.target_cell.x-1, 1), min(gs.target_cell.y+1, 40) ) } 319 | VirtualKeyCode::Numpad3 => { gs.target_cell = Point::new( min(gs.target_cell.x+1, 79), min(gs.target_cell.y+1, 40) ) } 320 | _ => { } 321 | } 322 | } 323 | } 324 | 325 | ItemMenuResult::NoResponse 326 | } 327 | 328 | const STORY_TYPES : & [& str] = &["Tales", "Sagas", "Adventures", "Anecdotes", "Fables", "Narratives"]; 329 | const STORY_NOUNS : & [& str] = &["Heroism", "Cowardice", "Vengeance", "Heroism", "Exploration", "Delving", "Dungeoneering"]; 330 | 331 | #[derive(Serialize, Deserialize)] 332 | pub struct MenuState { 333 | random : Vec, 334 | save_exists : bool, 335 | current_menu_option : i32, 336 | backdrop : Vec<(u8, f32)> 337 | } 338 | 339 | impl MenuState { 340 | pub fn new() -> MenuState { 341 | let mut rng = rand::thread_rng(); 342 | let save_exists = Path::new("./savegame.json").exists(); 343 | let mut cmo = 1; 344 | if save_exists { cmo = 0; } 345 | 346 | let mut bd : Vec<(u8, f32)> = Vec::new(); 347 | for _i in 0..(80*50) { 348 | let bg_i = rng.gen_range(0, 192); 349 | let bg : f32 = bg_i as f32 / 255.0; 350 | bd.push((rng.gen_range(32, 62) as u8, bg)); 351 | } 352 | 353 | MenuState{ 354 | random: vec![rng.gen_range(0, 6), rng.gen_range(0, 7), rng.gen_range(0, 7)], 355 | save_exists, 356 | current_menu_option : cmo, 357 | backdrop : bd 358 | } 359 | } 360 | } 361 | 362 | pub enum MainMenuResult { None, Continue, New, Quit } 363 | 364 | #[allow(non_snake_case)] 365 | pub fn display_main_menu(ctx : &mut Rltk, ms : &mut MenuState) -> MainMenuResult { 366 | let mut rng = rand::thread_rng(); 367 | ctx.cls(); 368 | 369 | // Backdrop 370 | for y in 0..50 { 371 | for x in 0..80 { 372 | let idx = (y*80)+x; 373 | ctx.set(x, y, RGB::from_f32(0.0, ms.backdrop[idx as usize].1, 0.0), RGB::named(rltk::BLACK), ms.backdrop[idx as usize].0); 374 | } 375 | } 376 | 377 | for x in 0..80 { 378 | for y in (1..50).rev() { 379 | let idx = (y * 80) + x; 380 | let above_idx = ((y-1) * 80) + x; 381 | ms.backdrop[idx] = ms.backdrop[above_idx]; 382 | ms.backdrop[idx].1 -= 0.02; 383 | if ms.backdrop[idx].1 < 0.0 { 384 | let bg_i = rng.gen_range(0, 192); 385 | let bg : f32 = bg_i as f32 / 255.0; 386 | ms.backdrop[idx] = (rng.gen_range(32, 62) as u8, bg); 387 | } 388 | } 389 | let y = 0; 390 | let idx = (y * 80) + x; 391 | let bg_i = rng.gen_range(0, 192); 392 | let bg : f32 = bg_i as f32 / 255.0; 393 | ms.backdrop[idx] = (rng.gen_range(32, 62) as u8, bg); 394 | } 395 | 396 | // Header 397 | ctx.draw_box(15, 8, 50, 11, RGB::named(rltk::GREEN), RGB::named(rltk::BLACK)); 398 | ctx.print_color_centered(10, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "Rusty Roguelike v1.0"); 399 | ctx.print_color_centered(12, RGB::named(rltk::RED), RGB::named(rltk::BLACK), &format!("{} in {} and {}", STORY_TYPES[ms.random[0]], STORY_NOUNS[ms.random[1]], STORY_NOUNS[ms.random[2]])); 400 | 401 | // Menu render 402 | let mut y = 15; 403 | if ms.save_exists { 404 | if ms.current_menu_option == 0 { 405 | ctx.print_color_centered(y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "(C)ontinue Saved Game"); 406 | } else { 407 | ctx.print_color_centered(y, RGB::named(rltk::GREY), RGB::named(rltk::BLACK), "(C)ontinue Saved Game"); 408 | } 409 | y += 1; 410 | } 411 | if ms.current_menu_option == 1 { 412 | ctx.print_color_centered(y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "(N)ew Game"); 413 | } else { 414 | ctx.print_color_centered(y, RGB::named(rltk::GREY), RGB::named(rltk::BLACK), "(N)ew Game"); 415 | } 416 | y += 1; 417 | if ms.current_menu_option == 2 { 418 | ctx.print_color_centered(y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "(Q)uit"); 419 | } else { 420 | ctx.print_color_centered(y, RGB::named(rltk::GREY), RGB::named(rltk::BLACK), "(Q)uit"); 421 | } 422 | 423 | // Copyright blurb 424 | ctx.print_color_centered(42, RGB::named(rltk::GREY), RGB::named(rltk::BLACK), "/r/roguelikedev Roguelike Tutorial Series"); 425 | ctx.print_color_centered(43, RGB::named(rltk::GREY), RGB::named(rltk::BLACK), "https://github.com/thebracket/rustyroguelike"); 426 | ctx.print_color_centered(44, RGB::named(rltk::GREY), RGB::named(rltk::BLACK), "(c) 2019 Bracket Productions"); 427 | 428 | // Keyboard input 429 | match ctx.key { 430 | None => {} 431 | Some(KEY) => { 432 | match KEY { 433 | VirtualKeyCode::Escape => { return MainMenuResult::Quit } 434 | VirtualKeyCode::Q => { return MainMenuResult::Quit } 435 | VirtualKeyCode::N => { return MainMenuResult::New } 436 | VirtualKeyCode::C => { if ms.save_exists { return MainMenuResult::Continue } } 437 | VirtualKeyCode::Up => { 438 | ms.current_menu_option -= 1; 439 | if ms.save_exists && ms.current_menu_option < 0 { ms.current_menu_option = 2 } 440 | if (!ms.save_exists) && ms.current_menu_option < 1 { ms.current_menu_option = 1 } 441 | } 442 | VirtualKeyCode::Down => { 443 | ms.current_menu_option += 1; 444 | if ms.save_exists && ms.current_menu_option > 2 { ms.current_menu_option = 0 } 445 | if (!ms.save_exists) && ms.current_menu_option > 2 { ms.current_menu_option = 1 } 446 | } 447 | VirtualKeyCode::Return => { 448 | match ms.current_menu_option { 449 | 0 => { return MainMenuResult::Continue } 450 | 1 => { return MainMenuResult::New } 451 | 2 => { return MainMenuResult::Quit } 452 | _ => {} 453 | } 454 | } 455 | _ => {} 456 | } 457 | } 458 | } 459 | 460 | MainMenuResult::None 461 | } 462 | 463 | #[allow(non_snake_case)] 464 | pub fn handle_level_up(ctx : &mut Rltk, gs : &mut State) { 465 | 466 | ctx.draw_box(10, 8, 60, 18, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); 467 | ctx.print_color_centered(10, RGB::named(rltk::WHITE), RGB::named(rltk::RED), &format!("Congratulations, you are now level {}!", gs.player().level)); 468 | ctx.print_color_centered(12, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "Your experience has improved your battle prowess."); 469 | ctx.print_color_centered(13, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "Select one of the following to improve:"); 470 | ctx.print_color_centered(15, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "(A) Give me more hit points."); 471 | ctx.print_color_centered(16, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "(B) I'd like to do more damage."); 472 | 473 | // Keyboard input 474 | match ctx.key { 475 | None => {} 476 | Some(KEY) => { 477 | match KEY { 478 | VirtualKeyCode::A => { 479 | gs.player_mut().fighter.max_hp += 10; 480 | gs.player_mut().fighter.hp = gs.player().fighter.max_hp; 481 | gs.game_state = TickType::PlayersTurn; 482 | } 483 | VirtualKeyCode::B => { 484 | gs.player_mut().fighter.power += 1; 485 | gs.game_state = TickType::PlayersTurn; 486 | } 487 | _ => {} 488 | } 489 | } 490 | } 491 | } 492 | 493 | #[allow(non_snake_case)] 494 | pub fn display_character_info(ctx : &mut Rltk, gs : &mut State) { 495 | let player = gs.player(); 496 | ctx.draw_box(10, 8, 60, 16, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); 497 | ctx.print_color_centered(10, RGB::named(rltk::WHITE), RGB::named(rltk::RED), "Character Information"); 498 | ctx.print_color_centered(12, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "You are not dead yet. That's something."); 499 | ctx.print_color_centered(13, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), &format!("You have beaten {} dungeon levels.", player.dungeon_level)); 500 | ctx.print_color_centered(14, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), &format!("You have {} experience points, needing {} to level.", player.xp, player.xp_to_level())); 501 | ctx.print_color_centered(15, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), &format!("You are level {}.", player.level)); 502 | ctx.print_color_centered(16, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), &format!("You have {} hit points, out of {}.", player.fighter.hp, player.fighter.max_hp)); 503 | ctx.print_color_centered(17, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), &format!("Your hit power is {}.", player.fighter.power)); 504 | ctx.print_color_centered(18, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), &format!("Your defense power is {}.", player.fighter.defense)); 505 | 506 | ctx.print_color_centered(20, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "Press any key to resume dungeon bashing!"); 507 | 508 | match ctx.key { 509 | None => {} 510 | Some(_) => { 511 | gs.game_state = TickType::PlayersTurn; 512 | } 513 | } 514 | } 515 | 516 | #[allow(non_snake_case)] 517 | pub fn display_help_info(ctx : &mut Rltk, gs : &mut State) { 518 | ctx.draw_box(10, 8, 60, 17, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); 519 | ctx.print_color_centered(10, RGB::named(rltk::WHITE), RGB::named(rltk::RED), "Controls"); 520 | ctx.print_color_centered(12, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "Arrow keys or NumPad keys to move."); 521 | ctx.print_color_centered(13, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "Walk into a monster to attack it."); 522 | ctx.print_color_centered(14, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "NumPad 5, or W to Wait."); 523 | ctx.print_color_centered(15, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "G to Get an item from the ground."); 524 | ctx.print_color_centered(16, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "U to Use an item from your inventory."); 525 | ctx.print_color_centered(17, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "E to Equip an item from your inventory."); 526 | ctx.print_color_centered(17, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "R to Remove an item you are using."); 527 | ctx.print_color_centered(17, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "D to Drop an item from your inventory."); 528 | ctx.print_color_centered(18, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "> to go down stairs, if you are standing on them."); 529 | ctx.print_color_centered(19, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "C for Character Info."); 530 | ctx.print_color_centered(20, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "? for this help menu. You've found this one."); 531 | ctx.print_color_centered(21, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "ESCAPE to save the game and quit to the menu."); 532 | 533 | ctx.print_color_centered(23, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "Press any key to resume dungeon bashing!"); 534 | 535 | match ctx.key { 536 | None => {} 537 | Some(_) => { 538 | gs.game_state = TickType::PlayersTurn; 539 | } 540 | } 541 | } -------------------------------------------------------------------------------- /src/game/inventory.rs: -------------------------------------------------------------------------------- 1 | use super::{Item, gui, gui::ItemMenuResult, State, TickType, BaseEntity, player}; 2 | use crate::rltk; 3 | use rltk::Rltk; 4 | extern crate serde; 5 | use serde::{Serialize, Deserialize}; 6 | 7 | #[derive(Serialize, Deserialize, Clone)] 8 | pub struct Inventory { 9 | pub items : Vec, 10 | pub equipped : Vec, 11 | capacity: i32 12 | } 13 | 14 | impl Inventory { 15 | pub fn new(capacity : i32) -> Inventory { 16 | Inventory{ items: Vec::new(), capacity, equipped: Vec::new() } 17 | } 18 | 19 | pub fn add_item(&mut self, item : Item) -> Vec { 20 | let mut result : Vec = Vec::new(); 21 | if self.items.len() as i32 > self.capacity { 22 | result.push("You cannot carry any more!".to_string()); 23 | } else { 24 | result.push(format!("You pick up the {}", item.name)); 25 | self.items.push(item); 26 | } 27 | result 28 | } 29 | 30 | pub fn remove_item_return_clone(&mut self, item_index: i32) -> Item { 31 | let item_copy = self.items[item_index as usize].clone(); 32 | self.items.remove(item_index as usize); 33 | item_copy 34 | } 35 | 36 | pub fn get_equippable_items(&self) -> Vec { 37 | let mut result = Vec::new(); 38 | for (i,item) in self.items.iter().enumerate() { 39 | match item.equippable { 40 | None => {} 41 | Some(_) => { result.push(i as i32); } 42 | } 43 | } 44 | result 45 | } 46 | } 47 | 48 | pub fn pickup(gs : &mut State) { 49 | let mut item_index = 0; 50 | let ppos = gs.player().position; 51 | for (i,e) in gs.entities.iter_mut().enumerate() { 52 | if e.can_pickup() && e.get_position() == ppos { 53 | // We can do it! 54 | item_index = i; 55 | } 56 | } 57 | 58 | if item_index > 0 { 59 | let cloned_item = gs.entities[item_index].as_item().unwrap().clone(); 60 | let results = gs.player_mut().inventory.add_item(cloned_item); 61 | gs.entities.remove(item_index); 62 | for s in results.iter() { 63 | gs.add_log_entry(s.clone()); 64 | } 65 | } else { 66 | gs.add_log_entry("There is nothing to pick up.".to_string()); 67 | } 68 | } 69 | 70 | pub fn use_item(gs : &mut State, ctx : &mut Rltk) { 71 | let (result, selection) = gui::handle_item_menu(gs, ctx, "Use which item? (or ESC)"); 72 | match result { 73 | ItemMenuResult::NoResponse => {} 74 | ItemMenuResult::Selected => { 75 | let result = player::use_item(selection, gs); 76 | for s in result.iter() { 77 | gs.add_log_entry(s.to_string()); 78 | } 79 | } 80 | ItemMenuResult::Cancel => { gs.game_state = TickType::PlayersTurn } 81 | } 82 | } 83 | 84 | pub fn drop_item(gs : &mut State, ctx : &mut Rltk) { 85 | let (result, selection) = gui::handle_item_menu(gs, ctx, "Drop which item? (or ESC)"); 86 | match result { 87 | ItemMenuResult::NoResponse => {} 88 | ItemMenuResult::Selected => { 89 | let mut item_copy = gs.player_mut().inventory.remove_item_return_clone(selection); 90 | item_copy.position = gs.player().get_position(); 91 | gs.add_log_entry(format!("You drop the {}", item_copy.name)); 92 | gs.entities.push(Box::new(item_copy)); 93 | gs.game_state = TickType::EnemyTurn; 94 | } 95 | ItemMenuResult::Cancel => { gs.game_state = TickType::PlayersTurn } 96 | } 97 | } 98 | 99 | pub fn wield_item(gs : &mut State, ctx : &mut Rltk) { 100 | let (result, selection) = gui::handle_equippable_menu(gs, ctx, "Wield which item? (or ESC)"); 101 | match result { 102 | ItemMenuResult::NoResponse => {} 103 | ItemMenuResult::Selected => { 104 | let result = wield_item_final(selection, gs); 105 | for s in result.iter() { 106 | gs.add_log_entry(s.to_string()); 107 | } 108 | } 109 | ItemMenuResult::Cancel => { gs.game_state = TickType::PlayersTurn } 110 | } 111 | } 112 | 113 | fn wield_item_final(item_index : i32, gs : &mut State) -> Vec { 114 | let mut result = Vec::new(); 115 | 116 | let slot = gs.player().inventory.items[item_index as usize].equippable.unwrap().slot; 117 | 118 | // Do we already have anything in that slot? If so, move it to the inventory 119 | let mut already_equipped : Vec = Vec::new(); 120 | for equipped in gs.player().inventory.equipped.iter() { 121 | if equipped.equippable.unwrap().slot == slot { 122 | result.push(format!("You unequip the {}", equipped.name)); 123 | already_equipped.push(equipped.clone()); 124 | } 125 | } 126 | gs.player_mut().inventory.equipped.retain(|a| a.equippable.unwrap().slot != slot); 127 | for item in already_equipped { 128 | gs.player_mut().inventory.items.push(item); 129 | } 130 | 131 | // Put the item in the equip list and remove it from the backpack 132 | let item = gs.player_mut().inventory.items[item_index as usize].clone(); 133 | result.push(format!("You equip the {}", item.name)); 134 | gs.player_mut().inventory.items.remove(item_index as usize); 135 | gs.player_mut().inventory.equipped.push(item); 136 | gs.game_state = TickType::EnemyTurn; 137 | 138 | result 139 | } 140 | 141 | pub fn unequip_item(gs : &mut State, ctx : &mut Rltk) { 142 | let (result, selection) = gui::handle_equipped_menu(gs, ctx, "Unequip which item? (or ESC)"); 143 | match result { 144 | ItemMenuResult::NoResponse => {} 145 | ItemMenuResult::Selected => { 146 | let result = unequip_item_final(selection, gs); 147 | for s in result.iter() { 148 | gs.add_log_entry(s.to_string()); 149 | } 150 | } 151 | ItemMenuResult::Cancel => { gs.game_state = TickType::PlayersTurn } 152 | } 153 | } 154 | 155 | fn unequip_item_final(item_index : i32, gs : &mut State) -> Vec { 156 | let mut result = Vec::new(); 157 | 158 | let item = gs.player().inventory.equipped[item_index as usize].clone(); 159 | result.push(format!("You remove the {}", item.name)); 160 | gs.player_mut().inventory.equipped.remove(item_index as usize); 161 | gs.player_mut().inventory.items.push(item); 162 | gs.game_state = TickType::EnemyTurn; 163 | 164 | result 165 | } 166 | 167 | pub fn item_targeting(gs : &mut State, ctx : &mut Rltk) { 168 | let result = gui::handle_item_targeting(gs, ctx, "Select your target with cursor keys or mouse, Escape to cancel."); 169 | match result { 170 | ItemMenuResult::NoResponse => {} 171 | ItemMenuResult::Cancel => { gs.game_state = TickType::PlayersTurn } 172 | ItemMenuResult::Selected => { player::use_area_item(gs); } 173 | } 174 | } -------------------------------------------------------------------------------- /src/game/item.rs: -------------------------------------------------------------------------------- 1 | use crate::rltk; 2 | use rltk::{RGB, Point}; 3 | use super::{BaseEntity, Map, random_choice}; 4 | extern crate serde; 5 | use serde::{Serialize, Deserialize}; 6 | 7 | #[derive(PartialEq, Clone, Copy, Serialize, Deserialize)] 8 | pub enum ItemType { HealthPotion, ZapScroll, FireballScroll, ConfusionScroll, Sword, Shield } 9 | 10 | #[derive(PartialEq, Clone, Copy, Serialize, Deserialize)] 11 | pub enum ItemSlot { MainHand, OffHand } 12 | 13 | #[derive(PartialEq, Clone, Copy, Serialize, Deserialize)] 14 | pub struct Equippable { 15 | pub slot : ItemSlot, 16 | pub power_bonus : i32, 17 | pub defense_bonus : i32 18 | } 19 | 20 | #[derive(PartialEq, Clone, Serialize, Deserialize)] 21 | pub struct Item { 22 | pub position : Point, 23 | pub glyph: u8, 24 | pub fg : RGB, 25 | pub name : String, 26 | pub item_type : ItemType, 27 | pub requires_targeting_mode : bool, 28 | pub equippable : Option 29 | } 30 | 31 | impl Item { 32 | pub fn new_random(x:i32, y:i32) -> Item { 33 | let choice = random_choice(vec![ 34 | ("Health".to_string(), 45), 35 | ("Zap".to_string(), 10), 36 | ("Fireball".to_string(), 10), 37 | ("Confusion".to_string(), 10), 38 | ("Sword".to_string(), 10), 39 | ("Shield".to_string(), 10), 40 | ("Dagger".to_string(), 5), 41 | ]); 42 | 43 | if choice == "Health" { Item::new_health_potion(x,y) } 44 | else if choice == "Zap" { Item::new_zap_scroll(x,y) } 45 | else if choice == "Fireball" { Item::new_fireball_scroll(x,y) } 46 | else if choice == "Sword" { Item::new_sword(x,y) } 47 | else if choice == "Shield" { Item::new_shield(x,y) } 48 | else if choice == "Dagger" { Item::new_dagger(x,y) } 49 | else { Item::new_confusion_scroll(x,y) } 50 | } 51 | 52 | pub fn new_health_potion(x:i32, y:i32) -> Item { 53 | Item{ 54 | position: Point::new(x, y), 55 | glyph: 173, 56 | fg: RGB::named(rltk::MAGENTA), 57 | name: "Health Potion".to_string(), 58 | item_type: ItemType::HealthPotion, 59 | requires_targeting_mode : false, 60 | equippable: None 61 | } 62 | } 63 | 64 | pub fn new_zap_scroll(x:i32, y:i32) -> Item { 65 | Item{ 66 | position: Point::new(x, y), 67 | glyph: 63, 68 | fg: RGB::named(rltk::CYAN), 69 | name: "Zap Scroll".to_string(), 70 | item_type: ItemType::ZapScroll, 71 | requires_targeting_mode : false, 72 | equippable: None 73 | } 74 | } 75 | 76 | pub fn new_fireball_scroll(x:i32, y:i32) -> Item { 77 | Item{ 78 | position: Point::new(x, y), 79 | glyph: 63, 80 | fg: RGB::named(rltk::ORANGE), 81 | name: "Fireball Scroll".to_string(), 82 | item_type: ItemType::FireballScroll, 83 | requires_targeting_mode : true, 84 | equippable: None 85 | } 86 | } 87 | 88 | pub fn new_confusion_scroll(x:i32, y:i32) -> Item { 89 | Item{ 90 | position: Point::new(x, y), 91 | glyph: 63, 92 | fg: RGB::named(rltk::BLUE), 93 | name: "Confusion Scroll".to_string(), 94 | item_type: ItemType::ConfusionScroll, 95 | requires_targeting_mode : false, 96 | equippable: None 97 | } 98 | } 99 | 100 | pub fn new_sword(x:i32, y:i32) -> Item { 101 | Item{ 102 | position: Point::new(x, y), 103 | glyph: 47, 104 | fg: RGB::named(rltk::CYAN), 105 | name: "Sword".to_string(), 106 | item_type: ItemType::Sword, 107 | requires_targeting_mode : false, 108 | equippable: Some(Equippable{ slot : ItemSlot::MainHand, power_bonus: 1, defense_bonus: 0 }) 109 | } 110 | } 111 | 112 | pub fn new_dagger(x:i32, y:i32) -> Item { 113 | Item{ 114 | position: Point::new(x, y), 115 | glyph: 47, 116 | fg: RGB::named(rltk::GREEN), 117 | name: "Dagger".to_string(), 118 | item_type: ItemType::Sword, 119 | requires_targeting_mode : false, 120 | equippable: Some(Equippable{ slot : ItemSlot::MainHand, power_bonus: 2, defense_bonus: 0 }) 121 | } 122 | } 123 | 124 | pub fn new_shield(x:i32, y:i32) -> Item { 125 | Item{ 126 | position: Point::new(x, y), 127 | glyph: 93, 128 | fg: RGB::named(rltk::BROWN1), 129 | name: "Shield".to_string(), 130 | item_type: ItemType::Shield, 131 | requires_targeting_mode : false, 132 | equippable: Some(Equippable{ slot : ItemSlot::OffHand, power_bonus: 0, defense_bonus: 1 }) 133 | } 134 | } 135 | } 136 | 137 | #[typetag::serde(name = "BEItem")] 138 | impl BaseEntity for Item { 139 | fn get_position(&self) -> Point { self.position } 140 | fn get_fg_color(&self) -> RGB { self.fg } 141 | fn get_glyph(&self) -> u8 { self.glyph } 142 | fn plot_visibility(&mut self, _map : &Map) {} 143 | fn get_tooltip_text(&self) -> String { format!("Item: {}", self.name) } 144 | fn get_name(&self) -> String { self.name.clone() } 145 | fn can_pickup(&self) -> bool { true } 146 | fn as_item(&self) -> Option<&Item> { Some(self) } 147 | } -------------------------------------------------------------------------------- /src/game/item_effects.rs: -------------------------------------------------------------------------------- 1 | use super::{State, BaseEntity, TickType, Combat, Particle}; 2 | use crate::rltk; 3 | use rltk::{RGB}; 4 | 5 | pub fn use_health_potion(item_index : i32, gs : &mut State, result : &mut Vec) { 6 | let player = &mut gs.player_mut(); 7 | if player.fighter.hp == player.fighter.max_hp { 8 | result.push("You are already at maximum health.".to_string()); 9 | } else { 10 | player.fighter.hp = player.fighter.max_hp; // Cheezed due to confusion over borrowing 11 | result.push("You are healed!".to_string()); 12 | player.inventory.remove_item_return_clone(item_index); 13 | } 14 | } 15 | 16 | pub fn use_zap_scroll(item_index : i32, gs : &mut State, result : &mut Vec) { 17 | let mut possible_targets : Vec<(usize, f32)> = Vec::new(); 18 | let visible_tiles = gs.player().visible_tiles.clone(); 19 | let my_pos = gs.player().get_position(); 20 | for (i,potential_target) in gs.entities.iter().enumerate() { 21 | if potential_target.is_mob() { 22 | let target_pos = potential_target.get_position(); 23 | if visible_tiles.contains(&target_pos) { 24 | possible_targets.push((i, rltk::DistanceAlg::Pythagoras.distance2d(my_pos, target_pos))); 25 | } 26 | } 27 | } 28 | 29 | if possible_targets.is_empty() { 30 | result.push("You can't see anyone to zap, so you put the scroll away.".to_string()); 31 | } else { 32 | possible_targets.sort_by(|a,b| a.1.partial_cmp(&b.1).unwrap()); 33 | 34 | let target = &mut gs.entities[possible_targets[0].0].as_mob_mut().unwrap(); 35 | 36 | let tp = target.get_position(); 37 | let line = rltk::line2d(rltk::LineAlg::Bresenham, tp, my_pos); 38 | for zap in line { 39 | gs.vfx.push(Particle::new(zap, RGB::named(rltk::CYAN), RGB::named(rltk::BLACK), 15, 200.0)); 40 | } 41 | 42 | result.push(format!("Lightning from the scroll zaps {} for 8 points of damage.", target.name)); 43 | target.take_damage(8); 44 | if target.fighter.hp < 1 { 45 | target.kill(); 46 | result.push(format!("{} is burned to a crisp.", target.name)); 47 | gs.player_mut().xp += target.fighter.xp_value; 48 | } 49 | gs.entities.retain(|e| !e.is_dead()); 50 | 51 | // Remove the scroll 52 | gs.player_mut().inventory.remove_item_return_clone(item_index); 53 | } 54 | } 55 | 56 | pub fn use_fireball_scroll(gs : &mut State, result : &mut Vec) { 57 | result.push("You launch a fireball!".to_string()); 58 | 59 | let target = gs.target_cell; 60 | let item_index = gs.targeting_item; 61 | 62 | let area_of_effect = rltk::field_of_view(target, 3, &gs.map); 63 | for pos in area_of_effect.iter() { 64 | gs.vfx.push(Particle::new(*pos, RGB::named(rltk::RED), RGB::named(rltk::YELLOW), 176, 200.0)); 65 | } 66 | let mut targets : Vec = Vec::new(); 67 | for (i,e) in gs.entities.iter().enumerate() { 68 | if area_of_effect.contains(&e.get_position()) && e.can_be_attacked() { targets.push(i); } 69 | } 70 | 71 | for target_id in targets { 72 | let target = gs.entities[target_id].as_combat(); 73 | match target { 74 | None => {} 75 | Some(target) => { 76 | result.push(format!("{} is burned for 8 points of damage.", target.get_name())); 77 | target.take_damage(8); 78 | if target.get_hp() < 1 { 79 | result.push(format!("{} is dead.", target.get_name())); 80 | target.kill(); 81 | gs.player_mut().xp += target.xp_value(); 82 | } 83 | } 84 | } 85 | } 86 | 87 | gs.entities.retain(|e| !(e.is_dead() && !e.is_player())); 88 | 89 | // Remove the scroll 90 | gs.player_mut().inventory.remove_item_return_clone(item_index); 91 | gs.game_state = TickType::EnemyTurn; 92 | 93 | for r in result { 94 | gs.add_log_entry(r.to_string()); 95 | } 96 | } 97 | 98 | pub fn use_confusion_scroll(item_index : i32, gs : &mut State, result : &mut Vec) { 99 | let mut possible_targets : Vec<(usize, f32)> = Vec::new(); 100 | let visible_tiles = gs.player().visible_tiles.clone(); 101 | let my_pos = gs.player().get_position(); 102 | for (i,potential_target) in gs.entities.iter().enumerate() { 103 | if potential_target.is_mob() { 104 | let target_pos = potential_target.get_position(); 105 | if visible_tiles.contains(&target_pos) { 106 | possible_targets.push((i, rltk::DistanceAlg::Pythagoras.distance2d(my_pos, target_pos))); 107 | } 108 | } 109 | } 110 | 111 | if possible_targets.is_empty() { 112 | result.push("You can't see anyone to zap, so you put the scroll away.".to_string()); 113 | } else { 114 | possible_targets.sort_by(|a,b| a.1.partial_cmp(&b.1).unwrap()); 115 | 116 | let mut target = &mut gs.entities[possible_targets[0].0].as_mob_mut().unwrap(); 117 | result.push(format!("{} is confused.", target.name)); 118 | target.confused = Some(5); 119 | 120 | // Remove the scroll 121 | gs.player_mut().inventory.remove_item_return_clone(item_index); 122 | } 123 | } -------------------------------------------------------------------------------- /src/game/map.rs: -------------------------------------------------------------------------------- 1 | use crate::rltk; 2 | use rltk::{ Point, Algorithm2D, BaseMap }; 3 | use super::TileType; 4 | extern crate serde; 5 | use serde::{Serialize, Deserialize}; 6 | 7 | #[derive(Serialize, Deserialize)] 8 | pub struct Map { 9 | pub tiles : Vec, 10 | pub visible : Vec, 11 | pub revealed : Vec, 12 | pub width: i32, 13 | pub height: i32, 14 | pub blocked : Vec 15 | } 16 | 17 | impl Map { 18 | pub fn new(w:i32, h:i32) -> Map { 19 | let mut visible = Vec::new(); 20 | let mut blank_map = Vec::new(); 21 | let mut revealed = Vec::new(); 22 | let mut blocked = Vec::new(); 23 | for _i in 0 .. (w*h) { 24 | blank_map.push(TileType::Wall); 25 | visible.push(false); 26 | revealed.push(false); 27 | blocked.push(false); 28 | } 29 | 30 | Map{tiles : blank_map, visible, revealed, width: w, height: h, blocked } 31 | } 32 | 33 | pub fn set_visibility(&mut self, vis : &[Point]) { 34 | for v in self.visible.iter_mut() { 35 | *v = false; 36 | } 37 | 38 | for pt in vis { 39 | let idx = self.tile_idx(pt.x, pt.y); 40 | if let Some(x) = idx { self.visible[x] = true; self.revealed[x] = true; } 41 | } 42 | } 43 | 44 | // Utility function: find the index of a tile at x/y 45 | fn tile_idx(&self, x:i32, y:i32) -> Option { 46 | if self.valid_tile(x, y) { 47 | Some(((y*self.width)+x) as usize) 48 | } else { 49 | None 50 | } 51 | } 52 | 53 | // Utility function: bounds checking 54 | fn valid_tile(&self, x:i32, y:i32) -> bool { 55 | x > 0 && x < self.width-1 && y > 0 && y < self.height-1 56 | } 57 | 58 | // Utility function: is a tile walkable 59 | pub fn is_walkable(&self, x:i32, y:i32) -> bool { 60 | let idx = self.tile_idx(x, y); 61 | match idx { 62 | Some(idx) => { 63 | match self.tiles[idx] { 64 | TileType::Floor => { true } 65 | TileType::Wall => { false } 66 | TileType::Stairs => { true } 67 | } 68 | } 69 | 70 | None => { 71 | false 72 | } 73 | } 74 | } 75 | 76 | // Utility function: is a tile walkable 77 | pub fn is_transparent(&self, x:i32, y:i32) -> bool { 78 | let idx = self.tile_idx(x, y); 79 | match idx { 80 | Some(idx) => { 81 | match self.tiles[idx] { 82 | TileType::Floor => { false } 83 | TileType::Wall => { true } 84 | TileType::Stairs => { false } 85 | } 86 | } 87 | 88 | None => { 89 | false 90 | } 91 | } 92 | } 93 | 94 | pub fn is_tile_visible(&self, pos : Point) -> bool { 95 | let idx = self.tile_idx(pos.x, pos.y); 96 | match idx { 97 | None => { false } 98 | Some(x) => { self.visible[x] } 99 | } 100 | } 101 | 102 | pub fn tile_description(&self, pos : Point) -> String { 103 | let idx = self.tile_idx(pos.x, pos.y); 104 | match idx { 105 | None => { return "".to_string(); } 106 | Some(x) => { 107 | if self.visible[x] { 108 | match self.tiles[x] { 109 | TileType::Floor => { return "Floor".to_string() } 110 | TileType::Wall => { return "Wall".to_string() } 111 | TileType::Stairs => { return "Stairs".to_string() } 112 | } 113 | } 114 | } 115 | } 116 | "".to_string() 117 | } 118 | 119 | pub fn refresh_blocked(&mut self) { 120 | for y in 0..self.height { 121 | for x in 0..self.width { 122 | let idx = (y * self.width) + x; 123 | self.blocked[idx as usize] = !self.is_walkable(x, y); 124 | } 125 | } 126 | } 127 | 128 | pub fn set_tile_blocked(&mut self, idx : i32) { 129 | self.blocked[idx as usize] = true; 130 | } 131 | 132 | pub fn clear_tile_blocked(&mut self, idx : i32) { 133 | self.blocked[idx as usize] = false; 134 | } 135 | 136 | pub fn is_exit_valid(&self, x:i32, y:i32) -> bool { 137 | if x < 1 || x > self.width-1 || y < 1 || y > self.height-1 { return false; } 138 | let idx = (y * self.width) + x; 139 | !self.blocked[idx as usize] 140 | } 141 | 142 | pub fn is_tile_blocked(&self, idx: i32) -> bool { 143 | self.blocked[idx as usize] 144 | } 145 | } 146 | 147 | impl BaseMap for Map { 148 | fn is_opaque(&self, idx: i32) -> bool { 149 | self.is_transparent(idx % self.width, idx / self.width) 150 | } 151 | 152 | fn get_available_exits(&self, idx:i32) -> Vec<(i32, f32)> { 153 | let mut exits : Vec<(i32, f32)> = Vec::new(); 154 | let x = idx % self.width; 155 | let y = idx / self.width; 156 | 157 | // Cardinal directions 158 | if self.is_exit_valid(x-1, y) { exits.push((idx-1, 1.0)) }; 159 | if self.is_exit_valid(x+1, y) { exits.push((idx+1, 1.0)) }; 160 | if self.is_exit_valid(x, y-1) { exits.push((idx-self.width, 1.0)) }; 161 | if self.is_exit_valid(x, y+1) { exits.push((idx+self.width, 1.0)) }; 162 | 163 | // Diagonals 164 | if self.is_exit_valid(x-1, y-1) { exits.push(((idx-self.width)-1, 1.4)); } 165 | if self.is_exit_valid(x+1, y-1) { exits.push(((idx-self.width)+1, 1.4)); } 166 | if self.is_exit_valid(x-1, y+1) { exits.push(((idx+self.width)-1, 1.4)); } 167 | if self.is_exit_valid(x+1, y+1) { exits.push(((idx+self.width)+1, 1.4)); } 168 | 169 | exits 170 | } 171 | 172 | fn get_pathing_distance(&self, idx1:i32, idx2:i32) -> f32 { 173 | let p1 = Point::new(idx1 % self.width, idx1 / self.width); 174 | let p2 = Point::new(idx2 % self.width, idx2 / self.width); 175 | rltk::DistanceAlg::Pythagoras.distance2d(p1, p2) 176 | } 177 | } 178 | 179 | impl Algorithm2D for Map { 180 | fn point2d_to_index(&self, pt : Point) -> i32 { 181 | (pt.y * self.width) + pt.x 182 | } 183 | 184 | fn index_to_point2d(&self, idx:i32) -> Point { 185 | Point{ x: idx % self.width, y: idx / self.width } 186 | } 187 | } -------------------------------------------------------------------------------- /src/game/map_builder.rs: -------------------------------------------------------------------------------- 1 | use super::{ Map, Rect, TileType, Mob, Item }; 2 | use rand::Rng; 3 | use std::cmp::{max, min}; 4 | 5 | const ROOM_MAX_SIZE : i32 = 10; 6 | const ROOM_MIN_SIZE : i32 = 6; 7 | const MAX_ROOMS : i32 = 30; 8 | 9 | pub fn random_rooms_tut3(map : &mut Map) -> Vec { 10 | let mut rng = rand::thread_rng(); 11 | 12 | let mut rooms : Vec = Vec::new(); 13 | for _i in 1..MAX_ROOMS { 14 | let w = rng.gen_range(ROOM_MIN_SIZE, ROOM_MAX_SIZE); 15 | let h = rng.gen_range(ROOM_MIN_SIZE, ROOM_MAX_SIZE); 16 | let x = rng.gen_range(1, map.width - w - 1); 17 | let y = rng.gen_range(1, map.height - h - 1); 18 | 19 | let room_candidate = Rect::new(x, y, x+w, y+h); 20 | 21 | let mut collides = false; 22 | for room in rooms.iter() { 23 | if room_candidate.intersect(room) { 24 | collides = true; 25 | } 26 | } 27 | 28 | if !collides { 29 | apply_room(map, &room_candidate); 30 | 31 | if !rooms.is_empty() { 32 | let (new_x, new_y) = room_candidate.center(); 33 | let (prev_x, prev_y) = rooms[rooms.len()-1].center(); 34 | if rng.gen_range(0,1)==1 { 35 | apply_horizontal_tunnel(map, prev_x, new_x, prev_y); 36 | apply_vertical_tunnel(map, prev_y, new_y, new_x); 37 | } else { 38 | apply_vertical_tunnel(map, prev_y, new_y, prev_x); 39 | apply_horizontal_tunnel(map, prev_x, new_x, new_y); 40 | } 41 | } 42 | 43 | rooms.push(room_candidate); 44 | } 45 | } 46 | rooms 47 | } 48 | 49 | // Applies a rectangle room to the map 50 | fn apply_room(map : &mut Map, rect : &Rect) { 51 | for y in min(rect.y1, rect.y2) .. max(rect.y1, rect.y2) { 52 | for x in min(rect.x1, rect.x2) .. max(rect.x1, rect.x2) { 53 | let idx = (y * map.width) + x; 54 | if idx > 0 && idx < map.width*map.height { 55 | map.tiles[idx as usize] = TileType::Floor; 56 | } 57 | } 58 | } 59 | } 60 | 61 | fn apply_horizontal_tunnel(map: &mut Map, x1:i32, x2:i32, y:i32) { 62 | for x in min(x1,x2) ..= max(x1,x2) { 63 | let idx = (y * map.width) + x; 64 | if idx > 0 && idx < map.width*map.height { 65 | map.tiles[idx as usize] = TileType::Floor; 66 | } 67 | } 68 | } 69 | 70 | fn apply_vertical_tunnel(map: &mut Map, y1:i32, y2:i32, x:i32) { 71 | for y in min(y1,y2) ..= max(y1,y2) { 72 | let idx = (y * map.width) + x; 73 | if idx > 0 && idx < map.width*map.height { 74 | map.tiles[idx as usize] = TileType::Floor; 75 | } 76 | } 77 | } 78 | 79 | pub fn spawn_mobs(rooms: &[Rect], dungeon_level : i32) -> Vec { 80 | let mut rng = rand::thread_rng(); 81 | let mut mobs : Vec = Vec::new(); 82 | for i in 1 .. rooms.len() { 83 | let number_of_mobs = rng.gen_range(1, dungeon_level+3); 84 | if number_of_mobs > 0 { 85 | for _mobn in 1 .. number_of_mobs { 86 | let mob_x = rng.gen_range(rooms[i].x1+1, rooms[i].x2-1); 87 | let mob_y = rng.gen_range(rooms[i].y1+1, rooms[i].y2-1); 88 | 89 | let mut found = false; 90 | for existing_mob in mobs.iter() { 91 | if existing_mob.position.x == mob_x && existing_mob.position.y == mob_y { 92 | found = true; 93 | } 94 | } 95 | 96 | if !found { 97 | let mob = Mob::new_random(mob_x, mob_y); 98 | mobs.push(mob); 99 | } 100 | } 101 | } 102 | } 103 | mobs 104 | } 105 | 106 | pub fn spawn_items(rooms: &[Rect], mobs: &[Mob], dungeon_level : i32) -> Vec { 107 | let mut rng = rand::thread_rng(); 108 | let mut items : Vec = Vec::new(); 109 | 110 | for i in 1 .. rooms.len() { 111 | let number_of_items = rng.gen_range(1, dungeon_level+3); 112 | if number_of_items > 0 { 113 | for _itemn in 1 .. number_of_items { 114 | let item_x = rng.gen_range(rooms[i].x1+1, rooms[i].x2-1); 115 | let item_y = rng.gen_range(rooms[i].y1+1, rooms[i].y2-1); 116 | 117 | let mut found = false; 118 | for existing_mob in mobs.iter() { 119 | if existing_mob.position.x == item_x && existing_mob.position.y == item_y { 120 | found = true; 121 | } 122 | } 123 | 124 | if !found { 125 | let item = Item::new_random(item_x, item_y); 126 | items.push(item); 127 | } 128 | } 129 | } 130 | } 131 | 132 | items 133 | } -------------------------------------------------------------------------------- /src/game/mob.rs: -------------------------------------------------------------------------------- 1 | use crate::rltk; 2 | use rltk::{RGB, Point, Algorithm2D, a_star_search, field_of_view}; 3 | use super::{fighter::Fighter, Map, Combat, BaseEntity, State, attack, random_choice, Particle}; 4 | use rand::Rng; 5 | extern crate serde; 6 | use serde::{Serialize, Deserialize}; 7 | 8 | #[derive(Serialize, Deserialize)] 9 | pub struct Mob { 10 | pub position : Point, 11 | pub glyph: u8, 12 | pub fg : RGB, 13 | pub visible_tiles : Vec, 14 | pub name : String, 15 | pub fighter : Fighter, 16 | pub confused: Option 17 | } 18 | 19 | impl Mob { 20 | pub fn new_random(x:i32, y:i32) -> Mob { 21 | let choice = random_choice(vec![("Wight".to_string(), 10), ("Hound".to_string(), 45), ("Itereater".to_string(), 45)]); 22 | if choice == "Wight" { Mob::new_wight(x, y) } 23 | else if choice == "Hound" { Mob::new_hound(x, y) } 24 | else { Mob::new_iter(x, y) } 25 | } 26 | 27 | fn new_wight(x:i32, y:i32) -> Mob { 28 | Mob{ 29 | position: Point::new(x, y), 30 | glyph: 38, 31 | fg: RGB::named(rltk::RED), 32 | visible_tiles: Vec::new(), 33 | name: "Borrow Wight".to_string(), 34 | fighter: Fighter::new(2, 0, 1, 60), 35 | confused: None 36 | } 37 | } 38 | 39 | fn new_hound(x:i32, y:i32) -> Mob { 40 | Mob{ 41 | position: Point::new(x, y), 42 | glyph: 109, 43 | fg: RGB::named(rltk::RED), 44 | visible_tiles: Vec::new(), 45 | name: "Mut Hound".to_string(), 46 | fighter: Fighter::new(1, 0, 1, 30), 47 | confused: None 48 | } 49 | } 50 | 51 | fn new_iter(x:i32, y:i32) -> Mob { 52 | Mob{ 53 | position: Point::new(x, y), 54 | glyph: 105, 55 | fg: RGB::named(rltk::RED), 56 | visible_tiles: Vec::new(), 57 | name: "Itereater Beast".to_string(), 58 | fighter: Fighter::new(1, 0, 1, 30), 59 | confused: None 60 | } 61 | } 62 | 63 | pub fn turn_tick(&mut self, player_pos : Point, map : &mut Map) -> bool { 64 | if let Some(turns) = self.confused { 65 | let new_turns = turns-1; 66 | if new_turns == 0 { 67 | self.confused = None; 68 | } else { 69 | self.confused = Some(new_turns); 70 | } 71 | 72 | let mut rng = rand::thread_rng(); 73 | let delta_x = rng.gen_range(0, 3)-1; 74 | let delta_y = rng.gen_range(0, 3)-1; 75 | let new_loc = Point::new(self.position.x + delta_x, self.position.y + delta_y); 76 | if map.is_walkable(new_loc.x, new_loc.y) && !map.is_tile_blocked(map.point2d_to_index(new_loc)) { 77 | self.position = new_loc; 78 | } 79 | 80 | return false; 81 | } 82 | 83 | let can_see_player = self.visible_tiles.contains(&player_pos); 84 | 85 | if can_see_player { 86 | let distance = rltk::DistanceAlg::Pythagoras.distance2d(player_pos, self.position); 87 | if distance < 1.5 { 88 | return true; 89 | } else { 90 | self.path_to_player(player_pos, map); 91 | } 92 | } 93 | false 94 | } 95 | 96 | fn path_to_player(&mut self, player_pos : Point, map : &mut Map) { 97 | let path = a_star_search(map.point2d_to_index(self.position), map.point2d_to_index(player_pos), map); 98 | if path.success { 99 | let idx = path.steps[1]; 100 | if !map.is_tile_blocked(idx) { 101 | let old_idx = (self.position.y * map.width) + self.position.x; 102 | map.clear_tile_blocked(old_idx); 103 | map.set_tile_blocked(idx); 104 | self.position = map.index_to_point2d(idx); 105 | } 106 | } 107 | } 108 | } 109 | 110 | #[typetag::serde(name = "BEMob")] 111 | impl BaseEntity for Mob { 112 | fn get_position(&self) -> Point { self.position } 113 | fn get_fg_color(&self) -> RGB { self.fg } 114 | fn get_glyph(&self) -> u8 { self.glyph } 115 | fn as_combat(&mut self) -> Option<&mut Combat> { Some(self) } 116 | fn plot_visibility(&mut self, map : &Map) { 117 | self.visible_tiles = field_of_view(self.get_position(), 6, map); 118 | } 119 | fn get_tooltip_text(&self) -> String { format!("Enemy: {}", self.name) } 120 | fn blocks_tile(&self) -> bool { true } 121 | fn can_be_attacked(&self) -> bool { true } 122 | fn is_dead(&self) -> bool { self.fighter.dead } 123 | fn is_mob(&self) -> bool { true } 124 | fn as_mob_mut(&mut self) ->Option<&mut Mob> { Some(self) } 125 | fn get_name(&self) -> String { self.name.to_string() } 126 | } 127 | 128 | pub fn mob_tick(gs : &mut State) { 129 | // Build the master map of unavailable tiles 130 | gs.map.refresh_blocked(); 131 | for e in gs.entities.iter() { 132 | if e.blocks_tile() { 133 | let pos = e.get_position(); 134 | gs.map.set_tile_blocked(gs.map.point2d_to_index(pos)); 135 | } 136 | } 137 | 138 | let mut active_mobs : Vec = Vec::new(); 139 | for (i,e) in gs.entities.iter_mut().enumerate() { 140 | if e.is_mob() { active_mobs.push(i); } 141 | } 142 | 143 | let ppos = gs.player().position; 144 | let mut attacking_mobs : Vec = Vec::new(); 145 | 146 | for id in active_mobs { 147 | let mob = gs.entities[id].as_mob_mut().unwrap(); 148 | if mob.turn_tick(ppos, &mut gs.map) { 149 | attacking_mobs.push(id); 150 | } 151 | } 152 | 153 | let mut tmp : Vec = Vec::new(); 154 | for id in attacking_mobs { 155 | let attacker_name = gs.entities[id].get_name(); 156 | let attacker_power = gs.entities[id].as_combat().unwrap().get_power(); 157 | gs.vfx.push(Particle::new(gs.player().get_position(), RGB::named(rltk::RED), RGB::named(rltk::BLACK), 176, 200.0)); 158 | let (_xp, result) = attack(attacker_name, attacker_power, gs.player_as_combat()); 159 | for r in result { 160 | tmp.push(r); 161 | } 162 | } 163 | for s in tmp { 164 | gs.add_log_entry(s); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/game/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | use crate::rltk; 3 | use rltk::{ GameState }; 4 | 5 | extern crate serde; 6 | 7 | mod entity; 8 | pub use entity::BaseEntity; 9 | 10 | mod tiletype; 11 | pub use tiletype::TileType; 12 | 13 | mod ticktype; 14 | pub use ticktype::TickType; 15 | 16 | mod fighter; 17 | pub use fighter::Fighter; 18 | pub use fighter::Combat; 19 | pub use fighter::attack; 20 | 21 | mod player; 22 | pub use player::Player; 23 | 24 | mod mob; 25 | pub use mob::Mob; 26 | 27 | mod rect; 28 | pub use rect::Rect; 29 | 30 | mod map; 31 | pub use map::Map; 32 | 33 | mod item; 34 | use item::Item; 35 | use item::ItemType; 36 | 37 | mod inventory; 38 | use inventory::Inventory; 39 | 40 | mod item_effects; 41 | 42 | extern crate rand; 43 | 44 | mod map_builder; 45 | 46 | mod gui; 47 | 48 | mod gamestate; 49 | pub use gamestate::State; 50 | 51 | mod random; 52 | pub use random::random_choice; 53 | 54 | mod vfx; 55 | pub use vfx::Particle; 56 | -------------------------------------------------------------------------------- /src/game/player.rs: -------------------------------------------------------------------------------- 1 | use crate::rltk; 2 | use rltk::{RGB, Point, Rltk, field_of_view, Algorithm2D, VirtualKeyCode}; 3 | use super::{fighter::Fighter, Inventory, BaseEntity, Combat, Map, ItemType, State, attack, TickType, inventory, item_effects, TileType, Particle}; 4 | extern crate serde; 5 | use serde::{Serialize, Deserialize}; 6 | 7 | #[derive(Serialize, Deserialize, Clone)] 8 | pub struct Player { 9 | pub position : Point, 10 | pub glyph: u8, 11 | pub fg : RGB, 12 | pub visible_tiles : Vec, 13 | pub fighter : Fighter, 14 | pub inventory : Inventory, 15 | pub dungeon_level : i32, 16 | pub xp : i32, 17 | pub level : i32 18 | } 19 | 20 | impl Player { 21 | pub fn new(x:i32, y:i32, glyph:u8, fg : RGB) -> Player { 22 | Player{ 23 | position: Point::new(x, y), 24 | glyph, fg, 25 | visible_tiles: Vec::new(), 26 | fighter: Fighter::new(10, 0, 1, 0), 27 | inventory: Inventory::new(26), 28 | dungeon_level : 0, 29 | xp : 0, 30 | level : 1 31 | } 32 | } 33 | 34 | pub fn copy_from_other_player(&mut self, other : &Player) { 35 | self.glyph = other.glyph; 36 | self.fg = other.fg; 37 | self.fighter = other.fighter.clone(); 38 | self.inventory = other.inventory.clone(); 39 | self.dungeon_level = other.dungeon_level; 40 | self.fighter.hp = self.fighter.max_hp; 41 | // Not copying visible tiles or position, since this is intended for map transition 42 | } 43 | 44 | pub fn xp_to_level(&self) -> i32 { 45 | 200 + (self.level * 150) 46 | } 47 | } 48 | 49 | #[typetag::serde(name = "BEPlayer")] 50 | impl BaseEntity for Player { 51 | fn get_position(&self) -> Point { self.position } 52 | fn get_fg_color(&self) -> RGB { self.fg } 53 | fn get_glyph(&self) -> u8 { self.glyph } 54 | fn as_player(&self) -> Option<&Player> { Some(self) } 55 | fn as_player_mut(&mut self) -> Option<&mut Player> { Some(self) } 56 | fn as_combat(&mut self) -> Option<&mut Combat> { Some(self) } 57 | fn plot_visibility(&mut self, map : &Map) { 58 | self.visible_tiles = field_of_view(self.get_position(), 6, map); 59 | } 60 | fn get_tooltip_text(&self) -> String { "It's you!".to_string() } 61 | fn get_name(&self) -> String { "Player".to_string() } 62 | fn is_player(&self) -> bool { true } 63 | } 64 | 65 | // Handlers for gameplay 66 | 67 | #[derive(PartialEq)] 68 | pub enum PlayerTickResult { None, NextMap } 69 | 70 | pub fn player_tick(gs : &mut State, ctx : &mut Rltk) -> PlayerTickResult { 71 | let player_ro = gs.player(); 72 | if player_ro.xp > player_ro.xp_to_level() { 73 | let player_rw = gs.player_mut(); 74 | player_rw.level += 1; 75 | let new_level = player_rw.level; 76 | player_rw.fighter.hp = player_rw.fighter.max_hp; 77 | gs.add_log_entry(format!("You are now level {}! Your wounds heal.", new_level)); 78 | gs.game_state = TickType::LevelUpMenu; 79 | return PlayerTickResult::None; 80 | } 81 | 82 | let mut turn_ended = false; 83 | let mut attack_target : Option = None; 84 | 85 | if let Some(key) = ctx.key { 86 | match key { 87 | VirtualKeyCode::Escape => { gs.save(); gs.game_state = TickType::MainMenu; } 88 | 89 | // Numpad 90 | VirtualKeyCode::Numpad8 => { attack_target = move_player(gs, 0, -1); turn_ended = true; } 91 | VirtualKeyCode::Numpad4 => { attack_target = move_player(gs, -1, 0); turn_ended = true; } 92 | VirtualKeyCode::Numpad6 => { attack_target = move_player(gs, 1, 0); turn_ended = true; } 93 | VirtualKeyCode::Numpad2 => { attack_target = move_player(gs, 0, 1); turn_ended = true; } 94 | 95 | VirtualKeyCode::Numpad7 => { attack_target = move_player(gs, -1, -1); turn_ended = true; } 96 | VirtualKeyCode::Numpad9 => { attack_target = move_player(gs, 1, -1); turn_ended = true; } 97 | VirtualKeyCode::Numpad1 => { attack_target = move_player(gs, -1, 1); turn_ended = true; } 98 | VirtualKeyCode::Numpad3 => { attack_target = move_player(gs, 1, 1); turn_ended = true; } 99 | 100 | // Cursors 101 | VirtualKeyCode::Up => { attack_target = move_player(gs, 0, -1); turn_ended = true; } 102 | VirtualKeyCode::Down => { attack_target = move_player(gs, 0, 1); turn_ended = true; } 103 | VirtualKeyCode::Left => { attack_target = move_player(gs, -1, 0); turn_ended = true; } 104 | VirtualKeyCode::Right => { attack_target = move_player(gs, 1, 0); turn_ended = true; } 105 | 106 | // Wait 107 | VirtualKeyCode::Numpad5 => { turn_ended = true; } 108 | VirtualKeyCode::W => { turn_ended = true; } 109 | 110 | // Items 111 | VirtualKeyCode::G => { inventory::pickup(gs); turn_ended = true; } 112 | VirtualKeyCode::U => { use_menu(gs); } 113 | VirtualKeyCode::D => { drop_menu(gs); } 114 | VirtualKeyCode::E => { equip_menu(gs); } 115 | VirtualKeyCode::R => { unequip_menu(gs); } 116 | 117 | // Level Change 118 | VirtualKeyCode::Period => { 119 | if gs.map.tiles[gs.map.point2d_to_index(gs.player().position) as usize] == TileType::Stairs { 120 | return PlayerTickResult::NextMap; 121 | } else { 122 | gs.add_log_entry("You aren't on stairs".to_string()); 123 | } 124 | } 125 | 126 | // Character Info 127 | VirtualKeyCode::C => { gs.game_state = TickType::CharacterMenu; } 128 | VirtualKeyCode::Slash => { gs.game_state = TickType::HelpMenu; } 129 | _ => {} 130 | } 131 | } 132 | 133 | if let Some(target) = attack_target { 134 | gs.vfx.push(Particle::new(gs.entities[target].get_position(), RGB::named(rltk::RED), RGB::named(rltk::BLACK), 176, 200.0)); 135 | let player = gs.player_as_combat(); 136 | let (xp, result) = attack(player.get_name(), player.get_power(), gs.entities[target].as_combat().unwrap()); 137 | for s in result { 138 | gs.add_log_entry(s.to_string()); 139 | } 140 | gs.entities.retain(|e| !e.is_dead()); 141 | let p = gs.player_mut(); 142 | p.xp += xp; 143 | } 144 | 145 | if turn_ended { 146 | gs.update_visibility(); 147 | gs.game_state = TickType::EnemyTurn; 148 | } 149 | 150 | PlayerTickResult::None 151 | } 152 | 153 | // Returns the ID of the target if we're attacking 154 | fn move_player(gs : &mut State, delta_x : i32, delta_y: i32) -> Option { 155 | let mut result : Option = None; 156 | let new_x = gs.player().position.x + delta_x; 157 | let new_y = gs.player().position.y + delta_y; 158 | let mut can_move : bool = true; 159 | if new_x > 0 && new_x < 79 && new_y > 0 && new_y < 49 && gs.map.is_walkable(new_x, new_y) { 160 | 161 | // Lets see if we are bumping a mob 162 | let new_pos = Point::new(new_x, new_y); 163 | for (i,e) in gs.entities.iter_mut().enumerate() { 164 | if e.get_position() == new_pos && e.blocks_tile() { 165 | // Tile is indeed blocked 166 | can_move = false; 167 | if e.can_be_attacked() { 168 | // Attack it! 169 | result = Some(i); 170 | } 171 | } 172 | } 173 | 174 | if can_move { 175 | gs.player_mut().position.x = new_x; 176 | gs.player_mut().position.y = new_y; 177 | } 178 | } 179 | result 180 | } 181 | 182 | fn use_menu(gs : &mut State) { 183 | if gs.player().inventory.items.is_empty() { 184 | gs.add_log_entry("You don't have any usable items.".to_string()); 185 | } else { 186 | gs.game_state = TickType::UseMenu; 187 | } 188 | } 189 | 190 | fn drop_menu(gs : &mut State) { 191 | if gs.player().inventory.items.is_empty() { 192 | gs.add_log_entry("You don't have any items to drop!".to_string()); 193 | } else { 194 | gs.game_state = TickType::DropMenu; 195 | } 196 | } 197 | 198 | fn equip_menu(gs : &mut State) { 199 | if gs.player().inventory.get_equippable_items().is_empty() { 200 | gs.add_log_entry("You don't have any equippable items.".to_string()); 201 | } else { 202 | gs.game_state = TickType::WieldMenu; 203 | } 204 | } 205 | 206 | fn unequip_menu(gs : &mut State) { 207 | if gs.player().inventory.equipped.is_empty() { 208 | gs.add_log_entry("You don't have any equipped items.".to_string()); 209 | } else { 210 | gs.game_state = TickType::UnequipMenu; 211 | } 212 | } 213 | 214 | pub fn use_item(item_index : i32, gs : &mut State) -> Vec { 215 | let mut result = Vec::new(); 216 | 217 | if gs.player().inventory.items[item_index as usize].requires_targeting_mode { 218 | gs.game_state = TickType::TargetingItem; 219 | gs.target_cell = gs.player().position; 220 | gs.targeting_item = item_index; 221 | result.push("Select a target tile".to_string()); 222 | return result; 223 | } 224 | 225 | let item_type = gs.player().inventory.items[item_index as usize].item_type; 226 | match item_type { 227 | ItemType::HealthPotion => { item_effects::use_health_potion(item_index, gs, &mut result) } 228 | ItemType::ZapScroll => { item_effects::use_zap_scroll(item_index, gs, &mut result) } 229 | ItemType::ConfusionScroll => { item_effects::use_confusion_scroll(item_index, gs, &mut result) } 230 | _ => {} 231 | } 232 | 233 | gs.game_state = TickType::PlayersTurn; 234 | 235 | result 236 | } 237 | 238 | pub fn use_area_item(gs : &mut State) { 239 | let mut result = Vec::new(); 240 | let item_type = gs.player().inventory.items[gs.targeting_item as usize].item_type; 241 | if let ItemType::FireballScroll = item_type { 242 | item_effects::use_fireball_scroll(gs, &mut result) 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/game/random.rs: -------------------------------------------------------------------------------- 1 | use rand::Rng; 2 | 3 | pub fn random_choice(table : Vec<(String, i32)>) -> String { 4 | let mut rng = rand::thread_rng(); 5 | 6 | let n = rng.gen_range(1,100); 7 | let mut running_sum = 0; 8 | for (opt,chance) in table.iter() { 9 | if n < chance+running_sum { return opt.to_string() } 10 | running_sum += chance; 11 | } 12 | table[0].0.to_string() 13 | } -------------------------------------------------------------------------------- /src/game/rect.rs: -------------------------------------------------------------------------------- 1 | pub struct Rect { 2 | pub x1 : i32, 3 | pub x2 : i32, 4 | pub y1 : i32, 5 | pub y2 : i32 6 | } 7 | 8 | impl Rect { 9 | pub fn new(x1:i32, y1: i32, x2:i32, y2:i32) -> Rect { 10 | Rect{x1, y1, x2, y2} 11 | } 12 | 13 | // Returns true if this overlaps with other 14 | pub fn intersect(&self, other:&Rect) -> bool { 15 | self.x1 <= other.x2 && self.x2 >= other.x1 && self.y1 <= other.y2 && self.y2 >= other.y1 16 | } 17 | 18 | pub fn center(&self) -> (i32, i32) { 19 | ((self.x1 + self.x2)/2, (self.y1 + self.y2)/2) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/game/ticktype.rs: -------------------------------------------------------------------------------- 1 | extern crate serde; 2 | use serde::{Serialize, Deserialize}; 3 | 4 | #[derive(Serialize, Deserialize, PartialEq)] 5 | pub enum TickType { 6 | None, MainMenu, PlayersTurn, EnemyTurn, GameOver, UseMenu, DropMenu, TargetingItem, LevelUpMenu, CharacterMenu, HelpMenu, WieldMenu, UnequipMenu 7 | } -------------------------------------------------------------------------------- /src/game/tiletype.rs: -------------------------------------------------------------------------------- 1 | extern crate serde; 2 | use serde::{Serialize, Deserialize}; 3 | 4 | #[derive(Serialize, Deserialize, PartialEq)] 5 | pub enum TileType { 6 | Wall, Floor, Stairs 7 | } 8 | -------------------------------------------------------------------------------- /src/game/vfx.rs: -------------------------------------------------------------------------------- 1 | use crate::rltk; 2 | use crate ::rltk::Console; 3 | use rltk::{Point, RGB, Rltk}; 4 | use super::{ State }; 5 | extern crate serde; 6 | use serde::{Serialize, Deserialize}; 7 | 8 | #[derive(Serialize, Deserialize, Clone, Copy)] 9 | pub struct Particle { 10 | position : Point, 11 | lifetime_ms : f32, 12 | fg : RGB, 13 | bg : RGB, 14 | glyph : u8 15 | } 16 | 17 | impl Particle { 18 | pub fn new(position:Point, fg:RGB, bg:RGB, glyph: u8, lifetime_ms : f32) -> Particle { 19 | Particle{ position, fg, bg, glyph, lifetime_ms } 20 | } 21 | 22 | pub fn render(&self, ctx : &mut Rltk) { 23 | ctx.set(self.position.x, self.position.y, self.fg, self.bg, self.glyph); 24 | } 25 | } 26 | 27 | pub fn age_particles(gs : &mut State, ctx : &mut Rltk) { 28 | for p in gs.vfx.iter_mut() { 29 | p.lifetime_ms -= ctx.frame_time_ms; 30 | } 31 | gs.vfx.retain(|a| a.lifetime_ms > 0.0); 32 | } -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate rltk; 2 | use rltk::Rltk; 3 | mod game; 4 | 5 | fn main() { 6 | let gs = game::State::new_menu(); 7 | let mut context = Rltk::init_simple8x8(80, 50, "Rusty Roguelike", "resources"); 8 | context.with_post_scanlines(true); 9 | rltk::main_loop(context, gs); 10 | } 11 | --------------------------------------------------------------------------------