├── .github └── workflows │ ├── docs.yml │ └── test.yml ├── .gitignore ├── API.md ├── LICENSE ├── README.md ├── exampleApp ├── assets │ ├── font.png │ ├── font.png.dat │ └── icon.png ├── config.nims ├── exampleApp.nimble ├── index.html ├── nico_minimal.html └── src │ └── main.nim ├── examples ├── adither.nim ├── assets │ ├── drop.ogg │ ├── exampleMusic.ogg │ ├── font.png │ ├── font.png.dat │ ├── gui.png │ ├── hitPaddle.ogg │ ├── hitWall.ogg │ ├── map.json │ ├── pal_aurora-1x.png │ ├── palette.gpl │ ├── platformer.png │ ├── rotation.png │ ├── spritesheet.png │ └── spritesheetRGBA.png ├── audio.nim ├── benchmark.nim ├── camera.nim ├── config.nims ├── coro.nim ├── gui.nim ├── gui2.nim ├── lines.nim ├── nico_minimal.html ├── overlap.nim ├── paintout.html ├── paintout.nim ├── palette.nim ├── platformer.html ├── platformer.nim ├── rotation.nim ├── sat.nim ├── scale.nim ├── screenscale.html ├── screenscale.nim ├── stencil.nim ├── stencil2.nim ├── textinput.nim ├── texture.nim ├── tweaker.nim ├── vertex.html ├── vertex.nim └── webrtc │ ├── Makefile │ ├── assets │ ├── font.png │ └── font.png.dat │ ├── index.html │ ├── main.js │ ├── peerjs.js │ └── src │ ├── main.nim │ └── peerjs.nim ├── nico.nim ├── nico.nim.cfg ├── nico.nimble ├── nico ├── backends │ ├── common.nim │ └── sdl2.nim ├── console.nim ├── controller.nim ├── debug.nim ├── fontdata.nim ├── grids.nim ├── gui.nim ├── gui2.nim ├── keycodes.nim ├── matrix.nim ├── quat.nim ├── ringbuffer.nim ├── spritedraw.nim ├── stb_vorbis.nim ├── tweaks.nim ├── utils.nim └── vec.nim ├── nicoandroid.nim ├── nicoandroid.nim.cfg ├── nicoboot.nim ├── nicosynth.nim ├── nicosynth.nim.cfg └── tests ├── assets ├── aurora.gpl ├── font.png ├── font.png.dat ├── map.json ├── pal_aurora-1x.png ├── palette.gpl ├── palette.png ├── paletteRGBA.png ├── spritesheet.json ├── spritesheet.png ├── spritesheetRGB.png └── spritesheetRGBA.png ├── camera.nim ├── config.nim ├── copymem.nim ├── fonts.nim ├── palette.nim ├── rgba.nim └── tilemap.nim /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Generate docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | gen: 10 | name: Generate and deploy docs 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: iffy/install-nim@v4.1.1 16 | - name: Generate 17 | run: | 18 | nimble install -d -y 19 | nimble docs 20 | - name: Deploy 21 | uses: peaceiris/actions-gh-pages@v3 22 | with: 23 | github_token: ${{ secrets.GITHUB_TOKEN }} 24 | publish_dir: ./docs 25 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | skip: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - run: echo "Skip job" 12 | 13 | before: 14 | runs-on: ubuntu-latest 15 | if: "! contains(github.event.head_commit.message, '[skip ci]')" 16 | steps: 17 | - run: echo "not contains '[skip ci]'" 18 | 19 | build: 20 | runs-on: ${{ matrix.os }} 21 | strategy: 22 | matrix: 23 | os: 24 | - ubuntu-latest 25 | #- windows-latest 26 | #- macOS-latest 27 | nim_version: 28 | - '1.4.0' 29 | - 'stable' 30 | needs: before 31 | steps: 32 | - uses: actions/checkout@v1 33 | - name: Cache nimble 34 | id: cache-nimble 35 | uses: actions/cache@v1 36 | with: 37 | path: ~/.nimble 38 | key: ${{ runner.os }}-nimble-${{ hashFiles('*.nimble') }} 39 | - uses: jiro4989/setup-nim-action@v1 40 | with: 41 | nim-version: ${{ matrix.nim_version }} 42 | 43 | - name: Fix apt packages 44 | run: | 45 | # see. https://github.com/actions/virtual-environments/issues/675 46 | sudo sed -i 's/azure\.//' /etc/apt/sources.list 47 | sudo apt update -yqq 48 | 49 | - name: Install sdl2 50 | run: sudo apt install -y libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev 51 | 52 | - name: Install packages 53 | run: nimble install -Y 54 | 55 | - name: Build examples 56 | run: | 57 | nimble paintout 58 | nimble platformer 59 | nimble audio 60 | nimble vertex 61 | nimble gui 62 | 63 | - name: Build exampleApp 64 | run: | 65 | cd exampleApp 66 | nimble release 67 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | src/nimcache/* 3 | *.exe 4 | *.js 5 | *.dll 6 | *.wasm 7 | *.data 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Jez Kabanoff "Impbox" 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NICO Game Framework 2 | [![test](https://github.com/ftsf/nico/workflows/test/badge.svg)](https://github.com/ftsf/nico/actions) 3 | 4 | NICO is a simple game framework for the [Nim](http://nim-lang.org/) programming language inspired by the [PICO-8](https://www.lexaloffle.com/pico-8.php) API. 5 | 6 | ## Supported platforms: 7 | * Windows 8 | * Linux, including RaspberryPi 9 | * Web/HTML5 via Emscripten 10 | * Android 11 | * MacOS 12 | 13 | ## What it does: 14 | * Paletted Bitmap Graphics (you can load a custom palette up to 256 colors) 15 | * Fixed or flexible custom display resolution with pixel scaling 16 | * Sprite drawing, load png spritesheets, specify tile size per sheet (can load multiple and switch between them) 17 | * Tilemap drawing, import json from Tiled 18 | * Drawing primitives: pixels, lines, rectangles, circles, triangles 19 | * Input: Keyboard, Gamepad, Mouse, Touch 20 | * Sfx playback: load and play ogg vorbis files, configurable number of mixer channels. 21 | * Built in chip synth 22 | * Music playback: stream ogg vorbis files. 23 | * Custom audio callback for generating your own sounds via code. 24 | * Text drawing: load and draw fonts from png, supports variable width fonts. 25 | * Export animated gifs 26 | 27 | ## Installation 28 | * You will need to have the Nim compiler installed 29 | * Run ```nimble install nico``` 30 | * Run ```nicoboot ``` to create a new directory with an example base ready to start working with. 31 | * [You can watch a quick tutorial here](https://www.youtube.com/watch?v=czLI5XJFxYA&list=PLxLdEZg8DRwTIEzUpfaIcBqhsj09mLWHx&index=3) 32 | * Native build: 33 | * You'll need [SDL2](https://www.libsdl.org/download-2.0.php) for native builds, on Windows, ensure SDL2.dll is copied to your project directory. 34 | * From your project directory run ```nimble runr``` to build and run the example as a native build. 35 | * From your project directory run ```nimble rund``` to build and run the example as a debug native build. 36 | * Web build: 37 | * For web builds you'll need [Emscripten](https://emscripten.org/docs/getting_started/downloads.html). 38 | * From your project directory run ```nimble webr``` to build for web in release mode. 39 | * From your project directory run ```nimble webd``` to build for web in debug mode. 40 | 41 | ## Learning 42 | * [API Documentation](API.md) 43 | * [Examples](examples/) 44 | 45 | ## Why should you use NICO? 46 | * It's fun and easy to use 47 | * Learn Nim the fun way! It's a great new statically typed programming language that compiles to C. 48 | * You can build for Web, Windows, Linux, Mac, Android, and potentially other platforms. 49 | 50 | ## Future work: 51 | * API Documentation 52 | * More examples 53 | * Tests 54 | * Utility modules for common higher level tasks 55 | * Browser to browser networking using WebRTC 56 | * Immediate mode GUI 57 | * 3D Utils and Rasterizer 58 | 59 | ## Games made using NICO: 60 | * [Vektor 2089](https://impbox.itch.io/vektor2089) 61 | * [Smalltrek](https://impbox.itch.io/smalltrek) 62 | * [Moving in](https://impbox.itch.io/moving-in) 63 | * [Cute Cats Daily](https://impbox.itch.io/cute-cats-daily) 64 | * [Super Netwalk Deluxe](https://impbox.itch.io/super-netwalk-deluxe) 65 | -------------------------------------------------------------------------------- /exampleApp/assets/font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftsf/nico/409eec1eeab05f5eb07d17343992638ce37f25ed/exampleApp/assets/font.png -------------------------------------------------------------------------------- /exampleApp/assets/font.png.dat: -------------------------------------------------------------------------------- 1 | !"#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{}~ -------------------------------------------------------------------------------- /exampleApp/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftsf/nico/409eec1eeab05f5eb07d17343992638ce37f25ed/exampleApp/assets/icon.png -------------------------------------------------------------------------------- /exampleApp/config.nims: -------------------------------------------------------------------------------- 1 | when defined(emscripten): 2 | import std/compilesettings 3 | import strformat 4 | 5 | # This path will only run if -d:emscripten is passed to nim. 6 | --nimcache:tmp # Store intermediate files close by in the ./tmp dir. 7 | 8 | --os:linux # Emscripten pretends to be linux. 9 | --cpu:wasm32 # Emscripten is 32bits. 10 | --cc:clang # Emscripten is very close to clang, so we ill replace it. 11 | when defined(windows): 12 | --clang.exe:emcc.bat # Replace C 13 | --clang.linkerexe:emcc.bat # Replace C linker 14 | --clang.cpp.exe:emcc.bat # Replace C++ 15 | --clang.cpp.linkerexe:emcc.bat # Replace C++ linker. 16 | else: 17 | --clang.exe:emcc # Replace C 18 | --clang.linkerexe:emcc # Replace C linker 19 | --clang.cpp.exe:emcc # Replace C++ 20 | --clang.cpp.linkerexe:emcc # Replace C++ linker. 21 | --listCmd # List what commands we are running so that we can debug them. 22 | 23 | --gc:orc # GC:arc is friendlier with crazy platforms. 24 | --exceptions:goto # Goto exceptions are friendlier with crazy platforms. 25 | --define:noSignalHandler # Emscripten doesn't support signal handlers. 26 | 27 | --dynlibOverride:SDL2 28 | 29 | when defined(opengl): 30 | --dynlibOverride:opengl 31 | 32 | --define:emscripten 33 | 34 | # Pass this to Emscripten linker to generate html file scaffold for us. 35 | const projName = projectName() 36 | const userSetOutFile = querySetting(SingleValueSetting.outFile) 37 | const outFile = if userSetOutFile == "": &"{projName}.html" else: userSetOutFile 38 | when defined(debug): 39 | switch("passL", &"-o {outFile} --shell-file nico_minimal.html -lidbfs.js -s ASSERTIONS=1 -s USE_SDL=2 -s FORCE_FILESYSTEM=1 -s ALLOW_MEMORY_GROWTH -s EXPORTED_FUNCTIONS=[\"_main\",\"_initConfigDone\"] -s EXPORTED_RUNTIME_METHODS=[\"ccall\",\"cwrap\"] -s EXTRA_EXPORTED_RUNTIME_METHODS=[\"print\"] -O0 --preload-file assets") 40 | else: 41 | switch("passL", &"-o {outFile} --shell-file nico_minimal.html -lidbfs.js -s ASSERTIONS=1 -s USE_SDL=2 -s FORCE_FILESYSTEM=1 -s ALLOW_MEMORY_GROWTH -s EXPORTED_FUNCTIONS=[\"_main\",\"_initConfigDone\"] -s EXPORTED_RUNTIME_METHODS=[\"ccall\",\"cwrap\"] -s EXTRA_EXPORTED_RUNTIME_METHODS=[\"print\"] -O3 --preload-file assets") 42 | -------------------------------------------------------------------------------- /exampleApp/exampleApp.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.1.0" 4 | author = "exampleOrg" 5 | description = "exampleApp" 6 | license = "?" 7 | 8 | # Deps 9 | requires "nim >= 1.2.0" 10 | requires "nico >= 0.2.5" 11 | 12 | srcDir = "src" 13 | 14 | import strformat 15 | 16 | const releaseOpts = "-d:danger" 17 | const debugOpts = "-d:debug" 18 | 19 | task runr, "Runs exampleApp for current platform": 20 | exec &"nim c -r {releaseOpts} -o:exampleApp src/main.nim" 21 | 22 | task rund, "Runs debug exampleApp for current platform": 23 | exec &"nim c -r {debugOpts} -o:exampleApp src/main.nim" 24 | 25 | task release, "Builds exampleApp for current platform": 26 | exec &"nim c {releaseOpts} -o:exampleApp src/main.nim" 27 | 28 | task webd, "Builds debug exampleApp for web": 29 | exec &"nim c {debugOpts} -d:emscripten -o:exampleApp.html src/main.nim" 30 | 31 | task webr, "Builds release exampleApp for web": 32 | exec &"nim c {releaseOpts} -d:emscripten -o:exampleApp.html src/main.nim" 33 | 34 | task debug, "Builds debug exampleApp for current platform": 35 | exec &"nim c {debugOpts} -o:exampleApp_debug src/main.nim" 36 | 37 | task deps, "Downloads dependencies": 38 | if defined(windows): 39 | if not fileExists("SDL2.dll"): 40 | if not fileExists("SDL2_x64.zip"): 41 | exec "curl https://www.libsdl.org/release/SDL2-2.0.20-win32-x64.zip -o SDL2_x64.zip" 42 | if findExe("tar") != "": 43 | exec "tar -xf SDL2_x64.zip SDL2.dll" 44 | else: 45 | exec "unzip SDL2_x64.zip SDL2.dll" 46 | if fileExists("SDL2_x64.zip"): 47 | rmFile("SDL2_x64.zip") 48 | elif defined(macosx) and findExe("brew") != "": 49 | exec "brew list sdl2 || brew install sdl2" 50 | else: 51 | echo "I don't know how to install SDL on your OS! 😿" 52 | 53 | task androidr, "Release build for android": 54 | if defined(windows): 55 | exec &"nicoandroid.cmd" 56 | else: 57 | exec &"nicoandroid" 58 | exec &"nim c -c --nimcache:android/app/jni/src/armeabi {releaseOpts} --cpu:arm --os:android -d:androidNDK --noMain --genScript src/main.nim" 59 | exec &"nim c -c --nimcache:android/app/jni/src/arm64 {releaseOpts} --cpu:arm64 --os:android -d:androidNDK --noMain --genScript src/main.nim" 60 | exec &"nim c -c --nimcache:android/app/jni/src/x86 {releaseOpts} --cpu:i386 --os:android -d:androidNDK --noMain --genScript src/main.nim" 61 | exec &"nim c -c --nimcache:android/app/jni/src/x86_64 {releaseOpts} --cpu:amd64 --os:android -d:androidNDK --noMain --genScript src/main.nim" 62 | withDir "android": 63 | if defined(windows): 64 | exec &"gradlew.bat assembleDebug" 65 | else: 66 | exec "./gradlew assembleDebug" 67 | -------------------------------------------------------------------------------- /exampleApp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | exampleApp 19 | 20 | 21 |
22 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /exampleApp/nico_minimal.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | exampleApp 7 | 59 | 60 | 61 |
emscripten
62 |
Downloading...
63 |
64 | 65 |
66 | 67 | 124 | {{{ SCRIPT }}} 125 | 126 | 127 | -------------------------------------------------------------------------------- /exampleApp/src/main.nim: -------------------------------------------------------------------------------- 1 | import nico 2 | 3 | const orgName = "exampleOrg" 4 | const appName = "exampleApp" 5 | 6 | var buttonDown = false 7 | 8 | proc gameInit() = 9 | loadFont(0, "font.png") 10 | 11 | proc gameUpdate(dt: float32) = 12 | buttonDown = btn(pcA) 13 | 14 | proc gameDraw() = 15 | cls() 16 | setColor(if buttonDown: 7 else: 3) 17 | printc("welcome to " & appName, screenWidth div 2, screenHeight div 2) 18 | 19 | nico.init(orgName, appName) 20 | nico.createWindow(appName, 128, 128, 4, false) 21 | nico.run(gameInit, gameUpdate, gameDraw) 22 | -------------------------------------------------------------------------------- /examples/adither.nim: -------------------------------------------------------------------------------- 1 | import nico 2 | 3 | var ditherLevel = 0.5f 4 | var xorMode = false 5 | 6 | proc gameInit() = 7 | setPalette(loadPalettePico8()) 8 | 9 | proc gameUpdate(dt: Pfloat) = 10 | if btn(pcUp): 11 | ditherLevel += 0.01f 12 | if btn(pcDown): 13 | ditherLevel -= 0.01f 14 | if btnp(pcA): 15 | xorMode.incWrap() 16 | 17 | proc gameDraw() = 18 | cls() 19 | if xorMode: 20 | ditherADitherXor(ditherLevel) 21 | else: 22 | ditherADitherAdd(ditherLevel) 23 | setColor(7) 24 | boxfill(0,0,screenWidth,screenHeight) 25 | 26 | ditherNone() 27 | print($ditherLevel, 1, 1) 28 | setColor(0) 29 | print($ditherLevel, 1, 14) 30 | 31 | # initialization 32 | nico.init("nico", "test") 33 | 34 | # we want a fixed sized screen with perfect square pixels 35 | fixedSize(true) 36 | integerScale(true) 37 | # create the window 38 | nico.createWindow("nico",128,128,4) 39 | 40 | # start, say which functions to use for init, update and draw 41 | nico.run(gameInit, gameUpdate, gameDraw) 42 | -------------------------------------------------------------------------------- /examples/assets/drop.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftsf/nico/409eec1eeab05f5eb07d17343992638ce37f25ed/examples/assets/drop.ogg -------------------------------------------------------------------------------- /examples/assets/exampleMusic.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftsf/nico/409eec1eeab05f5eb07d17343992638ce37f25ed/examples/assets/exampleMusic.ogg -------------------------------------------------------------------------------- /examples/assets/font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftsf/nico/409eec1eeab05f5eb07d17343992638ce37f25ed/examples/assets/font.png -------------------------------------------------------------------------------- /examples/assets/font.png.dat: -------------------------------------------------------------------------------- 1 | !"#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{}~ -------------------------------------------------------------------------------- /examples/assets/gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftsf/nico/409eec1eeab05f5eb07d17343992638ce37f25ed/examples/assets/gui.png -------------------------------------------------------------------------------- /examples/assets/hitPaddle.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftsf/nico/409eec1eeab05f5eb07d17343992638ce37f25ed/examples/assets/hitPaddle.ogg -------------------------------------------------------------------------------- /examples/assets/hitWall.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftsf/nico/409eec1eeab05f5eb07d17343992638ce37f25ed/examples/assets/hitWall.ogg -------------------------------------------------------------------------------- /examples/assets/map.json: -------------------------------------------------------------------------------- 1 | { "height":16, 2 | "infinite":false, 3 | "layers":[ 4 | { 5 | "data":[21, 22, 21, 23, 22, 21, 24, 23, 24, 23, 22, 21, 22, 23, 24, 21, 21, 24, 23, 23, 22, 22, 24, 22, 21, 22, 24, 22, 23, 22, 23, 21, 23, 21, 23, 24, 24, 21, 24, 24, 24, 23, 22, 24, 21, 24, 24, 24, 29, 29, 29, 29, 29, 30, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 23, 21, 22, 21, 23, 31, 24, 21, 21, 22, 23, 22, 22, 24, 22, 21, 23, 21, 24, 21, 23, 31, 23, 22, 22, 23, 23, 22, 23, 24, 23, 24, 22, 24, 23, 21, 24, 31, 23, 24, 23, 22, 22, 21, 23, 22, 23, 21, 21, 21, 23, 24, 23, 31, 21, 21, 21, 22, 22, 21, 24, 24, 21, 22, 22, 22, 24, 22, 21, 31, 24, 24, 24, 22, 22, 24, 21, 21, 21, 23, 21, 22, 22, 21, 24, 31, 24, 21, 22, 24, 22, 22, 21, 23, 23, 21, 21, 24, 21, 22, 21, 31, 24, 24, 21, 23, 23, 22, 24, 21, 22, 21, 22, 22, 24, 23, 23, 31, 24, 24, 24, 22, 23, 22, 23, 22, 22, 23, 24, 21, 22, 21, 24, 31, 24, 24, 21, 21, 22, 21, 23, 21, 21, 23, 22, 21, 22, 23, 21, 31, 24, 24, 24, 24, 21, 24, 22, 23, 24, 24, 21, 21, 23, 22, 22, 31, 22, 21, 24, 22, 24, 23, 21, 23, 22, 22, 24, 23, 24, 24, 21, 31, 22, 24, 22, 21, 23, 23, 22, 21, 23, 24], 6 | "height":16, 7 | "name":"Tile Layer 1", 8 | "opacity":1, 9 | "type":"tilelayer", 10 | "visible":true, 11 | "width":16, 12 | "x":0, 13 | "y":0 14 | }], 15 | "nextobjectid":1, 16 | "orientation":"orthogonal", 17 | "renderorder":"right-down", 18 | "tiledversion":"1.1.4", 19 | "tileheight":16, 20 | "tilesets":[ 21 | { 22 | "columns":8, 23 | "firstgid":1, 24 | "image":"rotation.png", 25 | "imageheight":128, 26 | "imagewidth":128, 27 | "margin":0, 28 | "name":"rotation", 29 | "spacing":0, 30 | "tilecount":64, 31 | "tileheight":16, 32 | "tilewidth":16 33 | }], 34 | "tilewidth":16, 35 | "type":"map", 36 | "version":1, 37 | "width":16 38 | } -------------------------------------------------------------------------------- /examples/assets/pal_aurora-1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftsf/nico/409eec1eeab05f5eb07d17343992638ce37f25ed/examples/assets/pal_aurora-1x.png -------------------------------------------------------------------------------- /examples/assets/palette.gpl: -------------------------------------------------------------------------------- 1 | GIMP Palette 2 | # 3 | 190 74 47 Untitled 4 | 216 118 68 Untitled 5 | 234 212 170 Untitled 6 | 228 166 114 Untitled 7 | 184 111 80 Untitled 8 | 116 63 57 Untitled 9 | 63 40 50 Untitled 10 | 158 40 53 Untitled 11 | 228 59 68 Untitled 12 | 247 118 34 Untitled 13 | 254 174 52 Untitled 14 | 254 231 97 Untitled 15 | 99 199 77 Untitled 16 | 62 137 72 Untitled 17 | 38 92 66 Untitled 18 | 25 60 62 Untitled 19 | 18 78 137 Untitled 20 | 0 149 233 Untitled 21 | 44 232 245 Untitled 22 | 255 255 255 Untitled 23 | 192 203 220 Untitled 24 | 139 155 180 Untitled 25 | 90 105 136 Untitled 26 | 58 68 102 Untitled 27 | 38 43 68 Untitled 28 | 255 0 68 Untitled 29 | 24 20 37 Untitled 30 | 104 56 108 Untitled 31 | 181 80 136 Untitled 32 | 246 117 122 Untitled 33 | 232 183 150 Untitled 34 | 194 133 105 Untitled 35 | -------------------------------------------------------------------------------- /examples/assets/platformer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftsf/nico/409eec1eeab05f5eb07d17343992638ce37f25ed/examples/assets/platformer.png -------------------------------------------------------------------------------- /examples/assets/rotation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftsf/nico/409eec1eeab05f5eb07d17343992638ce37f25ed/examples/assets/rotation.png -------------------------------------------------------------------------------- /examples/assets/spritesheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftsf/nico/409eec1eeab05f5eb07d17343992638ce37f25ed/examples/assets/spritesheet.png -------------------------------------------------------------------------------- /examples/assets/spritesheetRGBA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftsf/nico/409eec1eeab05f5eb07d17343992638ce37f25ed/examples/assets/spritesheetRGBA.png -------------------------------------------------------------------------------- /examples/audio.nim: -------------------------------------------------------------------------------- 1 | import nico 2 | import strutils 3 | 4 | var n = 69 5 | var shape = 0 6 | 7 | type Instrument = tuple[shape: SynthShape, init: range[0..15], change: range[-7..7]] 8 | type Row = tuple[note: uint8, inst: uint8, command: uint8, arg: uint8] 9 | type Pattern = array[16, Row] 10 | type SongRow = array[4, uint8] 11 | type Song = array[64, SongRow] 12 | 13 | var patterns: array[64, Pattern] 14 | var song: Song 15 | var instruments: array[64, Instrument] 16 | 17 | type View = enum 18 | songView = "Song" 19 | patternView = "Pattern" 20 | instrumentView = "Inst" 21 | 22 | var view: View 23 | var tick = 0 24 | var currentPattern = 0 25 | var currentSongRow = 0 26 | var currentChannel = 0 27 | var currentPatternRow = 0 28 | var currentPatternCol: range[0..3] = 0 29 | var currentInstrument = 0 30 | var currentInstrumentSetting: range[0..3] = 0 31 | 32 | var songChannelCursors: array[4, uint8] 33 | 34 | proc musicUpdate() = 35 | for channel in 0..3: 36 | let pat = song[songChannelCursors[channel]][channel] 37 | if pat != 0: 38 | let n = patterns[pat][tick mod 16].note 39 | if n != 0: 40 | # todo get instr settings 41 | let i = patterns[pat][tick mod 16].inst 42 | let inst = instruments[i] 43 | if i == 0: 44 | pitch(channel, note(n.int)) 45 | else: 46 | synth(channel, inst.shape, note(n.int), inst.init, inst.change) 47 | tick += 1 48 | if tick mod 16 == 0: 49 | # move cursors 50 | for channel in 0..3: 51 | # check if the next row in the song has an entry 52 | if song[songChannelCursors[channel] + 1][channel] != 0: 53 | songChannelCursors[channel] += 1 54 | else: 55 | # seek up to the top of the island 56 | while true: 57 | if songChannelCursors[channel] == 0 or song[songChannelCursors[channel] - 1][channel] == 0: 58 | break 59 | else: 60 | songChannelCursors[channel] -= 1 61 | 62 | proc gameInit() = 63 | bpm(90) 64 | tpb(4) 65 | n = 69 66 | shape = 0 67 | tick = 0 68 | view = songView 69 | currentPattern = 0 70 | currentSongRow = 0 71 | currentChannel = 0 72 | currentPatternRow = 0 73 | currentPatternCol = 0 74 | 75 | instruments[1].shape = synP25 76 | instruments[1].init = 7 77 | instruments[1].change = -1 78 | 79 | instruments[2].shape = synP12 80 | instruments[2].init = 5 81 | instruments[2].change = -2 82 | 83 | instruments[3].shape = synSaw 84 | instruments[3].init = 3 85 | instruments[3].change = -3 86 | 87 | instruments[4].shape = synNoise 88 | instruments[4].init = 5 89 | instruments[4].change = -7 90 | 91 | instruments[5].shape = synTri 92 | instruments[5].init = 0 93 | instruments[5].change = 1 94 | 95 | setAudioTickCallback(musicUpdate) 96 | 97 | proc gameUpdate(dt: float32) = 98 | if btn(pcX): 99 | if btnp(pcLeft): 100 | if view > typeof(view).low: 101 | view = (view.int - 1).View 102 | return 103 | if btnp(pcRight): 104 | if view < typeof(view).high: 105 | view = (view.int + 1).View 106 | return 107 | 108 | case view: 109 | of songView: 110 | if btnp(pcY): 111 | for i in 0..3: 112 | songChannelCursors[i] = currentSongRow.uint8 113 | tick = 0 114 | if btn(pcA): 115 | if btnp(pcLeft): 116 | if song[currentSongRow][currentChannel].int > 0: 117 | song[currentSongRow][currentChannel] -= 1 118 | currentPattern = song[currentSongRow][currentChannel].int 119 | if btnp(pcRight): 120 | if song[currentSongRow][currentChannel].int < 63: 121 | song[currentSongRow][currentChannel] += 1 122 | currentPattern = song[currentSongRow][currentChannel].int 123 | if btnp(pcUp): 124 | if song[currentSongRow][currentChannel].int < 48: 125 | song[currentSongRow][currentChannel] += 16 126 | currentPattern = song[currentSongRow][currentChannel].int 127 | if btnp(pcDown): 128 | if song[currentSongRow][currentChannel].int > 15: 129 | song[currentSongRow][currentChannel] -= 16 130 | currentPattern = song[currentSongRow][currentChannel].int 131 | elif btnp(pcB): 132 | song[currentSongRow][currentChannel] = 0 133 | else: 134 | if btnp(pcLeft): 135 | currentChannel -= 1 136 | if currentChannel < 0: 137 | currentChannel = 0 138 | currentPattern = song[currentSongRow][currentChannel].int 139 | if btnp(pcRight): 140 | currentChannel += 1 141 | if currentChannel > 3: 142 | currentChannel = 3 143 | currentPattern = song[currentSongRow][currentChannel].int 144 | if btnp(pcUp): 145 | currentSongRow -= 1 146 | if currentSongRow < 0: 147 | currentSongRow = 0 148 | currentPattern = song[currentSongRow][currentChannel].int 149 | if btnp(pcDown): 150 | currentSongRow += 1 151 | if currentSongRow > 63: 152 | currentSongRow = 63 153 | currentPattern = song[currentSongRow][currentChannel].int 154 | 155 | of patternView: 156 | if btn(pcA): 157 | case currentPatternCol: 158 | of 0: 159 | if patterns[currentPattern][currentPatternRow].note != 0: 160 | n = patterns[currentPattern][currentPatternRow].note.int 161 | if btnp(pcUp): 162 | n += 12 163 | if btnp(pcDown): 164 | n -= 12 165 | if btnp(pcLeft): 166 | n -= 1 167 | if btnp(pcRight): 168 | n += 1 169 | patterns[currentPattern][currentPatternRow].note = n.uint8 170 | patterns[currentPattern][currentPatternRow].inst = currentInstrument.uint8 171 | of 1: 172 | currentInstrument = patterns[currentPattern][currentPatternRow].inst.int 173 | if btnp(pcUp): 174 | if currentInstrument < 63: 175 | currentInstrument += 16 176 | else: 177 | currentInstrument = 63 178 | if btnp(pcDown): 179 | if currentInstrument > 16: 180 | currentInstrument -= 16 181 | else: 182 | currentInstrument = 0 183 | if btnp(pcLeft): 184 | if currentInstrument > 0: 185 | currentInstrument -= 1 186 | if btnp(pcRight): 187 | if currentInstrument < 15: 188 | currentInstrument += 1 189 | patterns[currentPattern][currentPatternRow].inst = currentInstrument.uint8 190 | of 2: 191 | discard 192 | of 3: 193 | discard 194 | elif btnp(pcB): 195 | case currentPatternCol: 196 | of 0: 197 | patterns[currentPattern][currentPatternRow].note = 0 198 | of 1: 199 | patterns[currentPattern][currentPatternRow].inst = 0 200 | of 2: 201 | patterns[currentPattern][currentPatternRow].command = 0 202 | of 3: 203 | patterns[currentPattern][currentPatternRow].arg = 0 204 | else: 205 | if btnp(pcUp): 206 | if currentPatternRow > 0: 207 | currentPatternRow -= 1 208 | if btnp(pcDown): 209 | if currentPatternRow < 15: 210 | currentPatternRow += 1 211 | if btnp(pcLeft): 212 | if currentPatternCol > 0: 213 | currentPatternCol -= 1 214 | if btnp(pcRight): 215 | if currentPatternCol < 3: 216 | currentPatternCol += 1 217 | 218 | of instrumentView: 219 | if btn(pcA): 220 | case currentInstrumentSetting: 221 | of 0: 222 | if btnp(pcLeft): 223 | if instruments[currentInstrument].shape < SynthShape.high: 224 | instruments[currentInstrument].shape.inc() 225 | if btnp(pcRight): 226 | if instruments[currentInstrument].shape > SynthShape.low: 227 | instruments[currentInstrument].shape.dec() 228 | of 1: 229 | if btnp(pcLeft): 230 | if instruments[currentInstrument].init > 0: 231 | instruments[currentInstrument].init -= 1 232 | if btnp(pcRight): 233 | if instruments[currentInstrument].init < 15: 234 | instruments[currentInstrument].init += 1 235 | of 2: 236 | if btnp(pcLeft): 237 | if instruments[currentInstrument].change > -7: 238 | instruments[currentInstrument].change -= 1 239 | if btnp(pcRight): 240 | if instruments[currentInstrument].change < 7: 241 | instruments[currentInstrument].change += 1 242 | of 3: 243 | discard 244 | else: 245 | if btnp(pcUp): 246 | if currentInstrumentSetting > 0: 247 | currentInstrumentSetting -= 1 248 | if btnp(pcDown): 249 | if currentInstrumentSetting < 15: 250 | currentInstrumentSetting += 1 251 | 252 | proc noteString(n: int): string = 253 | if n == 0: 254 | return "..." 255 | 256 | let oct = n div 12 257 | let note = n mod 12 258 | 259 | case note: 260 | of 0: 261 | return "C-" & $oct 262 | of 1: 263 | return "C#" & $oct 264 | of 2: 265 | return "D-" & $oct 266 | of 3: 267 | return "D#" & $oct 268 | of 4: 269 | return "E-" & $oct 270 | of 5: 271 | return "F-" & $oct 272 | of 6: 273 | return "F#" & $oct 274 | of 7: 275 | return "G-" & $oct 276 | of 8: 277 | return "G#" & $oct 278 | of 9: 279 | return "A-" & $oct 280 | of 10: 281 | return "A#" & $oct 282 | of 11: 283 | return "B-" & $oct 284 | else: 285 | return "???" 286 | 287 | proc gameDraw() = 288 | cls() 289 | 290 | case view: 291 | of songView: 292 | setColor(2) 293 | printr($view, screenWidth - 1, 1) 294 | for row in 0..", 16 + col * 16 - 4, 1 + row * 8) 301 | setColor(if currentSongRow == row and currentChannel == col: 7 else: 5) 302 | if song[row][col] == 0: 303 | print("..", 16 + col * 16, 1 + row * 8) 304 | else: 305 | print(toHex(song[row][col]), 16 + col * 16, 1 + row * 8) 306 | of patternView: 307 | setColor(2) 308 | printr($view & " " & toHex(currentPattern, 2), screenWidth - 1, 1) 309 | var p = patterns[currentPattern] 310 | for row in 0..= 1f: 65 | secondTimer = 0f 66 | framesRenderedLastSecond = framesRendered 67 | framesRendered = 0 68 | 69 | cx = (sin(time / 5f) * 32f).int 70 | cy = (sin(time / 3f) * 32f).int 71 | 72 | if keyp(K_c): 73 | useClip = not useClip 74 | 75 | if keyp(K_v): 76 | vsync = not vsync 77 | setVSync(vsync) 78 | 79 | if btn(pcLeft) and toDraw[mode] > 0: 80 | toDraw[mode] -= 10 81 | if btn(pcRight): 82 | toDraw[mode] += 10 83 | if btnpr(pcDown) and toDraw[mode] > 1000: 84 | toDraw[mode] -= 1000 85 | if btnpr(pcUp): 86 | toDraw[mode] += 1000 87 | if btnp(pcA): 88 | mode.incWrap() 89 | if btnp(pcB): 90 | autoAdjust = not autoAdjust 91 | 92 | if autoAdjust: 93 | if lastMs < 15.0'f: 94 | toDraw[mode] += 100 95 | elif lastMs > 16.0'f: 96 | if toDraw[mode] > 100: 97 | toDraw[mode] -= 100 98 | 99 | proc gameDraw() = 100 | cls() 101 | let tstart = getPerformanceCounter() 102 | var count = 0 103 | 104 | let toDraw = toDraw[mode] 105 | 106 | setCamera(cx,cy) 107 | 108 | if useClip: 109 | clip(10,10,screenWidth-1-20,screenHeight-1-20) 110 | 111 | case mode: 112 | of bmPoints: 113 | for i in 0.. 2 | 3 | 4 | 5 | 6 | Emscripten-Generated Code 7 | 59 | 60 | 61 |
emscripten
62 |
Downloading...
63 |
64 | 65 |
66 | 67 | 124 | {{{ SCRIPT }}} 125 | 126 | 127 | -------------------------------------------------------------------------------- /examples/overlap.nim: -------------------------------------------------------------------------------- 1 | import nico, math 2 | 3 | 4 | 5 | var 6 | mouseDraw : SpriteDraw 7 | blockDraw : SpriteDraw 8 | 9 | 10 | proc init() = 11 | setCamera(0, 0) 12 | loadSpriteSheet(0, "platformer.png",16,16) 13 | mouseDraw = initSpriteDraw(0,8,0,0) 14 | 15 | blockDraw = initSpriteDraw(0,0,54,54) 16 | 17 | proc update(dt: Pfloat) = 18 | let moose = mouse() 19 | mouseDraw.x = moose[0] - 4 20 | mouseDraw.y= moose[1] - 4 21 | 22 | 23 | proc draw() = 24 | cls() 25 | setColor(17) 26 | spr(mouseDraw) 27 | spr(blockDraw) 28 | setColor(1) 29 | if(mouseDraw.sprOverlap(blockDraw)): 30 | printc("Over the object",75,0) 31 | 32 | nico.init("Nico","Overlap") 33 | 34 | nico.createWindow("nico",128,128,4,false) 35 | 36 | loadFont(0,"font.png") 37 | setFont(0) 38 | fixedSize(true) 39 | integerScale(true) 40 | 41 | nico.run(init,update,draw) -------------------------------------------------------------------------------- /examples/paintout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Emscripten-Generated Code 7 | 21 | 22 | 23 | 24 | 25 | 26 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /examples/paintout.nim: -------------------------------------------------------------------------------- 1 | import nico 2 | import math 3 | 4 | # frame counter 5 | var frame = 0 6 | 7 | # color 8 | var t = 8.0f 9 | 10 | # paddle 11 | var px = 64.0f 12 | var py = 120.0f 13 | var pxv = 0.0f 14 | 15 | # ball 16 | var bx = 64.0f 17 | var by = 64.0f 18 | var bxv = 0.0f 19 | var byv = 0.0f 20 | 21 | var text: string 22 | var splat = false 23 | var clear = false 24 | 25 | const textOptions = [ 26 | "HELLO WORLD", 27 | "WELCOME TO NICO", 28 | "MEOW", 29 | "RIBBIT", 30 | "NIM IS FUN", 31 | ] 32 | 33 | const 34 | sfxHitPaddle = 0 35 | sfxHitWall = 1 36 | sfxDrop = 2 37 | 38 | proc gameInit() = 39 | # load all our assets 40 | loadFont(0, "font.png") 41 | setFont(0) 42 | 43 | loadMusic(0, "exampleMusic.ogg") 44 | 45 | loadSfx(sfxHitPaddle, "hitPaddle.ogg") 46 | loadSfx(sfxHitWall, "hitWall.ogg") 47 | loadSfx(sfxDrop, "drop.ogg") 48 | 49 | # start playing music 50 | music(15, 0) 51 | 52 | text = "HELLO WORLD" 53 | bxv = 0.5f 54 | byv = 0.75f 55 | 56 | proc gameUpdate(dt: Pfloat) = 57 | frame += 1 58 | t += 0.1f 59 | if t >= 16f: 60 | t = 8f 61 | 62 | # move paddle 63 | if btn(pcLeft): 64 | pxv -= 0.1f 65 | if btn(pcRight): 66 | pxv += 0.1f 67 | px += pxv 68 | pxv *= 0.95f 69 | 70 | let tw = textWidth(text).float32 71 | if px - tw / 2f < 0: 72 | px = tw / 2f 73 | if px > screenWidth.float32 - tw / 2f: 74 | px = (screenWidth.float32 - tw / 2f) 75 | 76 | # move ball 77 | bx += bxv 78 | by += byv 79 | byv += 0.1f 80 | 81 | # hit the sides 82 | if bx > screenWidth - 4 or bx < 4: 83 | bxv = -bxv 84 | splat = true 85 | sfx(-1, sfxHitWall) 86 | 87 | # hit paddle 88 | if by > screenHeight - 8 and bx > px - tw div 2 - 4 and bx < px + tw div 2 + 4: 89 | byv = -byv * 0.8 - 1.0 90 | bxv += pxv 91 | splat = true 92 | sfx(-1, sfxHitPaddle) 93 | 94 | # restart 95 | if btnp(pcA) or by > screenHeight: 96 | bx = 64 97 | by = 64 98 | byv = -2.0 99 | bxv = rnd(2.0)-1.0 100 | clear = true 101 | sfx(-1, sfxDrop) 102 | 103 | if btnp(pcB): 104 | # switch text 105 | text = rnd(textOptions) 106 | 107 | proc gameDraw() = 108 | # clear the at start and when we restart 109 | if frame == 1 or clear or btnp(pcA): 110 | setColor(1) 111 | rectfill(0,0,128,128) 112 | clear = false 113 | 114 | # draw the paddle 115 | setColor(t.int) 116 | printc(text, px, py) 117 | 118 | # drip effect 119 | for i in 0..<1000: 120 | let x = rnd(screenWidth) 121 | let y = rnd(screenHeight) 122 | let c = pget(x,y) 123 | if c != 1 or rnd(10) == 0: 124 | pset(x,y+1,c) 125 | 126 | # draw ball 127 | let d = sqrt(pow(bxv,2.0) + pow(byv,2.0)) 128 | let ballSize = if d > 2.0: 2 elif d > 1.0: 3 elif d > 0.5: 4 else: 5 129 | setColor(t.int) 130 | circfill(bx,by, ballSize) 131 | 132 | # some random dark splats 133 | if rnd(30) == 0: 134 | setColor(1) 135 | let x = rnd(128) 136 | let y = rnd(128) 137 | circfill(x,y,rnd(5)+3) 138 | for i in 0..rnd(30): 139 | circfill(x + rnd(30)-15, y + rnd(30)-15, rnd(2)+1) 140 | 141 | # splats at ball location during impact 142 | if splat: 143 | setColor(t.int) 144 | let x = bx + rnd(10)-5 145 | let y = by + rnd(10)-5 146 | circfill(x,y,rnd(10)+5) 147 | for i in 0..rnd(30): 148 | circfill(x + rnd(30)-15, y + rnd(30)-15, rnd(2)+1) 149 | splat = false 150 | 151 | # initialization 152 | nico.init("nico", "test") 153 | 154 | # we want a fixed sized screen with perfect square pixels 155 | fixedSize(true) 156 | integerScale(true) 157 | # create the window 158 | nico.createWindow("nico",128,128,4) 159 | 160 | # start, say which functions to use for init, update and draw 161 | nico.run(gameInit, gameUpdate, gameDraw) 162 | -------------------------------------------------------------------------------- /examples/palette.nim: -------------------------------------------------------------------------------- 1 | import nico 2 | 3 | 4 | proc gameInit() = 5 | setPalette(loadPalettePico8()) 6 | loadSpriteSheet(0, "spritesheet.png") 7 | loadSpriteSheet(1, "spritesheetRGBA.png") 8 | 9 | proc gameUpdate(dt: float32) = 10 | discard 11 | 12 | proc gameDraw() = 13 | cls() 14 | setSpritesheet(0) 15 | for i in 0..<8: 16 | spr(16+i, i * 8, 0) 17 | setSpritesheet(1) 18 | for i in 0..<8: 19 | spr(16+i, i * 8, 8) 20 | 21 | nico.init("nico","palette") 22 | nico.createWindow("palette", 128, 128, 3) 23 | nico.run(gameInit, gameUpdate, gameDraw) 24 | -------------------------------------------------------------------------------- /examples/platformer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Emscripten-Generated Code 7 | 59 | 60 | 61 |
emscripten
62 |
Downloading...
63 |
64 | 65 |
66 | 67 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /examples/rotation.nim: -------------------------------------------------------------------------------- 1 | import nico 2 | import nico/vec 3 | import math 4 | 5 | var angle = 0f 6 | var mode = 0 7 | var rotFn = sprRot 8 | var pos: Vec2f 9 | 10 | proc gameInit() = 11 | # load all our assets 12 | loadSpriteSheet(0, "rotation.png", 16, 16) 13 | angle = 0f 14 | pos = vec2f(screenWidth div 2, screenHeight div 2) 15 | 16 | proc gameUpdate(dt: Pfloat) = 17 | if btn(pcLeft): 18 | angle -= 3f * dt 19 | if btn(pcRight): 20 | angle += 3f * dt 21 | if btn(pcUp): 22 | pos += angle.angleToVec * 50f * dt 23 | 24 | if btnp(pcA): 25 | mode += 1 26 | if mode > 2: 27 | mode = 0 28 | 29 | if keyp(K_RETURN): 30 | rotFn = if rotFn == sprRot: sprShearRot else: sprRot 31 | 32 | proc gameDraw() = 33 | cls() 34 | 35 | var i = 0 36 | for ty in 0.. bestProj: 76 | bestProj = proj 77 | result = (p, i) 78 | i.inc() 79 | 80 | proc isLeftOf(p: Vec2f, edge: Line): bool = 81 | ## returns true if p is left of edge 82 | let tmp1 = edge[1] - edge[0] 83 | let tmp2 = p - edge[1] 84 | 85 | let x = (tmp1.x * tmp2.y) - (tmp1.y * tmp2.x) 86 | 87 | if x < 0: 88 | return false 89 | elif x > 0: 90 | return true 91 | else: 92 | # colinear points 93 | return false 94 | 95 | proc getIntersect(a,b: Line): Vec2f = 96 | ## returns the intersection of two lines 97 | let adir = a[1] - a[0] 98 | let bdir = b[1] - b[0] 99 | 100 | let dotPerp = (adir.x * bdir.y) - (adir.y * bdir.x) 101 | 102 | if abs(dotPerp) < 0.0001f: 103 | # parallel 104 | return 105 | 106 | let c = b[0] - a[0] 107 | let t = (c.x * bdir.y - c.y * bdir.x) / dotPerp 108 | 109 | return a[0] + (t * adir) 110 | 111 | proc clip(ln: Line, plane: Line): Line = 112 | # returns ln clipped by plane 113 | result = ln 114 | # get intersection of ln and plane 115 | let i = getIntersect(ln, plane) 116 | let aOK = ln[0].isLeftOf(plane) 117 | let bOK = ln[1].isLeftOf(plane) 118 | if not aOK and not bOK: 119 | # both on wrong side of plane 120 | result[0] = i 121 | result[1] = i 122 | elif not aOK: 123 | result[0] = i 124 | result[1] = ln[1] 125 | elif not bOK: 126 | result[0] = ln[0] 127 | result[1] = i 128 | 129 | proc getContactData(a,b: Object, collisionNormal: Vec2f, penetration: float32): ContactData = 130 | ## returns the indices of the significant edges on A and B 131 | let vertA = a.getSupport(collisionNormal) 132 | let vertB = b.getSupport(-collisionNormal) 133 | 134 | var bestAEdgeDot: float32 = -Inf 135 | var bestAEdge: int = -1 136 | var bestBEdgeDot: float32 = -Inf 137 | var bestBEdge: int = -1 138 | 139 | block: 140 | var i = 0 141 | for edge in a.edges: 142 | if vertA[0] in edge: 143 | let d = dot(edge.normal, collisionNormal) 144 | if abs(d) > bestAEdgeDot: 145 | bestAEdgeDot = abs(d) 146 | bestAEdge = i 147 | i.inc() 148 | block: 149 | var i = 0 150 | for edge in b.edges: 151 | if vertB[0] in edge: 152 | let d = dot(edge.normal, -collisionNormal) 153 | if abs(d) > bestBEdgeDot: 154 | bestBEdgeDot = abs(d) 155 | bestBEdge = i 156 | i.inc() 157 | 158 | a.highlightVertIndex = vertA[1] 159 | b.highlightVertIndex = vertB[1] 160 | 161 | a.highlightEdgeIndex = bestAEdge 162 | b.highlightEdgeIndex = bestBEdge 163 | 164 | var reference,incident: Line 165 | var refAdjacentA: Line 166 | var refAdjacentB: Line 167 | 168 | var flipped = false 169 | 170 | if bestAEdgeDot < bestBEdgeDot: 171 | reference = a.edge(bestAEdge) 172 | refAdjacentA = a.edge(bestAEdge-1) 173 | refAdjacentB = a.edge(bestAEdge+1) 174 | a.highlightColor = 12 175 | incident = b.edge(bestBEdge) 176 | b.highlightColor = 8 177 | else: 178 | reference = b.edge(bestBEdge) 179 | refAdjacentA = b.edge(bestBEdge-1) 180 | refAdjacentB = b.edge(bestBEdge+1) 181 | b.highlightColor = 12 182 | incident = a.edge(bestAEdge) 183 | a.highlightColor = 8 184 | flipped = true 185 | 186 | ## We now clip the incident with all the adjacent faces of the reference. This is done by taking the 187 | ## adjacent faces normal and any vertex that it contains to produce a plane equation. 188 | if flipped: 189 | incident = incident.clip(refAdjacentA) 190 | incident = incident.clip(refAdjacentB) 191 | else: 192 | incident = incident.clip(refAdjacentA) 193 | incident = incident.clip(refAdjacentB) 194 | 195 | result.clipEdge = incident 196 | 197 | # final clipping, remove points behind reference 198 | if incident[0].isLeftOf(reference): 199 | result.points[result.nPoints] = incident[0] 200 | result.nPoints += 1 201 | if incident[1].isLeftOf(reference): 202 | result.points[result.nPoints] = incident[1] 203 | result.nPoints += 1 204 | 205 | proc sat(a,b: Object): (bool,Vec2f,float32) = 206 | ## return true if objects are overlapping, if overlapping returns the axis of min overlap 207 | var axisOfMinOverlap: Vec2f 208 | var minOverlap: float32 = Inf 209 | 210 | for axis in getAxes(a,b): 211 | var amin = Inf 212 | var bmin = Inf 213 | var amax = -Inf 214 | var bmax = -Inf 215 | 216 | # project each edge against the current axis 217 | for edge in a.edges: 218 | for p in edge: 219 | var v = dot(p,axis) 220 | if v < amin: 221 | amin = v 222 | if v > amax: 223 | amax = v 224 | for edge in b.edges: 225 | for p in edge: 226 | var v = dot(p,axis) 227 | if v < bmin: 228 | bmin = v 229 | if v > bmax: 230 | bmax = v 231 | 232 | if bmin > amax or amin > bmax: 233 | # found axis of separation, we can exit early 234 | result[0] = false 235 | return 236 | 237 | var overlap = 0f 238 | if amax > bmin: 239 | overlap = abs(bmin - amax) 240 | elif bmax > amin: 241 | overlap = abs(amin - bmax) 242 | 243 | if abs(overlap) < abs(minOverlap): 244 | minOverlap = overlap 245 | axisOfMinOverlap = axis 246 | 247 | return (true, axisOfMinOverlap, minOverlap) 248 | 249 | proc addTorque(self: Object, torque: float32) = 250 | self.avel += torque / self.mass 251 | 252 | proc addForceAtPos(self: Object, force: Vec2f, point: Vec2f) = 253 | self.vel += force / self.mass 254 | self.addTorque(cross(point - self.pos, force)) 255 | 256 | proc update(self: Object, dt: float32) = 257 | self.angle += self.avel * dt 258 | self.pos += self.vel * dt 259 | 260 | self.avel *= 0.9f 261 | self.vel *= 0.999f 262 | 263 | proc draw(self: Object) = 264 | var i = 0 265 | for edge in self.edges: 266 | #if self.highlightEdgeIndex == i: 267 | # setColor(self.highlightColor) 268 | #else: 269 | setColor(self.color) 270 | line(edge[0], edge[1]) 271 | i.inc 272 | 273 | setColor(self.color) 274 | line(self.pos, self.pos + self.angle.angleToVec(10f)) 275 | 276 | setColor(8) 277 | line(self.pos, self.pos + self.vel) 278 | 279 | #if self.highlightVertIndex >= 0: 280 | # setColor(self.highlightColor) 281 | # let p = self.point(self.highlightVertIndex) 282 | # circfill(p.x, p.y, 2) 283 | 284 | 285 | proc gameInit() = 286 | # we want a fixed sized screen with perfect square pixels 287 | fixedSize(true) 288 | integerScale(true) 289 | # create the window 290 | nico.createWindow("nico",128,128,4) 291 | 292 | objs = @[] 293 | objs.add(Object(color: 6, mass: 1f, pos: vec2f(20,64), rawPoints: @[vec2f(-16f, -16f), vec2f(16f, -16f), vec2f(16f, 16f), vec2f(-16f, 16f)])) 294 | objs.add(Object(color: 5, mass: 3f, pos: vec2f(82,64), rawPoints: @[vec2f(-32f, -16f), vec2f(16f, -16f), vec2f(16f, 16f), vec2f(-16f, 16f)])) 295 | objs.add(Object(color: 5, mass: 8f, pos: vec2f(0,100), rawPoints: @[vec2f(-32f, -16f), vec2f(16f, -16f), vec2f(16f, 16f), vec2f(-16f, 16f)])) 296 | 297 | proc gameUpdate(dt: float32) = 298 | if btnp(pcStart): 299 | gameInit() 300 | return 301 | 302 | if btn(pcLeft): 303 | objs[0].addTorque(-30f * dt) 304 | if btn(pcRight): 305 | objs[0].addTorque(30f * dt) 306 | if btn(pcUp): 307 | objs[0].vel += objs[0].angle.angleToVec(36f) * dt 308 | if btn(pcDown): 309 | objs[0].vel -= objs[0].angle.angleToVec(36f) * dt 310 | 311 | for s in 0..<4: 312 | for obj in objs: 313 | obj.update(dt * 0.25f) 314 | 315 | obj.highlightVertIndex = -1 316 | obj.highlightEdgeIndex = -1 317 | 318 | for i in 0.. 2 | 3 | 4 | 5 | 6 | Emscripten-Generated Code 7 | 59 | 60 | 61 |
emscripten
62 |
Downloading...
63 |
64 | 65 |
66 | 67 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /examples/screenscale.nim: -------------------------------------------------------------------------------- 1 | import nico 2 | import strformat 3 | 4 | nico.init("nico","screenscale") 5 | 6 | var test: seq[int] 7 | 8 | proc gameInit() = 9 | discard 10 | 11 | proc gameUpdate(dt: float32) = 12 | if btnp(pcA): 13 | fixedSize(not fixedSize()) 14 | if btnp(pcB): 15 | integerScale(not integerScale()) 16 | 17 | for i in 0..<1000: 18 | test.add(i) 19 | 20 | test = @[] 21 | 22 | 23 | proc gameDraw() = 24 | cls() 25 | 26 | setColor(1) 27 | for y in countup(0, screenHeight, 16): 28 | hline(0, y, screenWidth) 29 | 30 | for x in countup(0, screenWidth, 16): 31 | vline(x, 0, screenHeight) 32 | 33 | setColor(12) 34 | box(0, 0, screenWidth, screenHeight) 35 | 36 | setColor(7) 37 | var y = 4 38 | print(&"canvas : {screenWidth}x{screenHeight}", 4, y) 39 | y += 10 40 | print(&"scale : {getScreenScale()}", 4, y) 41 | y += 10 42 | #print(&"display: {displayWidth}x{displayHeight}", 4, y) 43 | y += 10 44 | print(&"fixedSize: {fixedSize()}", 4, y) 45 | y += 10 46 | print(&"integerScale: {integerScale()}", 4, y) 47 | y += 10 48 | print(&"mem: {getOccupiedMem()} / {getTotalMem()}", 4, y) 49 | 50 | fixedSize(false) 51 | integerScale(true) 52 | 53 | nico.createWindow("screenscale", 128, 128, 3) 54 | 55 | addResizeFunc(proc(w,h: int) = 56 | echo &"resized to {w}x{h}" 57 | ) 58 | 59 | nico.run(gameInit, gameUpdate, gameDraw) 60 | -------------------------------------------------------------------------------- /examples/stencil.nim: -------------------------------------------------------------------------------- 1 | import nico 2 | import nico/vec 3 | import math 4 | 5 | const moveSpeed = 50f 6 | 7 | var debugStencil = false 8 | 9 | var a,b,c: Vec2f 10 | var av,bv,cv: Vec2f 11 | 12 | proc gameInit() = 13 | a = vec2f(rnd(0,screenWidth),rnd(0,screenHeight)) 14 | b = vec2f(rnd(0,screenWidth),rnd(0,screenHeight)) 15 | c = vec2f(rnd(0,screenWidth),rnd(0,screenHeight)) 16 | av = vec2f(rnd(-moveSpeed,moveSpeed),rnd(-moveSpeed,moveSpeed)) 17 | bv = vec2f(rnd(-moveSpeed,moveSpeed),rnd(-moveSpeed,moveSpeed)) 18 | cv = vec2f(rnd(-moveSpeed,moveSpeed),rnd(-moveSpeed,moveSpeed)) 19 | 20 | proc gameUpdate(dt: float32) = 21 | a += av * dt 22 | b += bv * dt 23 | c += cv * dt 24 | 25 | if a.x < 0: 26 | a.x = 0 27 | av.x = -av.x 28 | if a.y < 0: 29 | a.y = 0 30 | av.y = -av.y 31 | if a.x > screenWidth - 1: 32 | a.x = (screenWidth - 1).float32 33 | av.x = -av.x 34 | if a.y > screenHeight - 1: 35 | a.y = (screenHeight - 1).float32 36 | av.y = -av.y 37 | 38 | if b.x < 0: 39 | b.x = 0 40 | bv.x = -bv.x 41 | if b.y < 0: 42 | b.y = 0 43 | bv.y = -bv.y 44 | if b.x > screenWidth - 1: 45 | b.x = (screenWidth - 1).float32 46 | bv.x = -bv.x 47 | if b.y > screenHeight - 1: 48 | b.y = (screenHeight - 1).float32 49 | bv.y = -bv.y 50 | 51 | if c.x < 0: 52 | c.x = 0 53 | cv.x = -cv.x 54 | if c.y < 0: 55 | c.y = 0 56 | cv.y = -cv.y 57 | if c.x > screenWidth - 1: 58 | c.x = (screenWidth - 1).float32 59 | cv.x = -cv.x 60 | if c.y > screenHeight - 1: 61 | c.y = (screenHeight - 1).float32 62 | cv.y = -cv.y 63 | 64 | if btnp(pcA): 65 | debugStencil = not debugStencil 66 | 67 | proc gameDraw() = 68 | stencilClear(0) # clear the stencil buffer to 0 69 | cls() 70 | # draw background 71 | setColor(8) 72 | line(a.x,a.y,b.x,b.y) 73 | line(b.x,b.y,c.x,c.y) 74 | line(c.x,c.y,a.x,a.y) 75 | 76 | # draw the hole of our donut onto the stencil buffer 77 | setColor(-1) # color -1 means write 1 in stencil buffer 78 | circfill(64 + sin(time() / 3f) * 4f,64 + sin(time() / 1.234f) * 4f, 8 + sin(time() / 2.123f) * 4f) 79 | 80 | # draw the rest of the donut 81 | # by default we only draw where the stencil buffer != 0 82 | setColor(7) 83 | circfill(64,64,16.float32 + sin(time() / 2f) * 4f) 84 | 85 | if debugStencil: 86 | # draw blue everywhere where the stencil buffer == 0 87 | setColor(12) 88 | boxfill(0,0,screenWidth,screenHeight) 89 | 90 | setColor(10) 91 | print("stencil buffering with -1 color", 1, 1) 92 | 93 | # initialization 94 | nico.init("nico", "test") 95 | 96 | # we want a fixed sized screen with perfect square pixels 97 | fixedSize(true) 98 | integerScale(true) 99 | # create the window 100 | nico.createWindow("nico",128,128,4) 101 | 102 | # start, say which functions to use for init, update and draw 103 | nico.run(gameInit, gameUpdate, gameDraw) 104 | -------------------------------------------------------------------------------- /examples/stencil2.nim: -------------------------------------------------------------------------------- 1 | import nico 2 | import nico/vec 3 | import math 4 | 5 | const moveSpeed = 50f 6 | 7 | var debugStencil = false 8 | 9 | var a,b,c: Vec2f 10 | var av,bv,cv: Vec2f 11 | 12 | proc gameInit() = 13 | a = vec2f(rnd(0,screenWidth),rnd(0,screenHeight)) 14 | b = vec2f(rnd(0,screenWidth),rnd(0,screenHeight)) 15 | c = vec2f(rnd(0,screenWidth),rnd(0,screenHeight)) 16 | av = vec2f(rnd(-moveSpeed,moveSpeed),rnd(-moveSpeed,moveSpeed)) 17 | bv = vec2f(rnd(-moveSpeed,moveSpeed),rnd(-moveSpeed,moveSpeed)) 18 | cv = vec2f(rnd(-moveSpeed,moveSpeed),rnd(-moveSpeed,moveSpeed)) 19 | 20 | proc gameUpdate(dt: float32) = 21 | a += av * dt 22 | b += bv * dt 23 | c += cv * dt 24 | 25 | if a.x < 0: 26 | a.x = 0 27 | av.x = -av.x 28 | if a.y < 0: 29 | a.y = 0 30 | av.y = -av.y 31 | if a.x > screenWidth - 1: 32 | a.x = (screenWidth - 1).float32 33 | av.x = -av.x 34 | if a.y > screenHeight - 1: 35 | a.y = (screenHeight - 1).float32 36 | av.y = -av.y 37 | 38 | if b.x < 0: 39 | b.x = 0 40 | bv.x = -bv.x 41 | if b.y < 0: 42 | b.y = 0 43 | bv.y = -bv.y 44 | if b.x > screenWidth - 1: 45 | b.x = (screenWidth - 1).float32 46 | bv.x = -bv.x 47 | if b.y > screenHeight - 1: 48 | b.y = (screenHeight - 1).float32 49 | bv.y = -bv.y 50 | 51 | if c.x < 0: 52 | c.x = 0 53 | cv.x = -cv.x 54 | if c.y < 0: 55 | c.y = 0 56 | cv.y = -cv.y 57 | if c.x > screenWidth - 1: 58 | c.x = (screenWidth - 1).float32 59 | cv.x = -cv.x 60 | if c.y > screenHeight - 1: 61 | c.y = (screenHeight - 1).float32 62 | cv.y = -cv.y 63 | 64 | if btnp(pcA): 65 | debugStencil = not debugStencil 66 | 67 | proc gameDraw() = 68 | stencilClear(0) # clear the stencil buffer to 0 69 | cls() 70 | # draw background 71 | setColor(8) 72 | line(a.x,a.y,b.x,b.y) 73 | line(b.x,b.y,c.x,c.y) 74 | line(c.x,c.y,a.x,a.y) 75 | 76 | # draw the hole of our donut onto the stencil buffer 77 | setColor(-1) # color -1 means write 1 in stencil buffer 78 | circfill(64 + sin(time() / 3f) * 4f,64 + sin(time() / 1.234f) * 4f, 8 + sin(time() / 2.123f) * 4f) 79 | 80 | # draw the rest of the donut 81 | # by default we only draw where the stencil buffer != 0 82 | setColor(7) 83 | circfill(64,64,16.float32 + sin(time() / 2f) * 4f) 84 | 85 | if debugStencil: 86 | # draw blue everywhere where the stencil buffer == 0 87 | setColor(12) 88 | boxfill(0,0,screenWidth,screenHeight) 89 | 90 | setColor(10) 91 | print("stencil buffering with -1 color", 1, 1) 92 | 93 | # initialization 94 | nico.init("nico", "test") 95 | 96 | # we want a fixed sized screen with perfect square pixels 97 | fixedSize(true) 98 | integerScale(true) 99 | # create the window 100 | nico.createWindow("nico",128,128,4) 101 | 102 | # start, say which functions to use for init, update and draw 103 | nico.run(gameInit, gameUpdate, gameDraw) 104 | -------------------------------------------------------------------------------- /examples/textinput.nim: -------------------------------------------------------------------------------- 1 | import nico 2 | 3 | var textInputString: string 4 | var textInputEventListener: EventListener 5 | 6 | proc gameInit() = 7 | textInputString = "" 8 | textInputEventListener = addEventListener(proc(ev: Event): bool = 9 | if ev.kind == ekTextInput: 10 | textInputString &= ev.text 11 | ) 12 | startTextInput() 13 | 14 | proc gameUpdate(dt: float32) = 15 | discard 16 | 17 | proc gameDraw() = 18 | cls() 19 | setColor(6) 20 | print("TYPE SOMETHING",1,1) 21 | setColor(7) 22 | print(textInputString,1,10) 23 | 24 | # initialization 25 | nico.init("nico", "test") 26 | 27 | # we want a fixed sized screen with perfect square pixels 28 | fixedSize(true) 29 | integerScale(true) 30 | # create the window 31 | nico.createWindow("nico",128,128,4) 32 | 33 | # start, say which functions to use for init, update and draw 34 | nico.run(gameInit, gameUpdate, gameDraw) 35 | -------------------------------------------------------------------------------- /examples/texture.nim: -------------------------------------------------------------------------------- 1 | import nico 2 | import nico/vec 3 | 4 | # frame counter 5 | var frame = 0 6 | 7 | var verts: array[4, Vec4f] 8 | var currentPoint: ptr Vec4f 9 | 10 | proc tquadfill(a,b,c,d: Vec4f) = 11 | tquadfill(a.x,a.y,a.z,a.w, b.x,b.y,b.z,b.w, c.x,c.y,c.z,c.w, d.x,d.y,d.z,d.w) 12 | 13 | proc tquadfill(verts: array[4, Vec4f]) = 14 | tquadfill(verts[0], verts[1], verts[2], verts[3]) 15 | 16 | proc gameInit() = 17 | setPalette(loadPalettePico8Extra()) 18 | loadSpriteSheet(0, "spritesheet.png", 8, 8) 19 | 20 | verts[0] = vec4f( 0, 0, 0.01f, 16.01f) 21 | verts[1] = vec4f(64, 0, 15.99f, 16.01f) 22 | verts[2] = vec4f(64, 64, 15.99f, 31.99f) 23 | verts[3] = vec4f( 0, 64, 0.01f, 31.99f) 24 | 25 | proc gameUpdate(dt: Pfloat) = 26 | frame.inc 27 | 28 | let (mx,my) = mouse() 29 | if mousebtnp(0): 30 | let mv = vec2f(mx,my) 31 | for v in verts.mitems: 32 | if nearer(mv, v.xy, 8f): 33 | currentPoint = v.addr 34 | elif mousebtn(0): 35 | if currentPoint != nil: 36 | currentPoint[].x = mx 37 | currentPoint[].y = my 38 | 39 | proc gameDraw() = 40 | cls() 41 | 42 | tquadfill(verts) 43 | 44 | for p in verts.mitems: 45 | setColor(if p.addr == currentPoint: 7 else: 5) 46 | circ(p.x,p.y,3) 47 | 48 | # initialization 49 | nico.init("nico", "test") 50 | 51 | # we want a fixed sized screen with perfect square pixels 52 | fixedSize(true) 53 | integerScale(true) 54 | # create the window 55 | nico.createWindow("nico",128,128,4) 56 | 57 | # start, say which functions to use for init, update and draw 58 | nico.run(gameInit, gameUpdate, gameDraw) 59 | -------------------------------------------------------------------------------- /examples/tweaker.nim: -------------------------------------------------------------------------------- 1 | import nico, nico/[tweaks, gui2] 2 | import macros 3 | 4 | tweaks("Main"): 5 | tweaks("Color"): 6 | tweak(color, 0, 0 .. 15) 7 | tweak(rotation, 0f, 0f..360f) 8 | tweak(drawing, false) 9 | 10 | var someVal = 0 11 | tweaks("otherPane"): 12 | addTweak("GivenName", someVal, -10..10) 13 | 14 | proc gameInit() = 15 | setPalette(loadPalettePico8()) 16 | 17 | proc gameUpdate(dt: float32) = 18 | guiStartFrame() 19 | guiPos(screenWidth div 2, 0) 20 | guiSize(screenWidth div 2, screenHeight) 21 | tweaksGUI2() 22 | 23 | proc gameDraw() = 24 | cls() 25 | guiDraw() 26 | 27 | nico.init("nico","tweaks") 28 | nico.createWindow("tweaker", 256, 256, 3) 29 | nico.run(gameInit, gameUpdate, gameDraw) 30 | -------------------------------------------------------------------------------- /examples/vertex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Emscripten-Generated Code 7 | 59 | 60 | 61 |
emscripten
62 |
Downloading...
63 |
64 | 65 |
66 | 67 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /examples/webrtc/Makefile: -------------------------------------------------------------------------------- 1 | APPNAME = app 2 | NIMC=nim c 3 | NIMJS=nim js 4 | SOURCES = $(shell ls src/*.nim) 5 | BUILDARGS = 6 | RELEASE_ARGS = -d:release 7 | DEBUG_ARGS = -d:debug 8 | 9 | release: $(SOURCES) 10 | ${NIMC} $(RELEASE_ARGS) $(BUILDARGS) -o:${APPNAME} src/main.nim 11 | 12 | debug: $(SOURCES) 13 | ${NIMC} $(DEBUG_ARGS) $(BUILDARGS) -o:${APPNAME}_debug src/main.nim 14 | 15 | run: release 16 | ./${APPNAME} 17 | 18 | rund: debug 19 | ./${APPNAME}_debug 20 | 21 | web: $(SOURCES) 22 | ${NIMJS} $(RELEASE_ARGS) $(BUILDARGS) -o:main.js src/main.nim 23 | 24 | webd: $(SOURCES) 25 | ${NIMJS} $(DEBUG_ARGS) $(BUILDARGS) -o:main.js src/main.nim 26 | 27 | .PHONY: release debug run rund web webd 28 | -------------------------------------------------------------------------------- /examples/webrtc/assets/font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftsf/nico/409eec1eeab05f5eb07d17343992638ce37f25ed/examples/webrtc/assets/font.png -------------------------------------------------------------------------------- /examples/webrtc/assets/font.png.dat: -------------------------------------------------------------------------------- 1 | !"#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{}~ -------------------------------------------------------------------------------- /examples/webrtc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | Nico Example App 19 | 20 | 21 |
22 | 23 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /examples/webrtc/src/main.nim: -------------------------------------------------------------------------------- 1 | import nico 2 | import nico/vec 3 | import peerjs 4 | import jsffi 5 | import dom 6 | import strutils 7 | import strscans 8 | 9 | var conn: DataConnection 10 | var peer: Peer 11 | var myID: string 12 | 13 | const idPrefix = "nicoTest" 14 | const idLen = 4 15 | 16 | var remoteID: array[idLen, int] 17 | var remoteIDindex: int 18 | 19 | var frame: uint 20 | 21 | var connecting = 0 22 | var connected = false 23 | var isHost = false 24 | 25 | var playerPos: array[2, Vec2i] 26 | var playerScores: array[2, int] 27 | var playerId: range[0..1] 28 | 29 | var ballPos: Vec2f 30 | var ballVel: Vec2f 31 | 32 | var paddleWidth = 16 33 | var pauseTimer = 1.0'f 34 | var ballSpeed: float32 = 0.5'f 35 | var resetTimer = 0'f 36 | var lastMessageTimer: int 37 | var lastPingTimer: int 38 | 39 | var charSet: seq[char] 40 | for c in '0'..'9': 41 | charSet.add(c) 42 | 43 | proc overlap(ax,ay,aw,ah, bx,by,bw,bh: Pint): bool = 44 | let aminx = ax 45 | let amaxx = ax + aw - 1 46 | let aminy = ay 47 | let amaxy = ay + ah - 1 48 | 49 | let bminx = bx 50 | let bmaxx = bx + bw - 1 51 | let bminy = by 52 | let bmaxy = by + bh - 1 53 | return not (aminx >= bmaxx or amaxx <= bminx or aminy >= bmaxy or amaxy <= bminy) 54 | 55 | proc drawPaddle(x,y,w,h,c: int) = 56 | setColor(c) 57 | circfill(x - w div 2 + 2, y, 2) 58 | circfill(x + w div 2 - 2, y, 2) 59 | boxfill(x - w div 2 + 2, y - h div 2, w - 4, h) 60 | 61 | proc handleData(d: string) = 62 | var i: int 63 | var pid, score: int 64 | if scanf(d, "bx:$i", i): 65 | ballPos.x = i.float32 66 | elif scanf(d, "by:$i", i): 67 | ballPos.y = i.float32 68 | elif scanf(d, "p:$i", i): 69 | playerPos[wrap(playerId + 1, 2)].x = i 70 | elif scanf(d, "stop"): 71 | pauseTimer = 1'f 72 | resetTimer = 1'f 73 | elif scanf(d, "start"): 74 | pauseTimer = 0'f 75 | resetTimer = 0'f 76 | elif scanf(d, "score:$i:$i", pid, score): 77 | playerScores[pid] = score 78 | lastMessageTimer = 0 79 | 80 | proc resetBall() = 81 | ballPos = vec2f(64,64) 82 | ballVel = vec2f(rnd(-1'f,1'f), rnd([-1'f,1'f])) 83 | ballVel = ballVel.clamp(ballSpeed) 84 | ballSpeed = 1.0'f 85 | conn.send("stop") 86 | conn.send("bx:" & $ballPos.x.int) 87 | conn.send("by:" & $ballPos.y.int) 88 | 89 | proc gameInit() = 90 | srand() 91 | loadFont(0, "font.png") 92 | 93 | myID = "" 94 | for i in 0.. 0: 134 | connecting -= 1 135 | if connecting == 0: 136 | connected = false 137 | return 138 | 139 | if not connected: 140 | # remote id entry 141 | if btnpr(pcLeft): 142 | remoteIDindex -= 1 143 | if btnpr(pcRight): 144 | remoteIDindex += 1 145 | remoteIDindex = wrap(remoteIDindex, remoteID.len) 146 | if btnpr(pcDown): 147 | remoteID[remoteIDindex].dec() 148 | if remoteID[remoteIDindex] < charSet.low: 149 | remoteID[remoteIDindex] = charSet.high 150 | if btnpr(pcUp): 151 | remoteID[remoteIDindex].inc() 152 | if remoteID[remoteIDindex] > charSet.high: 153 | remoteID[remoteIDindex] = charSet.low 154 | if btnp(pcA): 155 | connecting = 60 156 | var remoteIDtext: string 157 | for i,c in remoteID: 158 | remoteIDtext.add(charSet[c]) 159 | conn = peer.connect(idPrefix & remoteIDtext) 160 | conn.on("open", proc(data: cstring) = 161 | echo "connected" 162 | setWindowTitle("nicoTest " & myID & " - client") 163 | connected = true 164 | connecting = 0 165 | playerId = 1 166 | conn.on("data", proc(data: cstring) = 167 | handleData($data) 168 | ) 169 | lastMessageTimer = 0 170 | ) 171 | elif connected: 172 | lastMessageTimer += 1 173 | if lastMessageTimer > 120: 174 | connected = false 175 | #peer.reconnect() 176 | 177 | if not isHost: 178 | lastPingTimer += 1 179 | if lastPingTimer > 60: 180 | conn.send("ping") 181 | 182 | var dirty = false 183 | if btn(pcLeft): 184 | playerPos[playerId].x -= 1 185 | dirty = true 186 | if btn(pcRight): 187 | playerPos[playerId].x += 1 188 | dirty = true 189 | 190 | playerPos[playerId].x = clamp(playerPos[playerId].x, paddleWidth div 2, screenWidth - paddleWidth div 2) 191 | 192 | if dirty: 193 | conn.send("p:" & $playerPos[playerId].x) 194 | 195 | if isHost: 196 | if pauseTimer > 0: 197 | pauseTimer -= dt 198 | if pauseTimer <= 0: 199 | resetBall() 200 | resetTimer = 1.0'f 201 | 202 | if resetTimer > 0: 203 | resetTimer -= dt 204 | if resetTimer <= 0: 205 | conn.send("start") 206 | 207 | var lastPos = ballPos.vec2i 208 | 209 | playerPos[0].x = clamp(playerPos[0].x, paddleWidth div 2, screenWidth - paddleWidth div 2) 210 | playerPos[1].x = clamp(playerPos[1].x, paddleWidth div 2, screenWidth - paddleWidth div 2) 211 | 212 | if pauseTimer <= 0 and resetTimer <= 0: 213 | ballPos += ballVel 214 | 215 | if ballPos.x < 2 and ballVel.x < 0: 216 | ballPos.x = 2 217 | ballVel.x = -ballVel.x 218 | if ballPos.x > screenWidth - 2 and ballVel.x > 0: 219 | ballPos.x = (screenWidth - 2).float32 220 | ballVel.x = -ballVel.x 221 | 222 | if overlap(ballPos.x - 2, ballPos.y - 2, 4, 4, playerPos[0].x - paddleWidth div 2, playerPos[0].y - 3, paddleWidth, 5): 223 | if ballVel.y < 0: 224 | ballVel.y = -ballVel.y 225 | ballPos.y = playerPos[0].y.float32 + 4.0'f 226 | let relx = (ballPos.x - playerPos[0].x).float32 227 | if abs(relx) >= paddleWidth: 228 | ballVel.x += relx * 0.25'f 229 | ballVel = ballVel.clamp(ballSpeed) 230 | 231 | ballVel *= 2.0'f 232 | ballSpeed *= 1.1'f 233 | ballVel = ballVel.clamp(ballSpeed) 234 | 235 | if overlap(ballPos.x - 2, ballPos.y - 2, 4, 4, playerPos[1].x - paddleWidth div 2, playerPos[1].y - 3, paddleWidth, 5): 236 | if ballVel.y > 0: 237 | ballVel.y = -ballVel.y 238 | ballPos.y = playerPos[1].y.float32 - 4.0'f 239 | let relx = (ballPos.x - playerPos[1].x).float32 240 | if abs(relx) >= paddleWidth: 241 | ballVel.x += relx * 0.25'f 242 | ballVel = ballVel.clamp(ballSpeed) 243 | 244 | ballVel *= 2.0'f 245 | ballSpeed *= 1.1'f 246 | ballVel = ballVel.clamp(ballSpeed) 247 | 248 | if ballPos.y < 2 and ballVel.y < 0: 249 | playerScores[1] += 1 250 | conn.send("score:0:" & $playerScores[0]) 251 | conn.send("score:1:" & $playerScores[1]) 252 | pauseTimer = 1'f 253 | conn.send("stop") 254 | 255 | if ballPos.y > screenHeight - 2 and ballVel.y > 0: 256 | playerScores[0] += 1 257 | conn.send("score:0:" & $playerScores[0]) 258 | conn.send("score:1:" & $playerScores[1]) 259 | pauseTimer = 1'f 260 | conn.send("stop") 261 | 262 | var nextPos = ballPos.vec2i 263 | if lastPos != nextPos: 264 | conn.send("bx:" & $nextPos.x) 265 | conn.send("by:" & $nextPos.y) 266 | 267 | proc gameDraw() = 268 | frame.inc() 269 | cls() 270 | setColor(7) 271 | if not connected and connecting == 0: 272 | var y = 32 273 | print("local id:", screenWidth div 2, y) 274 | y += 12 275 | print(myID, screenWidth div 2, y, 2) 276 | y += 24 277 | 278 | setColor(8) 279 | print("remote id:", screenWidth div 2, y) 280 | y += 12 281 | var remoteIDtext: string 282 | for i,c in remoteID: 283 | remoteIDtext.add(charSet[c]) 284 | 285 | for i,c in remoteIDtext: 286 | if not connected: 287 | setColor(if i == remoteIDindex: 8 else: 5) 288 | glyph(c, screenWidth div 2 + i * 8, y, 2) 289 | 290 | y += 24 291 | 292 | setColor(5) 293 | print("enter your friend's\nlocal id with arrows\npress Z to connect", 4, y) 294 | 295 | if connecting > 0: 296 | printc("connecting...", screenWidth div 2, screenHeight - 12) 297 | 298 | if connected: 299 | setColor(1) 300 | rectfill(0,0,screenWidth-1,3) 301 | setColor(3) 302 | rectfill(0,screenHeight-4,screenWidth-1,screenHeight-1) 303 | 304 | var topPos = 8 305 | var bottomPos = screenHeight - 9 306 | 307 | drawPaddle(playerPos[0].x, playerPos[0].y, paddleWidth, 5, 11) 308 | drawPaddle(playerPos[1].x, playerPos[1].y, paddleWidth, 5, 12) 309 | 310 | setColor(if (pauseTimer > 0 or resetTimer > 0) and frame mod 10 < 5: 1 else: 7) 311 | circfill(ballPos.x, ballPos.y, 2) 312 | 313 | if pauseTimer > 0 or resetTimer > 0: 314 | setColor(11) 315 | printc($playerScores[0], screenWidth div 2, screenHeight div 2 - 32) 316 | setColor(12) 317 | printc($playerScores[1], screenWidth div 2, screenHeight div 2 + 32) 318 | 319 | nico.init("myOrg", "myApp") 320 | nico.fixedSize(true) 321 | nico.integerScale(true) 322 | nico.createWindow("myApp", 128, 128, 4, false) 323 | nico.run(gameInit, gameUpdate, gameDraw) 324 | -------------------------------------------------------------------------------- /examples/webrtc/src/peerjs.nim: -------------------------------------------------------------------------------- 1 | import dom 2 | import jsffi 3 | 4 | type 5 | EventEmitter* {.importjs:"EventEmitter".} = ref object of RootObj 6 | Peer* {.importjs:"Peer".} = ref object of EventEmitter 7 | connections*: seq[DataConnection] 8 | PeerOptions* = object 9 | debug*: int 10 | DataConnection* {.importjs:"DataConnection".} = ref object of EventEmitter 11 | peer*: cstring 12 | 13 | proc newPeer*(): Peer {.importjs:"new Peer(@)".} 14 | proc newPeer*(id: cstring): Peer {.importjs:"new Peer(@)".} 15 | proc newPeer*(options: PeerOptions): Peer {.importjs:"new Peer(@)".} 16 | proc newPeer*(id: cstring, options: PeerOptions): Peer {.importjs:"new Peer(@)".} 17 | #proc on*(self: Peer, event: string, callback: proc(data: string), context: JsObject = nil) {.importjs:"#.on(@)".} 18 | proc addListener*(self: EventEmitter, event: string, fn: proc(data: string), context: JsObject = nil, once: bool = false): EventEmitter {.importjs:"#.addListener(@)", discardable.} 19 | proc on*(self: Peer, event: cstring, callback: proc(data: JsObject), context: JsObject = nil): EventEmitter {.importjs:"#.on(@)", discardable.} 20 | 21 | proc connect*(self: Peer, id: cstring): DataConnection {.importjs:"#.connect(@)".} 22 | proc reconnect*(self: Peer) {.importjs:"#.reconnect(@)".} 23 | proc on*(self: DataConnection, event: cstring, callback: proc(data: cstring)) {.importjs:"#.on(@)".} 24 | proc send*(self: DataConnection, data: cstring) {.importjs:"#.send(@)".} 25 | -------------------------------------------------------------------------------- /nico.nim.cfg: -------------------------------------------------------------------------------- 1 | gcc.options.always = "-O2" 2 | nico.options.always = "-O2" 3 | -------------------------------------------------------------------------------- /nico.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.4.10" 4 | author = "Jez 'Impbox' Kabanov" 5 | description = "Nico Game Engine" 6 | license = "MIT" 7 | 8 | # Dependencies 9 | 10 | requires "nim >= 1.4.0" 11 | requires "sdl2_nim >= 2.0.14.2" 12 | requires "gifenc >= 0.1.0" 13 | requires "nimPNG >= 0.3.1" 14 | requires "zippy >= 0.5.9" 15 | 16 | skipDirs = @["examples","tests","android","tools"] 17 | installDirs = @["exampleApp"] 18 | installExt = @["nim"] 19 | 20 | import os 21 | import strformat 22 | import sugar 23 | 24 | bin = @["nicoboot","nicoandroid","nicosynth"] 25 | 26 | let tests = collect(newSeq): 27 | for file in listFiles("tests"): 28 | if file.endswith(".nim"): file 29 | 30 | task test, "run tests": 31 | for file in tests: 32 | exec &"nim c -p:. -r {file}" 33 | 34 | task docs, "Generate documentation": 35 | exec "nim doc -p:. --git.url:https://github.com/ftsf/nico --git.commit:main --project --outdir:docs nico.nim" 36 | exec "echo \"\" >> docs/index.html" 37 | 38 | task testemscripten, "compile tests with emscripten": 39 | # test they compile with emscripten backend, harder to test running 40 | for file in tests: 41 | exec &"nim c -d:emscripten -p:. {file}" 42 | 43 | task paintout, "compile paintout example": 44 | exec "nim c -p:. -d:debug examples/paintout.nim" 45 | 46 | task platformer, "compile platformer example": 47 | exec "nim c -p:. -d:release --multimethods:on -o:examples/platformer examples/platformer.nim" 48 | 49 | task audio, "compile audio example": 50 | exec "nim c -p:. -d:debug -o:examples/audio examples/audio.nim" 51 | 52 | task vertex, "compile vertex example": 53 | exec "nim c -p:. -d:debug -o:examples/vertex examples/vertex.nim" 54 | 55 | task gui, "compile gui example": 56 | exec "nim c -p:. -d:debug -o:examples/gui examples/gui.nim" 57 | 58 | task guiweb, "compile gui example": 59 | exec "nim c -d:emscripten -p:. -o:examples/gui.js examples/gui2.nim" 60 | 61 | task coro, "compile coro example": 62 | exec "nim c -p:. -d:debug -o:examples/gui examples/coro.nim" 63 | 64 | task benchmark, "compile benchmark example": 65 | exec "nim c -p:. -d:release -d:danger -o:examples/benchmark examples/benchmark.nim" 66 | 67 | task tweaker, "compile tweaker example": 68 | exec "nim c -p:. -d:release -d:danger -o:examples/tweaker examples/tweaker.nim" 69 | 70 | task examples, "compile all examples": 71 | exec "nimble paintout" 72 | exec "nimble platformer" 73 | exec "nimble audio" 74 | exec "nimble vertex" 75 | exec "nimble gui" 76 | exec "nimble benchmark" 77 | exec "nimble coro" 78 | exec "nimble tweaks" 79 | 80 | task nicosynth, "runs nicosynth": 81 | exec "nim c -r -p:. -d:release -o:tools/nicosynth tools/nicosynth.nim" 82 | 83 | task nicosynthWeb, "builds nicosynth for web": 84 | exec "nim c -d:emscripten -p:. -d:release -o:tools/nicosynth.html tools/nicosynth.nim" 85 | -------------------------------------------------------------------------------- /nico/console.nim: -------------------------------------------------------------------------------- 1 | import nico 2 | import strutils 3 | import tables 4 | 5 | var consoleBG: ColorId = 9 6 | var consoleFG: ColorId = 1 7 | var consoleRows = 5 8 | 9 | proc setConsoleBG*(bg: ColorId) = 10 | consoleBG = bg 11 | 12 | proc setConsoleFG*(fg: ColorId) = 13 | consoleFG = fg 14 | 15 | proc setConsoleRows*(rows: int) = 16 | consoleRows = rows 17 | 18 | var showConsole = false 19 | var viewIndex = 0 20 | 21 | type ConsoleCommandCallback* = proc(args: seq[string]): seq[string] 22 | 23 | var commands = newTable[string, ConsoleCommandCallback]() 24 | 25 | var consoleHistory = newSeq[string]() 26 | var historyIndex: int = 0 27 | var consoleBuffer = newSeq[string]() 28 | var inputBuffer = "" 29 | consoleBuffer.add("") 30 | 31 | proc consoleKeyListener(sym: int, mods: uint16, scancode:int, down: bool): bool 32 | 33 | nico.addKeyListener(consoleKeyListener) 34 | 35 | proc consoleKeyListener(sym: int, mods: uint16, scancode:int, down: bool): bool = 36 | when defined(js): 37 | return false 38 | else: 39 | if sym == 96 and down: 40 | showConsole = not showConsole 41 | return true 42 | 43 | if showConsole: 44 | if down: 45 | if sym == 13: 46 | # enter: do command 47 | if inputBuffer == "": 48 | return true 49 | consoleHistory.add(inputBuffer) 50 | historyIndex = 0 51 | let cmd = inputBuffer.split(' ') 52 | consoleBuffer.add("> " & inputBuffer) 53 | inputBuffer = "" 54 | viewIndex = 0 55 | if cmd[0] in commands: 56 | let args = if cmd.len > 1: cmd[1..^1] else: @[] 57 | let output = commands[cmd[0]](args) 58 | for line in output: 59 | consoleBuffer.add(line) 60 | else: 61 | consoleBuffer.add("unknown command: " & cmd[0]) 62 | return true 63 | elif sym == 8: 64 | # handle backspace 65 | if inputBuffer.len > 0: 66 | inputBuffer = inputBuffer[0..^2] 67 | return true 68 | elif sym == 32: 69 | # handle space 70 | inputBuffer.add(' ') 71 | return true 72 | 73 | elif scancode == SCANCODE_PAGEUP.int: 74 | if (mods and KMOD_CTRL.uint16) != 0 and consoleRows > 1: 75 | consoleRows -= 1 76 | return true 77 | viewIndex += 1 78 | if viewIndex > consoleBuffer.high: 79 | viewIndex = consoleBuffer.high 80 | return true 81 | 82 | elif scancode == SCANCODE_PAGEDOWN.int: 83 | if (mods and KMOD_CTRL.uint16) != 0: 84 | consoleRows += 1 85 | return true 86 | viewIndex -= 1 87 | if viewIndex < 0: 88 | viewIndex = 0 89 | return true 90 | 91 | elif scancode == SCANCODE_UP.int: 92 | if consoleHistory.len > 0: 93 | inputBuffer = consoleHistory[consoleHistory.high - historyIndex] 94 | historyIndex += 1 95 | if historyIndex > consoleHistory.high: 96 | historyIndex = 0 97 | return true 98 | elif scancode == SCANCODE_DOWN.int: 99 | if consoleHistory.len > 0: 100 | inputBuffer = consoleHistory[consoleHistory.high - historyIndex] 101 | historyIndex -= 1 102 | if historyIndex < 0: 103 | historyIndex = 0 104 | inputBuffer = "" 105 | return true 106 | try: 107 | if (mods and KMOD_CTRL.uint16) != 0: 108 | return false 109 | # enter the character, apply shifting 110 | let c = if ((mods and 1.uint16) != 0) or ((mods and 2.uint16) != 0): chr(sym).toUpperAscii else: chr(sym) 111 | if c.isAlphaNumeric or c.isSpaceAscii or c == '.': 112 | inputBuffer.add(c) 113 | return true 114 | except: 115 | debug "unhandled key: ", sym 116 | discard 117 | return false 118 | 119 | proc drawConsole*() = 120 | if showConsole: 121 | if consoleBuffer.len >= 100: 122 | consoleBuffer = consoleBuffer[consoleBuffer.high-99..consoleBuffer.high] 123 | 124 | setColor(consoleBG) 125 | rectfill(0,0,screenWidth, consoleRows * 7) 126 | setColor(consoleFG) 127 | hline(0, consoleRows * 7 - 1, screenWidth) 128 | 129 | var y = 1 130 | 131 | var startLine = max(consoleBuffer.high - (consoleRows-2) - viewIndex, 0) 132 | var endLine = min(startLine + consoleRows - 2, consoleBuffer.high) 133 | 134 | setColor(consoleFG) 135 | for i, line in consoleBuffer[startLine..endLine]: 136 | print(line, 1, y) 137 | y += 6 138 | 139 | print("> " & inputBuffer, 1, y) 140 | print($viewIndex, screenWidth - 10, 1) 141 | 142 | proc registerConsoleCommand*(cmd: string, callback: ConsoleCommandCallback) = 143 | commands[cmd] = callback 144 | 145 | proc unregisterConsoleCommand*(cmd: string) = 146 | commands.del(cmd) 147 | 148 | proc consoleLog*(args: varargs[string, `$`]) = 149 | consoleBuffer.add(join(args, ", ")) 150 | 151 | # example quit command 152 | registerConsoleCommand("quit") do(args: seq[string]) -> seq[string]: 153 | quit() 154 | -------------------------------------------------------------------------------- /nico/controller.nim: -------------------------------------------------------------------------------- 1 | import sdl2_nim/sdl 2 | 3 | type NicoControllerKind* = enum 4 | Keyboard 5 | Gamepad 6 | 7 | type NicoAxis* = enum 8 | pcXAxis 9 | pcYAxis 10 | pcXAxis2 11 | pcYAxis2 12 | pcLTrigger 13 | pcRTrigger 14 | 15 | type NicoButton* = enum 16 | pcLeft = "Left" 17 | pcRight = "Right" 18 | pcUp = "Up" 19 | pcDown = "Down" 20 | pcA = "A" 21 | pcB = "B" 22 | pcX = "X" 23 | pcY = "Y" 24 | pcL1 = "L1" 25 | pcL2 = "L2" 26 | pcL3 = "L3" 27 | pcR1 = "R1" 28 | pcR2 = "R2" 29 | pcR3 = "R3" 30 | pcStart = "Start" 31 | pcBack = "Back" 32 | 33 | type NicoController* = ref object 34 | kind*: NicoControllerKind 35 | name*: string 36 | when not defined(js): 37 | sdlController*: GameController # nil for keyboard 38 | id*: int # -1 for keyboard 39 | axes*: array[NicoAxis, tuple[current, previous: float, hold: int]] 40 | buttons*: array[NicoButton, int] 41 | deadzone*: float 42 | invertX*: bool 43 | invertY*: bool 44 | useRightStick*: bool 45 | 46 | proc newNicoController*(id: cint): NicoController = 47 | result = new(NicoController) 48 | result.id = id 49 | if id > -1: 50 | when not defined(js): 51 | result.sdlController = gameControllerOpen(id) 52 | if result.sdlController == nil: 53 | raise newException(Exception, "error opening game controller: " & $id) 54 | result.name = $result.sdlController.gameControllerName() 55 | result.kind = Gamepad 56 | result.deadzone = 0.50 57 | else: 58 | result.kind = Keyboard 59 | result.name = "KEYBOARD" 60 | echo "added game controller: ", id, ": ", result.kind, ": ", result.name 61 | 62 | proc update*(self: NicoController) = 63 | for i in self.buttons.low..self.buttons.high: 64 | if self.kind == Gamepad: 65 | if i == pcL2: 66 | if self.axes[pcLTrigger].current > self.deadzone: 67 | self.buttons[i] += 1 68 | else: 69 | self.buttons[i] = 0 70 | elif i == pcR2: 71 | if self.axes[pcRTrigger].current > self.deadzone: 72 | self.buttons[i] += 1 73 | else: 74 | self.buttons[i] = 0 75 | 76 | if self.buttons[i] == -1: 77 | self.buttons[i] = 0 78 | 79 | elif self.buttons[i] == -2: 80 | self.buttons[i] = -1 81 | 82 | if self.buttons[i] >= 1: 83 | self.buttons[i] += 1 84 | 85 | for i in self.axes.low..self.axes.high: 86 | if self.axes[i].previous < -self.deadzone and self.axes[i].current < -self.deadzone: 87 | self.axes[i].hold += 1 88 | elif self.axes[i].previous > self.deadzone and self.axes[i].current > self.deadzone: 89 | self.axes[i].hold += 1 90 | else: 91 | self.axes[i].hold = 0 92 | 93 | proc postUpdate*(self: NicoController) = 94 | for i in self.axes.low..self.axes.high: 95 | self.axes[i].previous = self.axes[i].current 96 | 97 | proc axis*(self: NicoController, axis: NicoAxis): float = 98 | return self.axes[axis].current 99 | 100 | proc axisp*(self: NicoController, axis: NicoAxis, value: float): bool = 101 | if value < 0: 102 | return self.axes[axis].current < -self.deadzone and not (self.axes[axis].previous < -self.deadzone) 103 | elif value > 0: 104 | return self.axes[axis].current > self.deadzone and not (self.axes[axis].previous > self.deadzone) 105 | else: 106 | return abs(self.axes[axis].current) < self.deadzone and not (abs(self.axes[axis].previous) < self.deadzone) 107 | 108 | proc btn*(self: NicoController, button: NicoButton): bool = 109 | if button == pcLeft: 110 | return self.buttons[button] > 0 or self.axis(pcXAxis) < -self.deadzone 111 | elif button == pcRight: 112 | return self.buttons[button] > 0 or self.axis(pcXAxis) > self.deadzone 113 | elif button == pcUp: 114 | return self.buttons[button] > 0 or self.axis(pcYAxis) < -self.deadzone 115 | elif button == pcDown: 116 | return self.buttons[button] > 0 or self.axis(pcYAxis) > self.deadzone 117 | return self.buttons[button] > 0 118 | 119 | proc btnp*(self: NicoController, button: NicoButton): bool = 120 | if button == pcLeft: 121 | return self.buttons[button] == 2 or self.axisp(pcXAxis, -1.0) 122 | elif button == pcRight: 123 | return self.buttons[button] == 2 or self.axisp(pcXAxis, 1.0) 124 | elif button == pcUp: 125 | return self.buttons[button] == 2 or self.axisp(pcYAxis, -1.0) 126 | elif button == pcDown: 127 | return self.buttons[button] == 2 or self.axisp(pcYAxis, 1.0) 128 | return self.buttons[button] == 2 129 | 130 | proc anybtnp*(self: NicoController): bool = 131 | for b in self.buttons.low..self.buttons.high: 132 | if self.buttons[b] == 2: 133 | return true 134 | for axis in pcXAxis..pcYAxis: 135 | if abs(self.axis(axis)) > self.deadzone: 136 | return true 137 | return false 138 | 139 | proc btnup*(self: NicoController, button: NicoButton): bool = 140 | return self.buttons[button] == -1 141 | 142 | proc btnpr*(self: NicoController, button: NicoButton, repeat = 48): bool = 143 | let v = self.buttons[button] 144 | if v == 2 or (v > 2 and (v - 2) mod repeat == 0): 145 | return true 146 | if button == pcLeft: 147 | if self.axes[pcXAxis].current < -self.deadzone: 148 | let v = self.axes[pcXAxis].hold 149 | return v mod repeat == 0 150 | elif button == pcRight: 151 | if self.axes[pcXAxis].current > self.deadzone: 152 | let v = self.axes[pcXAxis].hold 153 | return v mod repeat == 0 154 | elif button == pcUp: 155 | if self.axes[pcYAxis].current < -self.deadzone: 156 | let v = self.axes[pcYAxis].hold 157 | return v mod repeat == 0 158 | elif button == pcDown: 159 | if self.axes[pcYAxis].current > self.deadzone: 160 | let v = self.axes[pcYAxis].hold 161 | return v mod repeat == 0 162 | return false 163 | 164 | proc setButtonState*(self: NicoController, button: NicoButton, down: bool) = 165 | if button > NicoButton.high: 166 | return 167 | self.buttons[button] = if down: 1 else: -2 168 | 169 | proc setAxisValue*(self: NicoController, axis: NicoAxis, value: float) = 170 | if axis > NicoAxis.high: 171 | return 172 | if (axis == pcXAxis and self.invertX) or (axis == pcYAxis and self.invertY): 173 | self.axes[axis].current = -value 174 | else: 175 | self.axes[axis].current = value 176 | 177 | 178 | -------------------------------------------------------------------------------- /nico/debug.nim: -------------------------------------------------------------------------------- 1 | import nico 2 | import nico/vec 3 | import sequtils 4 | import strutils 5 | 6 | type DebugKind = enum 7 | Point 8 | Line 9 | Circle 10 | Text 11 | 12 | type DebugObject = object 13 | kind: DebugKind 14 | a,b: Vec2f 15 | r: float32 16 | color: int 17 | str: string 18 | ttl: float32 19 | category: string 20 | 21 | const debugDrawTime = 0f 22 | const debugColor = 8 23 | 24 | var currentCategory: string = "" 25 | var debugObjects: seq[DebugObject] 26 | 27 | import macros 28 | 29 | macro debugVar*(n: varargs[typed]): untyped = 30 | result = newNimNode(nnkStmtList, n) 31 | for i in 0..n.len-1: 32 | if n[i].kind == nnkStrLit: 33 | # pure string literals are written directly 34 | result.add(newCall("write", newIdentNode("stdout"), n[i])) 35 | else: 36 | # other expressions are written in : syntax 37 | result.add(newCall("write", newIdentNode("stdout"), toStrLit(n[i]))) 38 | result.add(newCall("write", newIdentNode("stdout"), newStrLitNode(": "))) 39 | result.add(newCall("write", newIdentNode("stdout"), n[i])) 40 | if i != n.len-1: 41 | # separate by ", " 42 | result.add(newCall("write", newIdentNode("stdout"), newStrLitNode(", "))) 43 | else: 44 | # add newline 45 | result.add(newCall("writeLine", newIdentNode("stdout"), newStrLitNode(""))) 46 | 47 | proc debugCategory*(s: string) = 48 | currentCategory = s 49 | 50 | proc debugPoint*(a: Vec2f, color: int = debugColor, ttl = debugDrawTime) = 51 | debugObjects.add(DebugObject(kind: Point, a: a, color: color, ttl: ttl, category: currentCategory)) 52 | 53 | proc debugCircle*(a: Vec2f, r: float32, color: int = debugColor, ttl = debugDrawTime) = 54 | debugObjects.add(DebugObject(kind: Circle, a: a, r: r, color: color, ttl: ttl, category: currentCategory)) 55 | 56 | proc debugLine*(a,b: Vec2f, color: int = debugColor, ttl = debugDrawTime) = 57 | debugObjects.add(DebugObject(kind: Line, a: a, b: b, color: color, ttl: ttl, category: currentCategory)) 58 | 59 | proc debugText*(str: string, a: Vec2f, color: int = debugColor, ttl = debugDrawTime) = 60 | debugObjects.add(DebugObject(kind: Text, str: str, a: a, color: color, ttl: ttl, category: currentCategory)) 61 | 62 | proc debugRay*(a,b: Vec2f, color: int = debugColor, ttl = debugDrawTime) = 63 | debugObjects.add(DebugObject(kind: Line, a: a, b: a + b, color: color, ttl: ttl, category: currentCategory)) 64 | 65 | proc debugBox*(x,y,w,h: float32, color: int = debugColor, ttl = debugDrawTime) = 66 | debugLine(vec2f(x,y), vec2f(x+w,y), color, ttl) 67 | debugLine(vec2f(x+w,y), vec2f(x+w,y+h), color, ttl) 68 | debugLine(vec2f(x+w,y+h), vec2f(x,y+h), color, ttl) 69 | debugLine(vec2f(x,y+h), vec2f(x,y), color, ttl) 70 | 71 | proc debugBox*(x,y,w,h: int, color: int = debugColor, ttl = debugDrawTime) = 72 | debugLine(vec2f(x,y), vec2f(x+w,y), color, ttl) 73 | debugLine(vec2f(x+w,y), vec2f(x+w,y+h), color, ttl) 74 | debugLine(vec2f(x+w,y+h), vec2f(x,y+h), color, ttl) 75 | debugLine(vec2f(x,y+h), vec2f(x,y), color, ttl) 76 | 77 | proc debugPoly*(poly: seq[Vec2f], color: int = debugColor, ttl = debugDrawTime) = 78 | for i in 1.. 0f) 108 | 109 | proc clearDebug*(filter: string = "") = 110 | for d in debugObjects.mitems: 111 | if filter == "" or filter in d.category: 112 | d.ttl -= timeStep 113 | debugObjects.keepItIf(it.ttl > 0f) 114 | -------------------------------------------------------------------------------- /nico/fontdata.nim: -------------------------------------------------------------------------------- 1 | # default font data so we can print text without reading a file at runtime 2 | 3 | import nico/backends/common 4 | 5 | let defaultFontSurface* = Surface(w: 381, h: 8, channels: 1, tw: 4, th: 4, filename: "", data: @[2'u8, 0, 0, 0, 2, 0, 1, 0, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 1, 1, 2, 1, 0, 1, 2, 1, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 1, 0, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 1, 2, 1, 1, 1, 2, 1, 1, 0, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 0, 1, 2, 1, 1, 1, 2, 1, 0, 0, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 1, 2, 0, 0, 0, 2, 1, 0, 0, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 1, 1, 0, 2, 1, 0, 0, 2, 0, 1, 1, 2, 0, 1, 0, 2, 0, 0, 0, 2, 0, 1, 0, 2, 1, 1, 1, 2, 1, 1, 0, 2, 0, 1, 1, 2, 1, 1, 0, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 0, 1, 2, 1, 1, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 1, 0, 0, 2, 1, 1, 1, 2, 1, 1, 0, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 1, 1, 2, 1, 1, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 1, 1, 2, 0, 1, 1, 2, 1, 1, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 2, 0, 1, 0, 2, 1, 0, 1, 2, 1, 1, 1, 2, 1, 1, 0, 2, 0, 0, 1, 2, 1, 1, 0, 2, 1, 0, 0, 2, 1, 0, 0, 2, 0, 0, 1, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 1, 0, 2, 1, 0, 1, 2, 0, 1, 0, 2, 0, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 1, 0, 0, 2, 1, 0, 0, 2, 0, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 1, 1, 1, 2, 0, 1, 0, 2, 0, 0, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 0, 2, 1, 1, 1, 2, 1, 1, 0, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 0, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 0, 1, 2, 1, 0, 0, 2, 1, 1, 1, 2, 1, 1, 0, 2, 0, 1, 1, 2, 1, 1, 1, 2, 0, 1, 0, 2, 1, 1, 1, 2, 0, 1, 1, 2, 1, 1, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 1, 1, 2, 1, 0, 0, 2, 0, 1, 0, 2, 0, 0, 1, 2, 1, 0, 1, 2, 0, 0, 0, 2, 0, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 0, 2, 1, 0, 1, 2, 1, 0, 0, 2, 1, 0, 0, 2, 1, 0, 0, 2, 1, 0, 1, 2, 0, 1, 0, 2, 0, 0, 1, 2, 1, 0, 1, 2, 1, 0, 0, 2, 1, 1, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 0, 2, 0, 1, 0, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 0, 0, 1, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 0, 1, 2, 2, 2, 2, 2, 2, 0, 0, 0, 2, 0, 1, 0, 2, 0, 0, 0, 2, 1, 0, 1, 2, 1, 1, 1, 2, 0, 1, 0, 2, 1, 1, 0, 2, 0, 0, 0, 2, 1, 0, 0, 2, 0, 0, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 1, 0, 2, 1, 0, 1, 2, 0, 1, 0, 2, 1, 1, 1, 2, 0, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 1, 0, 0, 2, 0, 0, 0, 2, 0, 0, 1, 2, 0, 1, 1, 2, 1, 1, 1, 2, 1, 0, 1, 2, 1, 1, 0, 2, 1, 0, 0, 2, 1, 0, 1, 2, 1, 1, 0, 2, 1, 1, 0, 2, 1, 0, 0, 2, 1, 0, 1, 2, 0, 1, 0, 2, 0, 1, 0, 2, 1, 1, 0, 2, 1, 0, 0, 2, 1, 1, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 0, 2, 0, 1, 0, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 0, 1, 0, 2, 1, 1, 1, 2, 0, 0, 1, 2, 1, 0, 0, 2, 0, 1, 0, 2, 0, 0, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 0, 0, 2, 1, 0, 1, 2, 1, 1, 0, 2, 1, 1, 0, 2, 1, 0, 1, 2, 1, 1, 1, 2, 0, 1, 0, 2, 0, 0, 1, 2, 1, 1, 0, 2, 1, 0, 0, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 1, 1, 2, 1, 0, 1, 2, 1, 1, 0, 2, 1, 1, 1, 2, 0, 1, 0, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 1, 1, 2, 0, 1, 0, 2, 1, 1, 1, 2, 0, 1, 0, 2, 1, 1, 0, 2, 0, 1, 1, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 1, 1, 1, 2, 0, 1, 1, 2, 1, 0, 0, 2, 1, 0, 1, 2, 0, 0, 0, 2, 1, 0, 0, 2, 0, 0, 1, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 1, 0, 2, 1, 0, 1, 2, 0, 1, 0, 2, 1, 0, 0, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 0, 0, 1, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 1, 1, 1, 2, 0, 1, 0, 2, 0, 0, 0, 2, 1, 0, 0, 2, 1, 1, 1, 2, 1, 0, 1, 2, 1, 0, 0, 2, 1, 0, 1, 2, 1, 0, 0, 2, 1, 0, 0, 2, 1, 0, 1, 2, 1, 1, 1, 2, 0, 1, 0, 2, 0, 1, 0, 2, 1, 0, 1, 2, 1, 0, 0, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 1, 1, 2, 1, 1, 0, 2, 1, 1, 0, 2, 0, 0, 1, 2, 0, 1, 0, 2, 1, 0, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 0, 1, 2, 0, 0, 1, 2, 1, 0, 0, 2, 1, 0, 0, 2, 0, 1, 0, 2, 0, 0, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 0, 2, 1, 0, 1, 2, 1, 0, 0, 2, 1, 0, 0, 2, 1, 0, 1, 2, 1, 0, 1, 2, 0, 1, 0, 2, 0, 0, 1, 2, 1, 0, 1, 2, 1, 0, 0, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 0, 2, 1, 1, 0, 2, 1, 0, 1, 2, 0, 0, 1, 2, 0, 1, 0, 2, 1, 0, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 0, 1, 2, 0, 0, 1, 2, 1, 0, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 1, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 2, 0, 1, 0, 2, 0, 0, 0, 2, 1, 0, 1, 2, 1, 1, 1, 2, 1, 0, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 1, 0, 1, 2, 0, 0, 0, 2, 1, 0, 0, 2, 0, 0, 0, 2, 0, 1, 0, 2, 1, 0, 0, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 1, 2, 1, 1, 1, 2, 0, 0, 1, 2, 0, 0, 0, 2, 1, 0, 0, 2, 0, 0, 1, 2, 0, 0, 0, 2, 1, 0, 0, 2, 0, 1, 0, 2, 1, 1, 1, 2, 1, 0, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 0, 2, 1, 1, 1, 2, 1, 0, 0, 2, 1, 1, 1, 2, 1, 0, 1, 2, 1, 1, 1, 2, 1, 1, 0, 2, 1, 0, 1, 2, 1, 1, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 1, 0, 2, 1, 0, 0, 2, 0, 1, 1, 2, 1, 0, 1, 2, 1, 1, 0, 2, 0, 1, 0, 2, 0, 1, 1, 2, 0, 1, 0, 2, 1, 1, 1, 2, 1, 0, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 0, 2, 0, 0, 1, 2, 0, 1, 1, 2, 0, 0, 0, 2, 1, 1, 1, 2, 0, 0, 0, 2, 1, 0, 1, 2, 1, 1, 1, 2, 0, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 0, 0, 2, 1, 1, 1, 2, 1, 0, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 0, 1, 2, 1, 1, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 1, 1, 2, 1, 0, 0, 2, 0, 1, 1, 2, 1, 0, 1, 2, 1, 1, 0, 2, 0, 1, 0, 2, 1, 1, 1, 2, 0, 1, 0, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 1, 1, 2, 1, 1, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]) 6 | const defaultFontChars* = " !\"#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{}~" 7 | -------------------------------------------------------------------------------- /nico/grids.nim: -------------------------------------------------------------------------------- 1 | import nico/vec 2 | 3 | type Grid*[T] = object 4 | width*: int 5 | height*: int 6 | data*: seq[T] 7 | default*: T 8 | 9 | proc set*[T](g: var Grid[T], x,y: int, t: T) = 10 | if x < 0 or y < 0 or x >= g.width or y >= g.height: 11 | return 12 | g.data[y * g.width + x] = t 13 | 14 | proc get*[T](g: Grid[T], x,y: int): T = 15 | if x < 0 or y < 0 or x >= g.width or y >= g.height: 16 | return default(T) 17 | return g.data[y * g.width + x] 18 | 19 | proc get*[T](g: Grid[T], v: Vec2i): T = 20 | return g.get(v.x, v.y) 21 | 22 | proc set*[T](g: var Grid[T], v: Vec2i, t: T) = 23 | g.set(v.x, v.y, t) 24 | 25 | proc `[]`*[T](g: Grid[T], v: Vec2i): T = 26 | g.get(v) 27 | 28 | proc `[]`*[T](g: Grid[T], x,y: int): T = 29 | g.get(x,y) 30 | 31 | proc `[]=`*[T](g: var Grid[T], v: Vec2i, t: T) = 32 | g.set(v,t) 33 | 34 | proc `[]=`*[T](g: var Grid[T], x,y: int, t: T) = 35 | g.set(vec2i(x,y),t) 36 | 37 | iterator items*[T](g: Grid[T]): Vec2i = 38 | for ty in 0.. 0: 44 | yield vec2i(v.x-1,v.y) 45 | if v.x < g.width - 1: 46 | yield vec2i(v.x+1,v.y) 47 | if v.y > 0: 48 | yield vec2i(v.x,v.y-1) 49 | if v.y < g.height - 1: 50 | yield vec2i(v.x,v.y+1) 51 | 52 | if diagonal: 53 | if v.x > 0: 54 | if v.y > 0: 55 | yield vec2i(v.x-1,v.y-1) 56 | if v.y < g.height - 1: 57 | yield vec2i(v.x-1,v.y+1) 58 | if v.x < g.width - 1: 59 | if v.y > 0: 60 | yield vec2i(v.x+1,v.y-1) 61 | if v.y < g.height - 1: 62 | yield vec2i(v.x+1,v.y+1) 63 | 64 | proc initGrid*[T](w,h: int, default: T = default(T)): Grid[T] = 65 | result.width = w 66 | result.height = h 67 | result.default = default 68 | result.data = newSeq[T](w*h) 69 | for y in 0.. b.size: raise newException(IndexDefect, "Index " & $idx & " out of bound") 48 | 49 | proc len*[T](b: RingBuffer[T]): int = 50 | return b.length 51 | 52 | iterator items*[T](b: RingBuffer[T]): T = 53 | for i in 0.. ", v 77 | case tweak.kind: 78 | of TweakFloat: 79 | tweak.f[] = v.getFloat 80 | tweakTable[k].fsaved = v.getFloat 81 | of TweakInt: 82 | tweak.i[] = v.getInt 83 | tweakTable[k].isaved = v.getInt 84 | of TweakBool: 85 | tweak.b[] = v.getBool 86 | tweakTable[k].bsaved = v.getBool 87 | else: 88 | echo "unregistered tweak: ", k 89 | echo "reloaded tweaks" 90 | except Exception as e: 91 | echo "error loading tweaks ", e.msg 92 | return 93 | 94 | proc saveTweaks*(name = "tweaks") = 95 | try: 96 | var j = newJObject() 97 | for k,v in tweakTable: 98 | case v.kind: 99 | of TweakFloat: 100 | j[k] = %* v.f[] 101 | of TweakInt: 102 | j[k] = %* v.i[] 103 | of TweakBool: 104 | j[k] = %* v.b[] 105 | saveJsonFile("assets/" & name & ".json", j) 106 | reloadTweaks(name) 107 | echo "saved tweaks" 108 | except: 109 | echo "error saving tweaks" 110 | 111 | template tweaks*(category: string, body: typed): untyped = 112 | let startCat = tweakCategory 113 | tweakCategory = tweakCategory / category 114 | body 115 | tweakCategory = startCat 116 | 117 | template tweak*(x: untyped, v: float32, rng = float32.low .. float32.high): untyped = 118 | var x: float32 = v 119 | addTweak(x.astToStr, x, rng) 120 | 121 | template tweak*(x: untyped, v: int, rng = int.low .. int.high): untyped = 122 | var x: int = v 123 | addTweak(x.astToStr, x, rng) 124 | 125 | template tweak*(x: untyped, v: bool): untyped = 126 | var x: bool = v 127 | addTweak(x.astToStr, x) 128 | 129 | proc listTweaks(args: seq[string]): seq[string] = 130 | result = newSeq[string]() 131 | for k,v in tweakTable: 132 | case v.kind: 133 | of TweakFloat: 134 | result.add(k & " = " & $v.f[]) 135 | of TweakInt: 136 | result.add(k & " = " & $v.i[]) 137 | of TweakBool: 138 | result.add(k & " = " & $v.b[]) 139 | 140 | proc setTweak(args: seq[string]): seq[string] = 141 | if args.len != 2: 142 | return @["invalid usage of tweak, requires 2 args got " & $args.len] 143 | let k = args[0].strip() 144 | let v = args[1].strip() 145 | 146 | echo "k: '", k, "' = ", v 147 | 148 | try: 149 | let tweak = tweakTable[k] 150 | case tweak.kind: 151 | of TweakFloat: 152 | tweak.f[] = parseFloat(v) 153 | of TweakInt: 154 | tweak.i[] = parseInt(v) 155 | of TweakBool: 156 | tweak.b[] = parseBool(v) 157 | saveTweaks() 158 | except KeyError: 159 | return @["unknown tweak: '" & k & "'"] 160 | 161 | #var winx,winy,winw,winh: Pint 162 | #winx = 2 163 | #winy = 2 164 | #winw = 120 165 | #winh = 120 166 | #var showWin = false 167 | #var scrollX,scrollY = 0 168 | 169 | #proc inspect*[T](x: var T): proc() = 170 | # var xptr = x.addr 171 | # return proc() = 172 | # if G.beginWindow("inspector", winx, winy, winw, winh, showWin, gTopToBottom): 173 | # G.hExpand = true 174 | # for name, v in xptr[].fieldPairs: 175 | # when v is float32: 176 | # G.drag(name, v, float32.low, float32.high, 0.01f) 177 | # elif v is int: 178 | # G.drag(name, v, int.low, int.high, 0.1f) 179 | # elif v is Vec2i: 180 | # G.beginHorizontal(10) 181 | # G.hExpand = false 182 | # G.label(name) 183 | # G.drag("", v.x, int.low, int.high, 0.1f) 184 | # G.drag("", v.y, int.low, int.high, 0.1f) 185 | # G.hExpand = true 186 | # G.endArea() 187 | # elif v is Vec2f: 188 | # G.beginHorizontal(10) 189 | # G.hExpand = false 190 | # G.label(name) 191 | # G.drag("", v.x, float32.low, float32.high, 0.1f) 192 | # G.drag("", v.y, float32.low, float32.high, 0.1f) 193 | # G.hExpand = true 194 | # G.endArea() 195 | # G.endArea() 196 | 197 | #proc tweaksGUI*() = 198 | # if G.beginWindow("tweaks", winx, winy, winw, winh, showWin, gTopToBottom): 199 | # G.hExpand = true 200 | # G.beginScrollArea(scrollX, scrollY) 201 | # var lastCat = "" 202 | # var open = true 203 | # for k,v in tweakTable: 204 | # let namebits = k.split('/') 205 | # var name = namebits[^1] 206 | # var cat = if namebits.len == 1: "" else: namebits[0] 207 | # if cat != lastCat: 208 | # open = G.beginDrawer(cat) 209 | # lastCat = cat 210 | # 211 | # if open: 212 | # case v.kind: 213 | # of TweakFloat: 214 | # G.drag(name, v.f[], v.fmin, v.fmax, 0.01f) 215 | # of TweakInt: 216 | # G.drag(name, v.i[], v.imin, v.imax, 0.1f) 217 | # of TweakBool: 218 | # G.toggle(name, v.b[], true) 219 | # 220 | # G.beginHorizontal(10) 221 | # G.hExpand = false 222 | # if G.button("save"): 223 | # saveTweaks() 224 | # if G.button("reset"): 225 | # reloadTweaks() 226 | # G.endArea() 227 | # G.endArea() 228 | # G.endArea() 229 | # 230 | 231 | var searchText = "" 232 | 233 | var tweakPresetName = "tweaks" 234 | var tweakPresets = getTweakPresets() 235 | 236 | proc tweaksGUI2*() = 237 | guiSetStatus(Default) 238 | if guiBegin("tweaks"): 239 | guiHorizontal: 240 | if guiButton("save"): 241 | saveTweaks(tweakPresetName) 242 | if guiButton("reset"): 243 | reloadTweaks(tweakPresetName) 244 | if guiOption("", tweakPresetName, tweakPresets): 245 | reloadTweaks(tweakPresetName) 246 | if guiTextField("", searchText): 247 | echo "searchText ", searchText 248 | 249 | var lastCat = "" 250 | var open = true 251 | 252 | for k,v in tweakTable: 253 | let namebits = k.split('/') 254 | var name = namebits[^1] 255 | var cat = if namebits.len == 1: "" else: namebits[0] 256 | 257 | if cat != lastCat: 258 | guiSetStatus(Default) 259 | if lastCat != "" and open: 260 | guiEndFoldout() 261 | open = guiStartFoldout(cat) 262 | lastCat = cat 263 | 264 | if open: 265 | if searchText == "" or searchText.toLowerAscii() in name.toLowerAscii(): 266 | case v.kind: 267 | of TweakFloat: 268 | guiSetStatus(if v.f[] != v.fsaved: Warning else: Default) 269 | if v.fmin == -Inf or v.fmax == Inf: 270 | guiDrag(name, v.f[], v.fsaved, v.fmin, v.fmax, 0.1f) 271 | else: 272 | guiSlider(name, v.f[], v.fsaved, v.fmin, v.fmax) 273 | of TweakInt: 274 | guiSetStatus(if v.i[] != v.isaved: Warning else: Default) 275 | if v.imin == int.low or v.imax == int.high: 276 | guiDrag(name, v.i[], v.isaved, v.imin, v.imax, 0.1f) 277 | else: 278 | guiSlider(name, v.i[], v.isaved, v.imin, v.imax) 279 | of TweakBool: 280 | guiSetStatus(if v.b[] != v.bsaved: Warning else: Default) 281 | guiToggle(name, v.b[]) 282 | 283 | 284 | 285 | registerConsoleCommand("tweaks", listTweaks) 286 | registerConsoleCommand("tweak", setTweak) 287 | registerConsoleCommand("reloadTweaks", proc(args: seq[string]): seq[string] = reloadTweaks(tweakPresetName)) 288 | registerConsoleCommand("saveTweaks", proc(args: seq[string]): seq[string] = saveTweaks(tweakPresetName)) 289 | -------------------------------------------------------------------------------- /nico/utils.nim: -------------------------------------------------------------------------------- 1 | import nico 2 | import strutils 3 | import strscans 4 | 5 | type TextAlign* = enum 6 | taLeft 7 | taRight 8 | taCenter 9 | 10 | var outlineColor: int = 0 11 | 12 | proc setOutlineColor*(oc: int) = 13 | outlineColor = oc 14 | 15 | proc printOutlineC*(text: string, x, y: cint, scale: cint = 1) = 16 | let oldColor = getColor() 17 | setColor(outlineColor) 18 | printc(text, x-scale, y, scale) 19 | printc(text, x+scale, y, scale) 20 | printc(text, x, y-scale, scale) 21 | printc(text, x, y+scale, scale) 22 | printc(text, x+scale, y+scale, scale) 23 | printc(text, x-scale, y-scale, scale) 24 | printc(text, x+scale, y-scale, scale) 25 | printc(text, x-scale, y+scale, scale) 26 | setColor(oldColor) 27 | printc(text, x, y, scale) 28 | 29 | proc printOutlineR*(text: string, x, y: cint, scale: cint = 1) = 30 | let oldColor = getColor() 31 | setColor(outlineColor) 32 | printr(text, x-scale, y, scale) 33 | printr(text, x+scale, y, scale) 34 | printr(text, x, y-scale, scale) 35 | printr(text, x, y+scale, scale) 36 | printr(text, x+scale, y+scale, scale) 37 | printr(text, x-scale, y-scale, scale) 38 | printr(text, x+scale, y-scale, scale) 39 | printr(text, x-scale, y+scale, scale) 40 | setColor(oldColor) 41 | printr(text, x, y, scale) 42 | 43 | proc printOutline*(text: string, x, y: cint, scale: cint = 1) = 44 | let oldColor = getColor() 45 | setColor(outlineColor) 46 | print(text, x-scale, y, scale) 47 | print(text, x+scale, y, scale) 48 | print(text, x, y-scale, scale) 49 | print(text, x, y+scale, scale) 50 | print(text, x+scale, y+scale, scale) 51 | print(text, x-scale, y-scale, scale) 52 | print(text, x+scale, y-scale, scale) 53 | print(text, x-scale, y+scale, scale) 54 | setColor(oldColor) 55 | print(text, x, y, scale) 56 | 57 | proc printShadow*(text: string, x, y: cint, scale: cint = 1) = 58 | let oldColor = getColor() 59 | setColor(0) 60 | print(text, x-scale, y, scale) 61 | setColor(oldColor) 62 | print(text, x, y, scale) 63 | 64 | proc richPrintWidthOneLine*(text: string, startChar = 0, endChar = -1): int = 65 | var i = startChar 66 | var endChar = if endChar == -1: text.high else: endChar 67 | while i < min(text.len,endChar+1): 68 | let c = text[i] 69 | if c == '<' and i + 1 < text.len: 70 | # scan foward until '>' 71 | var k = i + 1 72 | if text[k] == '<': # << = just print < 73 | result += glyphWidth(c) 74 | continue 75 | 76 | while k < text.len: 77 | if text[k] == '>': 78 | break 79 | k += 1 80 | 81 | let code = text[i+1..k-1] 82 | if code.startsWith("spr"): 83 | var sprId, palA, palB: int 84 | var (sw,sh) = spriteSize() 85 | if scanf(code, "spr($i,$i,$i)", sprId, sw, sh) or 86 | scanf(code, "spr($i)pal($i,$i)", sprId, palA, palB) or 87 | scanf(code, "spr($i)", sprId): 88 | result += sw 89 | i = k + 1 90 | continue 91 | 92 | i += 1 93 | result += glyphWidth(c) 94 | 95 | proc richPrintWidth*(text: string, start = 0): int = 96 | var maxWidth = 0 97 | for text in text.split('\n'): 98 | let width = richPrintWidthOneLine(text) 99 | if width > maxWidth: 100 | maxWidth = width 101 | return maxWidth 102 | 103 | proc richPrintCount*(text: string): int = 104 | var i = 0 105 | while i < text.len: 106 | let c = text[i] 107 | if c == '<': 108 | # scan foward until '>' 109 | var k = i + 1 110 | if text[k] == '<': 111 | result += glyphWidth(c) 112 | continue 113 | while k < text.len: 114 | if text[k] == '>': 115 | break 116 | k += 1 117 | i = k + 1 118 | continue 119 | i += 1 120 | result += 1 121 | 122 | var lastPrintedChar = '\0' 123 | 124 | proc richPrintLastPrintedChar*(): char = 125 | return lastPrintedChar 126 | 127 | proc richPrintOneLine*(text: string, x,y: int, align: TextAlign = taLeft, shadow = false, outline = false, startColor = 7, step = -1): int {.discardable.} = 128 | ## prints but handles color codes <0>black <8>red etc <-> to return to normal 129 | ## returns the number of chars printed 130 | result = 0 131 | 132 | if step == 0: 133 | return 0 134 | 135 | lastPrintedChar = '\0' 136 | 137 | var sx = x 138 | var x = x 139 | var y = y 140 | var wiggle = false 141 | var wiggleSpeed = 2.0 142 | var wiggleAmount = 2.0 143 | 144 | let t = time() 145 | 146 | var tlen = 0 147 | if align != taLeft: 148 | tlen = richPrintWidth(text) 149 | 150 | proc output(c: char, tlen: int) = 151 | if shadow: 152 | printShadow($c, x - (if align == taRight: tlen elif align == taCenter: tlen div 2 else: 0), y) 153 | elif outline: 154 | printOutline($c, x - (if align == taRight: tlen elif align == taCenter: tlen div 2 else: 0), y) 155 | else: 156 | print($c, x + (if wiggle: cos(x.float32 * 0.5421f + t * wiggleSpeed) * wiggleAmount.float32 else: 0).int - (if align == taRight: tlen elif align == taCenter: tlen div 2 else: 0).int, y + (if wiggle: sin(x.float32 * 0.234f + t * wiggleSpeed.float32 * 1.123f) * wiggleAmount.float32 else: 0f).int) 157 | lastPrintedChar = c 158 | x += glyphWidth(c) 159 | 160 | let hfh = fontHeight() div 2 161 | 162 | var i = 0 163 | while i < text.len: 164 | if step != -1 and result >= step: 165 | break 166 | 167 | let c = text[i] 168 | 169 | if c == '<': 170 | if i+1 < text.high and text[i+1] == '<': 171 | output(c, tlen) 172 | result += 1 173 | i += 2 174 | continue 175 | var k = i + 1 176 | while k < text.len: 177 | if text[k] == '>': 178 | break 179 | k += 1 180 | let code = text[i+1..k-1] 181 | if code == "/": 182 | setColor(startColor) 183 | wiggle = false 184 | elif scanf(code, "s($f,$f)", wiggleSpeed, wiggleAmount): 185 | wiggle = true 186 | elif scanf(code, "s($f)", wiggleSpeed): 187 | wiggle = true 188 | elif code == "s": 189 | wiggleSpeed = 2f 190 | wiggle = true 191 | elif code.startsWith("spr"): 192 | var sprId, palA, palB: int 193 | var (sw,sh) = spriteSize() 194 | if scanf(code, "spr($i,$i,$i)", sprId, sw, sh): 195 | spr(sprId, x - (if align == taCenter: tlen div 2 elif align == taRight: tlen else: 0), y - sh div 2 + hfh) 196 | x += sw 197 | elif scanf(code, "spr($i)pal($i,$i)", sprId, palA, palB): 198 | let original = pal(palA) 199 | pal(palA, palB) 200 | spr(sprId, x - (if align == taCenter: tlen div 2 elif align == taRight: tlen else: 0), y - sh div 2 + hfh) 201 | pal(palA, original) 202 | x += sw 203 | elif scanf(code, "spr($i)", sprId): 204 | spr(sprId, x - (if align == taCenter: tlen div 2 elif align == taRight: tlen else: 0), y - sh div 2 + hfh) 205 | x += sw 206 | else: 207 | let col = try: parseInt(code) except ValueError: startColor 208 | setColor(col) 209 | i = k + 1 210 | continue 211 | 212 | output(c, tlen) 213 | result += 1 214 | i += 1 215 | y += fontHeight() 216 | x = sx 217 | 218 | 219 | proc richPrint*(text: string, x,y: int, align: TextAlign = taLeft, shadow = false, outline = false, step = -1) = 220 | ## prints but handles color codes <0>black <8>red etc <-> to return to normal 221 | var charCount = 0 222 | var step = step 223 | var y = y 224 | var startColor = getColor() 225 | for text in text.split('\n'): 226 | charCount = richPrintOneLine(text, x,y, align, shadow, outline, startColor, step) 227 | if step != -1: 228 | step -= charCount 229 | y += fontHeight() 230 | setColor(startColor) 231 | 232 | proc richWrapLines*(text: string, width: int): seq[string] = 233 | ## returns a list of strings with text split to fit on lines of width 234 | var linesToProcess = text.split('\n') 235 | var currentLine = 0 236 | while currentLine < linesToProcess.len: 237 | var line = linesToProcess[currentLine] 238 | var w = richPrintWidth(line) 239 | if w <= width: 240 | result.add(line) 241 | currentLine += 1 242 | else: 243 | # line is too long, we need to split it 244 | # find all split chars in text and find the split that gives us the highest w under width 245 | var i = line.high 246 | var foundSplit = false 247 | while i > 0: 248 | let c = line[i] 249 | if c in [' ', '-']: 250 | w = richPrintWidthOneLine(line, 0, i) 251 | if w <= width: 252 | result.add(line[0.. maxWidth: 269 | maxWidth = width 270 | return maxWidth 271 | 272 | proc richPrintWrap*(text: string, x,y,w: int, align: TextAlign = taLeft, shadow = false, outline = false, step = -1) = 273 | var y = y 274 | var step = step 275 | var i = 0 276 | var startColor = getColor() 277 | for line in richWrapLines(text, w): 278 | let stepsPrinted = richPrintOneLine(line, x,y, align, shadow, outline, startColor, step) 279 | if step != -1: 280 | step -= stepsPrinted 281 | y += fontHeight() 282 | i += 1 283 | setColor(startColor) 284 | -------------------------------------------------------------------------------- /nicoandroid.nim: -------------------------------------------------------------------------------- 1 | import httpclient 2 | import os 3 | import osproc 4 | import strutils 5 | import zippy/ziparchives 6 | 7 | if not dirExists("android"): 8 | if not fileExists("android.zip"): 9 | # download and extract nico android base 10 | let client = newHttpClient() 11 | client.downloadFile("https://www.impbox.net/nico/android.zip", "android.zip") 12 | 13 | createDir("tmp_android") 14 | extractAll("android.zip", "tmp_android/android") 15 | moveFile("tmp_android/android/android", "android") 16 | removeDir("tmp_android") 17 | 18 | let dumpLines = execProcess("nim dump") 19 | let lastLine = dumpLines.splitLines()[^2] 20 | let nimbasePath = lastLine / "nimbase.h" 21 | copyFile(nimbasePath, "android/app/jni/src/nimbase.h") 22 | -------------------------------------------------------------------------------- /nicoandroid.nim.cfg: -------------------------------------------------------------------------------- 1 | --define:ssl 2 | -------------------------------------------------------------------------------- /nicoboot.nim: -------------------------------------------------------------------------------- 1 | # tool to set up a new nico project 2 | import os 3 | import osproc 4 | import parseopt 5 | 6 | var params = initOptParser(commandLineParams(), shortNoVal = {'f'}) 7 | 8 | var targetPath: string = "" 9 | var orgName: string = "" 10 | var appName: string = "" 11 | var overwrite = false 12 | 13 | var arg = 0 14 | 15 | for kind, key, val in getOpt(params): 16 | case kind: 17 | of cmdArgument: 18 | if arg == 0: 19 | orgName = key 20 | elif arg == 1: 21 | appName = key 22 | elif arg == 2: 23 | targetPath = key 24 | arg.inc 25 | of cmdLongOption, cmdShortOption: 26 | case key: 27 | of "f": overwrite = true 28 | of cmdEnd: 29 | assert(false) 30 | 31 | if targetPath == "": 32 | echo "nicoboot [-f] orgName appName projectPath" 33 | quit(1) 34 | 35 | # create a new project 36 | let sourcePath = joinPath(getAppDir(), "exampleApp") 37 | if overwrite == false and (dirExists(targetPath) or fileExists(targetPath) or symlinkExists(targetPath)): 38 | echo "not overwriting existing path: ", targetPath, " use -f to overwrite" 39 | quit(1) 40 | echo "copying ", sourcePath, " to ", targetPath 41 | copyDir(sourcePath, targetPath) 42 | # search and replace 43 | moveFile(joinPath(targetPath, "exampleApp.nimble"), joinPath(targetPath, appName & ".nimble")) 44 | echo execProcess("nimgrep", "", ["-!","exampleApp",appName,"-r",targetPath], nil, {poUsePath, poStdErrToStdOut}) 45 | echo execProcess("nimgrep", "", ["-!","exampleOrg",orgName,"-r",targetPath], nil, {poUsePath, poStdErrToStdOut}) 46 | echo "nico project ", appName, " created in ", targetPath 47 | -------------------------------------------------------------------------------- /nicosynth.nim: -------------------------------------------------------------------------------- 1 | import nico 2 | import nico/gui 3 | import strformat 4 | import strutils 5 | 6 | var data: SynthData 7 | var editIndex = 0 8 | var editMode = 0 9 | 10 | var outputStr: string 11 | var dirty = true 12 | 13 | const minNote = 32 14 | const maxNote = 32 + 64 15 | 16 | const initData = synthDataFromString("0037A235C232D22FC22C82296227422422") 17 | 18 | data = initData 19 | 20 | proc gameInit() = 21 | masterVol(255) 22 | sfxVol(255) 23 | musicVol(255) 24 | 25 | for v in data.steps.mitems: 26 | if v.shape == synSame: 27 | v.shape = synSqr 28 | 29 | proc play() = 30 | synth(0, outputStr) 31 | 32 | var xval,yval: int 33 | 34 | proc gameGui() = 35 | G.beginArea(4,4,screenWidth-8,20,gLeftToRight) 36 | var speed = data.speed.int 37 | if G.slider("speed", speed, 0, 15): 38 | data.speed = speed.uint8 39 | var loop = data.loop.int 40 | if G.slider("loop", loop, 0, 15): 41 | data.loop = loop.uint8 42 | if G.button("-"): 43 | for v in data.steps.mitems: 44 | v.note -= 12 45 | if G.button("+"): 46 | for v in data.steps.mitems: 47 | v.note += 12 48 | G.endArea() 49 | 50 | let playIndex = synthIndex(0) 51 | 52 | let (mx,my) = mouse() 53 | 54 | # pitch 55 | let pitchHeight = 64 56 | if G.xyarea(xval,yval,4, 24, screenWidth - 8, pitchHeight, proc(G: Gui, x,y,w,h: int, style: GuiStyle, ta,va: TextAlign) = 57 | for i in 0..= 0 and ix < data.steps.len: 84 | editMode = 0 85 | editIndex = ix 86 | var note = clamp(minNote + pitchHeight - yval, minNote, maxNote) 87 | if key(K_LSHIFT): 88 | # lock to C maj, 0 2 4 7 9 11 89 | let oct = note div 12 90 | let noteInOct = note mod 12 91 | let adjusted = case noteInOct: 92 | of 0,1: 0 93 | of 2,3: 2 94 | of 4,5: 4 95 | of 6,7: 7 96 | of 8,9: 9 97 | of 10,11: 11 98 | else: 0 99 | note = oct * 12 + adjusted 100 | data.steps[ix].note = note.uint8 101 | 102 | # volume 103 | let volumeHeight = 32 104 | if G.xyarea(xval,yval,4, 24 + pitchHeight + 4, screenWidth - 8, volumeHeight, proc(G: Gui, x,y,w,h: int, style: GuiStyle, ta,va: TextAlign) = 105 | for i in 0..= 0 and ix < data.steps.len: 134 | editMode = 1 135 | editIndex = ix 136 | 137 | var vol = lerp(0'f, 15'f, (yrange - yval).float32 / yrange.float32) 138 | 139 | data.steps[ix].volume = clamp(vol, 0, 15).uint8 140 | 141 | # shape 142 | let shapeHeight = 32 143 | if G.xyarea(xval,yval,4, 24 + pitchHeight + 4 + volumeHeight + 4, screenWidth - 8, shapeHeight, proc(G: Gui, x,y,w,h: int, style: GuiStyle, ta,va: TextAlign) = 144 | for i in 0..= 0 and ix < data.steps.len: 172 | editMode = 2 173 | editIndex = ix 174 | var shape = lerp(synSin.int.float32 - 0.5'f, synNoise2.float32 + 0.5'f, (yrange - yval).float32 / yrange.float32) 175 | data.steps[ix].shape = clamp(shape.int, synSin.int, synNoise2.int).SynthShape 176 | 177 | G.beginArea(0,screenHeight - 20, screenWidth, 20,gLeftToRight) 178 | if G.button("PLAY"): 179 | play() 180 | if G.button("COPY CODE"): 181 | echo fmt("synth(channel, \"{outputStr}\")") 182 | setClipboardText(outputStr) 183 | G.endArea() 184 | 185 | proc gameUpdate(dt: float32) = 186 | G.update(gameGui, dt) 187 | 188 | if btnp(pcA): 189 | play() 190 | 191 | if btnp(pcB): 192 | if data.steps[editIndex].shape < SynthShape.high: 193 | data.steps[editIndex].shape.inc() 194 | else: 195 | data.steps[editIndex].shape = synSin 196 | 197 | if btnpr(pcLeft): 198 | editIndex -= 1 199 | if btnpr(pcRight): 200 | editIndex += 1 201 | 202 | if btnpr(pcUp): 203 | case editMode: 204 | of 0: 205 | data.steps[editIndex].note.inc() 206 | of 1: 207 | data.steps[editIndex].volume.inc() 208 | of 2: 209 | data.steps[editIndex].shape.inc() 210 | else: discard 211 | if btnpr(pcDown): 212 | case editMode: 213 | of 0: 214 | data.steps[editIndex].note.dec() 215 | of 1: 216 | data.steps[editIndex].volume.dec() 217 | of 2: 218 | data.steps[editIndex].shape.dec() 219 | else: discard 220 | 221 | editIndex = wrap(editIndex, data.steps.len) 222 | 223 | if dirty: 224 | outputStr = synthDataToString(data) 225 | 226 | proc gameDraw() = 227 | cls() 228 | 229 | G.draw(gameGui) 230 | 231 | nico.init("nico", "nicoSynthEditor") 232 | nico.createWindow("nicoSynthEditor", 150, 190, 4, false) 233 | nico.run(gameInit, gameUpdate, gameDraw) 234 | -------------------------------------------------------------------------------- /nicosynth.nim.cfg: -------------------------------------------------------------------------------- 1 | --path:"." 2 | 3 | -------------------------------------------------------------------------------- /tests/assets/aurora.gpl: -------------------------------------------------------------------------------- 1 | GIMP Palette 2 | #Palette Name: Aurora 3 | #Description: Created by DawnBringer as part of the GrafX2 Toolbox. 4 | #Colors: 256 5 | 0 0 0 000000 6 | 17 17 17 111111 7 | 34 34 34 222222 8 | 51 51 51 333333 9 | 68 68 68 444444 10 | 85 85 85 555555 11 | 102 102 102 666666 12 | 119 119 119 777777 13 | 136 136 136 888888 14 | 153 153 153 999999 15 | 170 170 170 aaaaaa 16 | 187 187 187 bbbbbb 17 | 204 204 204 cccccc 18 | 221 221 221 dddddd 19 | 238 238 238 eeeeee 20 | 255 255 255 ffffff 21 | 0 127 127 007f7f 22 | 63 191 191 3fbfbf 23 | 0 255 255 00ffff 24 | 191 255 255 bfffff 25 | 129 129 255 8181ff 26 | 0 0 255 0000ff 27 | 63 63 191 3f3fbf 28 | 0 0 127 00007f 29 | 15 15 80 0f0f50 30 | 127 0 127 7f007f 31 | 191 63 191 bf3fbf 32 | 245 0 245 f500f5 33 | 253 129 255 fd81ff 34 | 255 192 203 ffc0cb 35 | 255 129 129 ff8181 36 | 255 0 0 ff0000 37 | 191 63 63 bf3f3f 38 | 127 0 0 7f0000 39 | 85 20 20 551414 40 | 127 63 0 7f3f00 41 | 191 127 63 bf7f3f 42 | 255 127 0 ff7f00 43 | 255 191 129 ffbf81 44 | 255 255 191 ffffbf 45 | 255 255 0 ffff00 46 | 191 191 63 bfbf3f 47 | 127 127 0 7f7f00 48 | 0 127 0 007f00 49 | 63 191 63 3fbf3f 50 | 0 255 0 00ff00 51 | 175 255 175 afffaf 52 | 0 191 255 00bfff 53 | 0 127 255 007fff 54 | 75 125 200 4b7dc8 55 | 188 175 192 bcafc0 56 | 203 170 137 cbaa89 57 | 166 160 144 a6a090 58 | 126 148 148 7e9494 59 | 110 130 135 6e8287 60 | 126 110 96 7e6e60 61 | 160 105 95 a0695f 62 | 192 120 114 c07872 63 | 208 138 116 d08a74 64 | 225 155 125 e19b7d 65 | 235 170 140 ebaa8c 66 | 245 185 155 f5b99b 67 | 246 200 175 f6c8af 68 | 245 225 210 f5e1d2 69 | 127 0 255 7f00ff 70 | 87 59 59 573b3b 71 | 115 65 60 73413c 72 | 142 85 85 8e5555 73 | 171 115 115 ab7373 74 | 199 143 143 c78f8f 75 | 227 171 171 e3abab 76 | 248 210 218 f8d2da 77 | 227 199 171 e3c7ab 78 | 196 158 115 c49e73 79 | 143 115 87 8f7357 80 | 115 87 59 73573b 81 | 59 45 31 3b2d1f 82 | 65 65 35 414123 83 | 115 115 59 73733b 84 | 143 143 87 8f8f57 85 | 162 162 85 a2a255 86 | 181 181 114 b5b572 87 | 199 199 143 c7c78f 88 | 218 218 171 dadaab 89 | 237 237 199 ededc7 90 | 199 227 171 c7e3ab 91 | 171 199 143 abc78f 92 | 142 190 85 8ebe55 93 | 115 143 87 738f57 94 | 88 125 62 587d3e 95 | 70 80 50 465032 96 | 25 30 15 191e0f 97 | 35 80 55 235037 98 | 59 87 59 3b573b 99 | 80 100 80 506450 100 | 59 115 73 3b7349 101 | 87 143 87 578f57 102 | 115 171 115 73ab73 103 | 100 192 130 64c082 104 | 143 199 143 8fc78f 105 | 162 216 162 a2d8a2 106 | 225 248 250 e1f8fa 107 | 180 238 202 b4eeca 108 | 171 227 197 abe3c5 109 | 135 180 142 87b48e 110 | 80 125 95 507d5f 111 | 15 105 70 0f6946 112 | 30 45 35 1e2d23 113 | 35 65 70 234146 114 | 59 115 115 3b7373 115 | 100 171 171 64abab 116 | 143 199 199 8fc7c7 117 | 171 227 227 abe3e3 118 | 199 241 241 c7f1f1 119 | 190 210 240 bed2f0 120 | 171 199 227 abc7e3 121 | 168 185 220 a8b9dc 122 | 143 171 199 8fabc7 123 | 87 143 199 578fc7 124 | 87 115 143 57738f 125 | 59 87 115 3b5773 126 | 15 25 45 0f192d 127 | 31 31 59 1f1f3b 128 | 59 59 87 3b3b57 129 | 73 73 115 494973 130 | 87 87 143 57578f 131 | 115 110 170 736eaa 132 | 118 118 202 7676ca 133 | 143 143 199 8f8fc7 134 | 171 171 227 ababe3 135 | 208 218 248 d0daf8 136 | 227 227 255 e3e3ff 137 | 171 143 199 ab8fc7 138 | 143 87 199 8f57c7 139 | 115 87 143 73578f 140 | 87 59 115 573b73 141 | 60 35 60 3c233c 142 | 70 50 70 463246 143 | 114 64 114 724072 144 | 143 87 143 8f578f 145 | 171 87 171 ab57ab 146 | 171 115 171 ab73ab 147 | 235 172 225 ebace1 148 | 255 220 245 ffdcf5 149 | 227 199 227 e3c7e3 150 | 225 185 210 e1b9d2 151 | 215 160 190 d7a0be 152 | 199 143 185 c78fb9 153 | 200 125 160 c87da0 154 | 195 90 145 c35a91 155 | 75 40 55 4b2837 156 | 50 22 35 321623 157 | 40 10 30 280a1e 158 | 64 24 17 401811 159 | 98 24 0 621800 160 | 165 20 10 a5140a 161 | 218 32 16 da2010 162 | 213 82 74 d5524a 163 | 255 60 10 ff3c0a 164 | 245 90 50 f55a32 165 | 255 98 98 ff6262 166 | 246 189 49 f6bd31 167 | 255 165 60 ffa53c 168 | 215 155 15 d79b0f 169 | 218 110 10 da6e0a 170 | 180 90 0 b45a00 171 | 160 75 5 a04b05 172 | 95 50 20 5f3214 173 | 83 80 10 53500a 174 | 98 98 0 626200 175 | 140 128 90 8c805a 176 | 172 148 0 ac9400 177 | 177 177 10 b1b10a 178 | 230 213 90 e6d55a 179 | 255 213 16 ffd510 180 | 255 234 74 ffea4a 181 | 200 255 65 c8ff41 182 | 155 240 70 9bf046 183 | 150 220 25 96dc19 184 | 115 200 5 73c805 185 | 106 168 5 6aa805 186 | 60 110 20 3c6e14 187 | 40 52 5 283405 188 | 32 70 8 204608 189 | 12 92 12 0c5c0c 190 | 20 150 5 149605 191 | 10 215 10 0ad70a 192 | 20 230 10 14e60a 193 | 125 255 115 7dff73 194 | 75 240 90 4bf05a 195 | 0 197 20 00c514 196 | 5 180 80 05b450 197 | 28 140 78 1c8c4e 198 | 18 56 50 123832 199 | 18 152 128 129880 200 | 6 196 145 06c491 201 | 0 222 106 00de6a 202 | 45 235 168 2deba8 203 | 60 254 165 3cfea5 204 | 106 255 205 6affcd 205 | 145 235 255 91ebff 206 | 85 230 255 55e6ff 207 | 125 215 240 7dd7f0 208 | 8 222 213 08ded5 209 | 16 156 222 109cde 210 | 5 90 92 055a5c 211 | 22 44 82 162c52 212 | 15 55 125 0f377d 213 | 0 74 156 004a9c 214 | 50 100 150 326496 215 | 0 82 246 0052f6 216 | 24 106 189 186abd 217 | 35 120 220 2378dc 218 | 105 157 195 699dc3 219 | 74 164 255 4aa4ff 220 | 144 176 255 90b0ff 221 | 90 197 255 5ac5ff 222 | 190 185 250 beb9fa 223 | 120 110 240 786ef0 224 | 74 90 255 4a5aff 225 | 98 65 246 6241f6 226 | 60 60 245 3c3cf5 227 | 16 28 218 101cda 228 | 0 16 189 0010bd 229 | 35 16 148 231094 230 | 12 33 72 0c2148 231 | 80 16 176 5010b0 232 | 96 16 208 6010d0 233 | 135 50 210 8732d2 234 | 156 65 255 9c41ff 235 | 189 98 255 bd62ff 236 | 185 145 255 b991ff 237 | 215 165 255 d7a5ff 238 | 215 195 250 d7c3fa 239 | 248 198 252 f8c6fc 240 | 230 115 255 e673ff 241 | 255 82 255 ff52ff 242 | 218 32 224 da20e0 243 | 189 41 255 bd29ff 244 | 189 16 197 bd10c5 245 | 140 20 190 8c14be 246 | 90 24 123 5a187b 247 | 100 20 100 641464 248 | 65 0 98 410062 249 | 50 10 70 320a46 250 | 85 25 55 551937 251 | 160 25 130 a01982 252 | 200 0 120 c80078 253 | 255 80 191 ff50bf 254 | 255 106 197 ff6ac5 255 | 250 160 185 faa0b9 256 | 252 58 140 fc3a8c 257 | 230 30 120 e61e78 258 | 189 16 57 bd1039 259 | 152 52 77 98344d 260 | 145 20 55 911437 261 | -------------------------------------------------------------------------------- /tests/assets/font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftsf/nico/409eec1eeab05f5eb07d17343992638ce37f25ed/tests/assets/font.png -------------------------------------------------------------------------------- /tests/assets/font.png.dat: -------------------------------------------------------------------------------- 1 | !"#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{}~ -------------------------------------------------------------------------------- /tests/assets/map.json: -------------------------------------------------------------------------------- 1 | { "height":16, 2 | "infinite":false, 3 | "layers":[ 4 | { 5 | "data":[0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32], 6 | "height":16, 7 | "id":1, 8 | "name":"Tile Layer 1", 9 | "opacity":1, 10 | "type":"tilelayer", 11 | "visible":true, 12 | "width":16, 13 | "x":0, 14 | "y":0 15 | }], 16 | "nextlayerid":2, 17 | "nextobjectid":1, 18 | "orientation":"orthogonal", 19 | "renderorder":"right-down", 20 | "tiledversion":"2018.08.22", 21 | "tileheight":16, 22 | "tilesets":[ 23 | { 24 | "firstgid":1, 25 | "source":"spritesheet.json" 26 | }], 27 | "tilewidth":16, 28 | "type":"map", 29 | "version":1.2, 30 | "width":16 31 | } -------------------------------------------------------------------------------- /tests/assets/pal_aurora-1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftsf/nico/409eec1eeab05f5eb07d17343992638ce37f25ed/tests/assets/pal_aurora-1x.png -------------------------------------------------------------------------------- /tests/assets/palette.gpl: -------------------------------------------------------------------------------- 1 | GIMP Palette 2 | # 3 | 190 74 47 Untitled 4 | 216 118 68 Untitled 5 | 234 212 170 Untitled 6 | 228 166 114 Untitled 7 | 184 111 80 Untitled 8 | 116 63 57 Untitled 9 | 63 40 50 Untitled 10 | 158 40 53 Untitled 11 | 228 59 68 Untitled 12 | 247 118 34 Untitled 13 | 254 174 52 Untitled 14 | 254 231 97 Untitled 15 | 99 199 77 Untitled 16 | 62 137 72 Untitled 17 | 38 92 66 Untitled 18 | 25 60 62 Untitled 19 | 18 78 137 Untitled 20 | 0 149 233 Untitled 21 | 44 232 245 Untitled 22 | 255 255 255 Untitled 23 | 192 203 220 Untitled 24 | 139 155 180 Untitled 25 | 90 105 136 Untitled 26 | 58 68 102 Untitled 27 | 38 43 68 Untitled 28 | 255 0 68 Untitled 29 | 24 20 37 Untitled 30 | 104 56 108 Untitled 31 | 181 80 136 Untitled 32 | 246 117 122 Untitled 33 | 232 183 150 Untitled 34 | 194 133 105 Untitled 35 | -------------------------------------------------------------------------------- /tests/assets/palette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftsf/nico/409eec1eeab05f5eb07d17343992638ce37f25ed/tests/assets/palette.png -------------------------------------------------------------------------------- /tests/assets/paletteRGBA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftsf/nico/409eec1eeab05f5eb07d17343992638ce37f25ed/tests/assets/paletteRGBA.png -------------------------------------------------------------------------------- /tests/assets/spritesheet.json: -------------------------------------------------------------------------------- 1 | { "columns":8, 2 | "image":"spritesheet.png", 3 | "imageheight":128, 4 | "imagewidth":128, 5 | "margin":0, 6 | "name":"spritesheet", 7 | "spacing":0, 8 | "tilecount":64, 9 | "tiledversion":"2018.08.22", 10 | "tileheight":16, 11 | "tilewidth":16, 12 | "type":"tileset", 13 | "version":1.2 14 | } -------------------------------------------------------------------------------- /tests/assets/spritesheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftsf/nico/409eec1eeab05f5eb07d17343992638ce37f25ed/tests/assets/spritesheet.png -------------------------------------------------------------------------------- /tests/assets/spritesheetRGB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftsf/nico/409eec1eeab05f5eb07d17343992638ce37f25ed/tests/assets/spritesheetRGB.png -------------------------------------------------------------------------------- /tests/assets/spritesheetRGBA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftsf/nico/409eec1eeab05f5eb07d17343992638ce37f25ed/tests/assets/spritesheetRGBA.png -------------------------------------------------------------------------------- /tests/camera.nim: -------------------------------------------------------------------------------- 1 | import unittest 2 | import nico 3 | 4 | suite "camera": 5 | setup: 6 | nico.init("nico","tests") 7 | nico.createWindow("test",32,32,1,false) 8 | 9 | teardown: 10 | nico.shutdown() 11 | 12 | test "camera": 13 | setCamera() 14 | setColor(1) 15 | boxfill(0,0,1,1) 16 | check(pgetRaw(0,0) == 1) 17 | 18 | setColor(2) 19 | setCamera(-8,-8) 20 | boxfill(0,0,1,1) 21 | check(pgetRaw(8,8) == 2) 22 | 23 | setColor(3) 24 | setCamera(-16,-16) 25 | print("X", 0, 0) 26 | check(pgetRaw(16,16) == 3) 27 | -------------------------------------------------------------------------------- /tests/config.nim: -------------------------------------------------------------------------------- 1 | import unittest 2 | import nico 3 | 4 | suite "config": 5 | setup: 6 | nico.init("nico","tests") 7 | nico.createWindow("test",32,32,1) 8 | 9 | teardown: 10 | nico.shutdown() 11 | 12 | test "config": 13 | loadConfig() 14 | 15 | test "updateConfigValue": 16 | updateConfigValue("Test", "entry", "value") 17 | saveConfig() 18 | 19 | test "getConfigValue": 20 | loadConfig() 21 | check(getConfigValue("Test", "entry") == "value") 22 | -------------------------------------------------------------------------------- /tests/copymem.nim: -------------------------------------------------------------------------------- 1 | import unittest 2 | import nico 3 | 4 | suite "copymem": 5 | setup: 6 | nico.init("nico","tests") 7 | nico.createWindow("test",32,32,1) 8 | 9 | teardown: 10 | nico.shutdown() 11 | 12 | test "copyMemToScreen": 13 | var buffer = newSeq[uint8](512) 14 | for i in 0..<512: 15 | buffer[i] = rnd(16).uint8 16 | for i in 0..10000: 17 | copyMemToScreen(rnd(128),rnd(128), buffer) 18 | 19 | test "copyMemToScreen negative": 20 | var buffer = newSeq[uint8](512) 21 | for i in 0..<512: 22 | buffer[i] = rnd(16).uint8 23 | for i in 0..10000: 24 | copyMemToScreen(rnd(128)-64,rnd(128)-64, buffer) 25 | 26 | test "copyPixelsToMem": 27 | var buffer = newSeq[uint8](512) 28 | for i in 0..10000: 29 | copyPixelsToMem(rnd(128),rnd(128), buffer) 30 | -------------------------------------------------------------------------------- /tests/fonts.nim: -------------------------------------------------------------------------------- 1 | import unittest 2 | import nico 3 | 4 | suite "fonts": 5 | setup: 6 | nico.init("nico","tests") 7 | nico.createWindow("test",32,32,1,false) 8 | 9 | teardown: 10 | nico.shutdown() 11 | 12 | test "loadFont": 13 | loadFont(0,"font.png") 14 | print("hello world",0,0) 15 | flip() 16 | 17 | 18 | test "loadFont": 19 | print("hello default font",0,0) 20 | flip() 21 | -------------------------------------------------------------------------------- /tests/palette.nim: -------------------------------------------------------------------------------- 1 | import unittest 2 | import nico 3 | 4 | suite "palette": 5 | setup: 6 | nico.init("nico","tests") 7 | nico.createWindow("test",32,32,1,false) 8 | 9 | teardown: 10 | nico.shutdown() 11 | 12 | test "loadPaletteFromGPL": 13 | setPalette(loadPaletteFromGPL("palette.gpl")) 14 | pset(0,0,1) 15 | check(palSize() == 32) 16 | check(pgetRGB(0,0) == (216'u8,118'u8,68'u8)) 17 | 18 | test "loadPaletteFromGPL2": 19 | setPalette(loadPaletteFromGPL("aurora.gpl")) 20 | check(palCol(16) == (0'u8, 127'u8, 127'u8)) 21 | pset(0,0,16) 22 | check(palSize() == 256) 23 | check(pgetRGB(0,0) == (0'u8, 127'u8, 127'u8)) 24 | 25 | test "loadPaletteFromHexString": 26 | # dawnbringer 16 palette 27 | setPalette(loadPaletteFromHexString("140c1c44243430346d4e4a4e854c30346524d04648757161597dced27d2c8595a16daa2cd2aa996dc2cadad45edeeed6")) 28 | check(palSize() == 16) 29 | check(pgetRGB(0,0) == (0x14'u8, 0x0C'u8, 0x1C'u8)) 30 | 31 | test "loadPaletteFromImage": 32 | setPalette(loadPaletteFromImage("palette.png")) 33 | check(palSize() == 32) 34 | pset(0,0,8) 35 | 36 | test "loadPaletteFromImage2": 37 | setPalette(loadPaletteFromImage("pal_aurora-1x.png")) 38 | check(palSize() == 256) 39 | check(palCol(16) == (0'u8, 127'u8, 127'u8)) 40 | pset(0,0,16) 41 | check(pgetRGB(0,0) == (0'u8, 127'u8, 127'u8)) 42 | 43 | test "loadPaletteFromImageRGBA": 44 | setPalette(loadPaletteFromImage("paletteRGBA.png")) 45 | check(palSize() == 35) 46 | pset(0,0,8) 47 | 48 | test "loadPalettePico8Extra": 49 | setPalette(loadPalettePico8Extra()) 50 | check(palSize() == 32) 51 | pset(0,0,16) 52 | 53 | test "loadPaletteCGA": 54 | setPalette(loadPaletteCGA()) 55 | pset(0,0,1) 56 | check(palSize() == 4) 57 | check(pgetRGB(0,0) == (85'u8,255'u8,255'u8)) 58 | -------------------------------------------------------------------------------- /tests/rgba.nim: -------------------------------------------------------------------------------- 1 | import unittest 2 | import nico 3 | 4 | suite "palette": 5 | setup: 6 | nico.init("nico","tests") 7 | nico.createWindow("test",32,32,1,false) 8 | 9 | teardown: 10 | nico.shutdown() 11 | 12 | test "loadSpritesheet": 13 | loadSpritesheet(0, "spritesheet.png", 16, 16) 14 | spr(10,0,0) 15 | check(pgetRGB(0,0) == palCol(10)) 16 | 17 | test "loadSpritesheetRGBA": 18 | loadSpritesheet(0, "spritesheetRGBA.png", 16, 16) 19 | spr(10,0,0) 20 | check(pgetRGB(0,0) == palCol(10)) 21 | 22 | test "loadSpritesheetRGB": 23 | loadSpritesheet(0, "spritesheetRGB.png", 16, 16) 24 | spr(10,0,0) 25 | check(pgetRGB(0,0) == palCol(10)) 26 | -------------------------------------------------------------------------------- /tests/tilemap.nim: -------------------------------------------------------------------------------- 1 | import unittest 2 | import nico 3 | 4 | suite "config": 5 | setup: 6 | nico.init("nico","tests") 7 | nico.createWindow("test",32,32,1) 8 | 9 | teardown: 10 | nico.shutdown() 11 | 12 | test "loadMap": 13 | loadSpriteSheet(0, "spritesheet.png", 16, 16) 14 | loadMap(0, "map.json") 15 | setMap(0) 16 | 17 | check(mapWidth() == 16) 18 | check(mapHeight() == 16) 19 | 20 | test "wrongTileSize": 21 | expect Exception: 22 | loadSpriteSheet(0, "spritesheet.png", 15, 15) 23 | 24 | test "mset mget": 25 | loadSpriteSheet(0, "spritesheet.png", 16, 16) 26 | loadMap(0, "map.json") 27 | setMap(0) 28 | 29 | check(mget(8,8) == 8) 30 | 31 | mset(8,8,0) 32 | check(mget(8,8) == 0) 33 | 34 | test "mapDraw": 35 | loadSpriteSheet(0, "spritesheet.png", 16, 16) 36 | loadMap(0, "map.json") 37 | setMap(0) 38 | 39 | mapDraw(0,0,mapWidth(),mapHeight(),0,0) 40 | 41 | check(pget(16*1, 16*1) == 0) 42 | check(pget(16*1+8, 16*1+8) == 1) 43 | 44 | test "mapDrawFiltered": 45 | loadSpriteSheet(0, "spritesheet.png", 16, 16) 46 | loadMap(0, "map.json") 47 | setMap(0) 48 | 49 | # set tile 17 to have bit 0 set 50 | fset(17, 0, true) 51 | 52 | # set map filter to only draw tiles with 0 bit set 53 | mapFilter(0, true) 54 | 55 | mapDraw(0,0,mapWidth(),mapHeight(),0,0) 56 | 57 | # this tile (1,0) should not be drawn as it is tile 2 which has no flag bit set 58 | check(pget(16*1+8, 16*0+8) == 0) 59 | 60 | # this tile (1,1) should be drawn as it is tile 17 which set set flag bit 0 61 | check(pget(16*1+8, 16*1+8) == 1) 62 | --------------------------------------------------------------------------------