Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
#- windows-latest
#- macOS-latest
nim_version:
- '1.4.0'
- '1.6.12'
- 'stable'
needs: before
steps:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ NICO is a simple game framework for the [Nim](http://nim-lang.org/) programming
* For web builds you'll need [Emscripten](https://emscripten.org/docs/getting_started/downloads.html).
* From your project directory run ```nimble webr``` to build for web in release mode.
* From your project directory run ```nimble webd``` to build for web in debug mode.
* From your project directory run ```nimble runweb``` or ```emrun projectname.html ``` open browser run it.

## Learning
* [API Documentation](API.md)
Expand Down
3 changes: 3 additions & 0 deletions exampleApp/exampleApp.nimble
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ task runr, "Runs exampleApp for current platform":
task rund, "Runs debug exampleApp for current platform":
exec &"nim c -r {debugOpts} -o:exampleApp src/main.nim"

task runweb, "Runs web exampleApp for current platform":
exec &"emrun exampleApp.html"

task release, "Builds exampleApp for current platform":
exec &"nim c {releaseOpts} -o:exampleApp src/main.nim"

Expand Down
Binary file added examples/assets/ChillBitmap7x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions examples/assets/ChillBitmap7x.png.dat

Large diffs are not rendered by default.

Binary file added examples/assets/quan.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions examples/assets/quan.png.dat

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions examples/nim.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--path:".."
119 changes: 119 additions & 0 deletions examples/unicode.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import nico

var messages = @[
("English", "!\"#$%&'()*+,-./0123456789\n:;<=>?@abcdefghijklmnopqrstuvwxyz\n[\\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{}~"),
("German", "Falsches Üben von Xylophonmusik quält jeden größeren Zwerg"),
("German", "Beiß nicht in die Hand, die dich füttert."),
("German", "Außerordentliche Übel erfordern außerordentliche Mittel."),
("Armenian", "Կրնամ ապակի ուտել և ինծի անհանգիստ չըներ"),
("Armenian", "Երբ որ կացինը եկաւ անտառ, ծառերը ասացին... «Կոտը մերոնցից է:»"),
("Armenian", "Գառը՝ գարնան, ձիւնը՝ ձմռան"),
("Polish", "Jeżu klątw, spłódź Finom część gry hańb!"),
("Polish", "Dobrymi chęciami jest piekło wybrukowane."),
("Romanian", "Îți mulțumesc că ai ales nico.\nȘi sper să ai o zi bună!"),
("Russian", "Эх, чужак, общий съём цен шляп (юфть) вдрызг!"),
("Russian", "Я люблю nico!"),
("Russian", "Молчи, скрывайся и таи\nИ чувства и мечты свои –\nПускай в душевной глубине\nИ всходят и зайдут оне\nКак звезды ясные в ночи-\nЛюбуйся ими – и молчи."),
("French", "Voix ambiguë d’un cœur qui au zéphyr préfère les jattes de kiwi"),
("Spanish", "Benjamín pidió una bebida de kiwi y fresa; Noé, sin vergüenza, la más exquisita champaña del menú."),
("Greek", "Ταχίστη αλώπηξ βαφής ψημένη γη, δρασκελίζει υπέρ νωθρού κυνός"),
("Greek", "Η καλύτερη άμυνα είναι η επίθεση."),
("Greek", "Χρόνια και ζαμάνια!"),
("Greek", "Πώς τα πας σήμερα;"),
("Chinese", "我能吞下玻璃而不伤身体。"),
("Chinese", "你吃了吗?"),
("Chinese", "不作不死。"),
("Chinese", "最近好吗?"),
("Chinese", "塞翁失马,焉知非福。"),
("Chinese", "千军易得, 一将难求"),
("Chinese", "万事开头难。"),
("Chinese", "风无常顺,兵无常胜。"),
("Chinese", "活到老,学到老。"),
("Chinese", "一言既出,驷马难追。"),
("Chinese", "路遥知马力,日久见人心"),
("Chinese", "有理走遍天下,无理寸步难行。"),
("Japanese", "猿も木から落ちる"),
("Japanese", "亀の甲より年の功"),
("Japanese", "うらやまし 思ひ切る時 猫の恋"),
("Japanese", "虎穴に入らずんば虎子を得ず。"),
("Japanese", "二兎を追う者は一兎をも得ず。"),
("Japanese", "馬鹿は死ななきゃ治らない。"),
("Japanese", "枯野路に 影かさなりて わかれけり"),
("Japanese", "繰り返し麦の畝縫ふ胡蝶哉"),
("Korean", "아득한 바다 위에 갈매기 두엇 날아 돈다.\n너훌너훌 시를 쓴다. 모르는 나라 글자다.\n널따란 하늘 복판에 나도 같이 시를 쓴다."),
("Korean", "제 눈에 안경이다"),
("Korean", "꿩 먹고 알 먹는다"),
("Korean", "로마는 하루아침에 이루어진 것이 아니다"),
("Korean", "고생 끝에 낙이 온다"),
("Korean", "개천에서 용 난다"),
("Korean", "안녕하세요?"),
("Korean", "만나서 반갑습니다"),
("Korean", "한국말 하실 줄 아세요?"),
]

var langs = @[
"quan.png",
"ChillBitmap7x.png",
"font.png"
]
var langIdx = 0
proc gameInit() =
for i in 0..<langs.len:
loadFont(i, langs[i])

setWindowTitle("[ " & langs[langIdx][0..^5] & " ] font")
discard

var iFrame: int
var isPrintr: bool
proc gameUpdate(dt: float32) =
if keyp(K_ESCAPE): shutdown()
if keyp(K_SPACE) or iFrame > 1000:
langIdx.inc
if langIdx >= langs.len:
isPrintr = not isPrintr
langIdx = langIdx mod langs.len
iFrame = 0
setFont(langIdx)
setWindowTitle("[ " & langs[langIdx][0..^5] & " ] font")
iFrame.inc
discard

import strformat
proc gameDraw() =
cls()
var
rcolor {.global.} = rnd(1, 15)
scale {.global.} = 1
block unicode:
#break
var
color = rcolor
h,i = 0
if iFrame < 2: rcolor = rnd(1, 15)
for (language, msg) in messages:
if h > screenHeight:
cls()
h = 0
setColor(color)
if not isPrintr:
print(&"{language}: {msg}", 0, h, scale)
else:
printr(&"{language}: {msg}", screenWidth, h, scale)
h += msg.textHeight(scale)
color.inc
color = color mod 15 + 1 # rnd(1,16)
i.inc
if i > iFrame div 20: break
discard

setColor(6)
printr(&"frame: {iFrame}", screenWidth, screenHeight - fontHeight())
printr("font: " & langs[langIdx][0..^5], screenWidth, screenHeight - fontHeight() * 2 * scale - scale)
discard

nico.init("nico", "unicode test")
nico.createWindow("nico", 384, 384, 2, false)
fixedSize(true)
integerScale(true)
nico.run(gameInit, gameUpdate, gameDraw)
106 changes: 60 additions & 46 deletions nico.nim
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import nico/backends/common
import tables
import unicode
import std/[tables, unicode, os]

import nico/backends/common
import nico/keycodes
export nico.keycodes

Expand All @@ -11,8 +10,6 @@ export spritedraw
when not defined(js):
import nico/backends/sdl2 as backend

import os

export StencilMode
export StencilBlend

Expand Down Expand Up @@ -154,6 +151,7 @@ proc printc*(text: string, x,y: Pint, scale: Pint = 1) # centered
proc printr*(text: string, x,y: Pint, scale: Pint = 1) # right aligned

proc textWidth*(text: string, scale: Pint = 1): Pint
proc textHeight*(text: string, scale: Pint = 1): Pint
proc glyphWidth*(c: Rune, scale: Pint = 1): Pint
proc glyphWidth*(c: char, scale: Pint = 1): Pint

Expand Down Expand Up @@ -738,7 +736,7 @@ proc ditherADitherXor*(v: float32, a = 149,b = 1234, c = 511) =
proc ditherPass(x,y: int): bool {.inline.} =
if gDitherMode == DitherNone:
return true

let x = floorMod(x + gDitherOffsetX, screenWidth)
let y = floorMod(y + gDitherOffsetY, screenHeight)

Expand Down Expand Up @@ -2221,10 +2219,17 @@ proc glyph*(c: Rune, x,y: Pint, scale: Pint = 1): Pint =
## draw a glyph from the current font
if currentFont == nil:
raise newException(Exception, "No font selected")

var src: Rect
if not currentFont.rects.hasKey(c):
return
let src: Rect = currentFont.rects[c]
let dst: Rect = (x.int, y.int, src.w * scale, src.h * scale)
let unknown = '?'.Rune
if not currentFont.rects.hasKey(unknown):
return
src = currentFont.rects[unknown]
else:
src = currentFont.rects[c]

let dst: Rect = (x.int, y.int, src.w * scale.int, src.h * scale.int)
try:
fontBlit(currentFont, src, dst, currentColor)
except IndexDefect:
Expand All @@ -2240,32 +2245,34 @@ proc fontHeight*(): Pint =
## returns the height of the current font in pixels
if currentFont == nil:
return 0
return currentFont.rects[Rune(' ')].h
return currentFontHeight

proc print*(text: string, x,y: Pint, scale: Pint = 1) =
## prints a string using the current font
if currentFont == nil:
raise newException(Exception, "No font selected")
var x = x
var y = y
let ix = x
let lineHeight = fontHeight() * scale + scale
var tx = x; var ty = y
let ix = x; let lineHeight = fontHeight() * scale + scale
for line in text.splitLines:
for c in line.runes:
x += glyph(c, x, y, scale)
x = ix
y += lineHeight
tx += glyph(c, tx, ty, scale)
tx = ix
ty += lineHeight

proc print*(text: string, scale: Pint = 1) =
## prints a string using the current font at cursor position
if currentFont == nil:
raise newException(Exception, "No font selected")
var x = cursorX
let y = cursorY
let lineHeight = fontHeight() * scale + scale
for c in text.runes:
x += glyph(c, x, y, scale)
cursorY += lineHeight
print(text, cursorX, cursorY, scale)

proc printr*(text: string, x,y: Pint, scale: Pint = 1) =
## prints a string in the current font, right aligned
let width = textWidth(text, scale)
print(text, x-width, y, scale)

proc printc*(text: string, x,y: Pint, scale: Pint = 1) =
## prints a string in the current font, center aligned
let width = textWidth(text, scale)
print(text, x-(width div 2), y, scale)


proc glyphWidth*(c: Rune, scale: Pint = 1): Pint =
## returns the width of the glyph in the current font
Expand All @@ -2283,20 +2290,23 @@ proc textWidth*(text: string, scale: Pint = 1): Pint =
## returns the width of a string in the current font
if currentFont == nil:
raise newException(Exception, "No font selected")
for c in text.runes:
if not currentFont.rects.hasKey(c):
raise newException(Exception, "character not in font: '" & $c & "'")
result += currentFont.rects[c].w*scale + scale

proc printr*(text: string, x,y: Pint, scale: Pint = 1) =
## prints a string in the current font, right aligned
let width = textWidth(text, scale)
print(text, x-width, y, scale)
for line in text.splitLines:
var lineWidth = 0
for c in line.runes:
if not currentFont.rects.hasKey(c):
let unknown = '?'.Rune
if not currentFont.rects.hasKey(unknown):
raise newException(Exception, "character not in font: '" & $c & "' = " & $(c.int))
lineWidth += currentFont.rects[unknown].w*scale + scale
else:
lineWidth += currentFont.rects[c].w*scale + scale
if result < lineWidth:
result = lineWidth

proc printc*(text: string, x,y: Pint, scale: Pint = 1) =
## prints a string in the current font, center aligned
let width = textWidth(text, scale)
print(text, x-(width div 2), y, scale)
proc textHeight*(text: string, scale: Pint = 1): Pint =
## returns the height of a string in the current font
let lineslen = text.countLines()
result = lineslen * fontHeight() * scale + lineslen * scale

proc copy*(sx,sy,dx,dy,w,h: Pint) =
## copy a rectangle of w by h pixels from sx,sy to dx,dy
Expand Down Expand Up @@ -2532,12 +2542,12 @@ proc spr*(drawer: SpriteDraw, x,y:Pint)=
proc sprOverlap*(a,b : SpriteDraw): bool=
##Will return true if the sprites overlap
setSpritesheet(a.spriteSheet)
let
let
aSprRect = getSprRect(a.spriteIndex,a.w,a.h)
aRect: Rect = (a.x, a.y, aSprRect.w, aSprRect.h)

if(a.spritesheet != b.spritesheet): setSpritesheet(b.spriteSheet)
let
let
bSprRect = getSprRect(b.spriteIndex,b.w,b.h)
bRect: Rect = (b.x, b.y, bSprRect.w, bSprRect.h)

Expand All @@ -2552,7 +2562,7 @@ proc sprOverlap*(a,b : SpriteDraw): bool=
bXRelative = xOverlap - b.x
bYRelative = yOverlap - b.y

var
var
surfA = spritesheets[a.spriteSheet]
surfB = spritesheets[b.spriteSheet]
indA = 0
Expand All @@ -2561,7 +2571,7 @@ proc sprOverlap*(a,b : SpriteDraw): bool=
#Foreach pixel in the overlap check the colour there
for xSamp in 0..<wOverlap:
for ySamp in 0..<hOverlap:
var
var
aX = aSprRect.x + xSamp + aXRelative
aY = aSprRect.y + ySamp + aYRelative
bX = bSprRect.x + xSamp + bxRelative
Expand All @@ -2572,7 +2582,7 @@ proc sprOverlap*(a,b : SpriteDraw): bool=
if(a.flipX):
aX = aSprRect.x + (aSprRect.w - (xSamp + aXRelative)) - 1
if(a.flipY):
aY = aSprRect.y + (aSprRect.h - (ySamp + aYRelative)) - 1
aY = aSprRect.y + (aSprRect.h - (ySamp + aYRelative)) - 1

if(b.flipX):
bX = bSprRect.x + (aSprRect.w - (xSamp + bXRelative)) - 1
Expand All @@ -2584,7 +2594,7 @@ proc sprOverlap*(a,b : SpriteDraw): bool=
indB = bX + bY * surfB.w
if(indA < surfA.data.len and indB < surfB.data.len):#Shouldnt ever happen but errors must be checked
if(surfA.data[indA] > 0 and surfB.data[indB] > 0): #Using 0 as of now for alpha check
return true
return true

return false

Expand Down Expand Up @@ -2955,10 +2965,14 @@ proc getControllers*(): seq[NicoController] =

proc setFont*(fontId: FontId) =
## sets the active font to be used by future print calls
if fontId > fonts.len:
if fontId > fonts.len: return
let font = fonts[fontId]
if font == nil:
echo "Haven't font id: " & $fontId
return
currentFont = font
currentFontId = fontId
currentFont = fonts[currentFontId]
currentFontHeight = currentFont.rects[Rune(' ')].h

proc getFont*(): FontId =
## gets the current font id
Expand Down
Loading