Skip to content

Instantly share code, notes, and snippets.

@rezich
Created June 1, 2017 07:40
Show Gist options
  • Save rezich/d8149f22eb0a776384485a6a62df1d5f to your computer and use it in GitHub Desktop.
Save rezich/d8149f22eb0a776384485a6a62df1d5f to your computer and use it in GitHub Desktop.
# nch -- nim-based crappy hellhole
import sdl2, sdl2/gfx, sdl2.image, sdl2.ttf, basic2d, random, math
type
Input {.pure.} = enum none, quit, action
InputState {.pure.} = enum up, pressed, down, released
Game = ref object
input: array[Input, bool]
inputLast: array[Input, bool]
states: seq[State]
ren: RendererPtr
exiting: bool
fpsman: FpsManager
State = ref object
name: string
init: proc (state: State)
step: proc (state: State, dt: float)
draw: proc (state: State)
exit: proc (state: State)
stepping: bool
drawing: bool
exiting: bool
game: Game
SDLException = object of Exception
# TODO: stop using this lol
let nullState = State(
name: "INVALID STATE"
)
#
## GAME ##
#
proc newGame(ren: RendererPtr): Game =
new result
result.ren = ren
result.exiting = false
result.states = @[]
result.fpsman.init()
result.fpsman.setFramerate(60)
return result
proc hasState(game: Game): bool =
return game.states.len > 0
proc curState(game: Game): State =
if game.hasState:
var i = 0;
while (i < game.states.len):
if game.states[game.states.high() - i].stepping:
return game.states[game.states.high() - i]
i += 1
echo "tried to get current State when there were none"
return nullState
proc pushState(game: Game, state: State) =
var newState: State
deepCopy(newState, state)
newState.stepping = true
newState.drawing = true
newState.exiting = false
newState.game = game
game.states.add(newState)
proc setState(game: Game, state: State) =
if game.hasState:
game.curState.stepping = false
game.pushState(state)
game.curState.stepping = true
proc exit(state: State) =
state.exiting = true
proc cleanupStates(game: Game) =
var i = 0
while i < game.states.len:
if game.states[i].exiting:
if (i > 0):
game.states[i - 1].stepping = true
game.states.delete(i)
else:
i += 1
proc toInput(key: Scancode): Input =
case key
of SDL_SCANCODE_SPACE: Input.action
of SDL_SCANCODE_ESCAPE: Input.quit
else: Input.none
proc handleInput(game: Game) =
shallowCopy(game.inputLast, game.input)
var event = defaultEvent
while pollEvent(event):
case event.kind
of QuitEvent:
game.exiting = true
of KeyDown:
game.input[event.key.keysym.scancode.toInput] = true
of KeyUp:
game.input[event.key.keysym.scancode.toInput] = false
else:
discard
proc getInput(game: Game, input: Input): InputState =
if not game.input[input] and not game.inputLast[input]: return InputState.up
if game.input[input] and not game.inputLast[input]: return InputState.pressed
if game.input[input] and game.inputLast[input]: return InputState.down
return InputState.released
proc step(game: Game) =
# quit
if game.getInput(Input.quit) == InputState.pressed:
game.exiting = true
let dt = game.fpsman.getFramerate() / 1000
# state step
for state in game.states:
if state.stepping and not state.step.isNil:
state.step(state, dt)
proc draw(game: Game) =
# state draw
for state in game.states:
if state.drawing and not state.draw.isNil:
state.draw(state)
game.ren.present()
template sdlFailIf(cond: typed, reason: string) =
if cond: raise SDLException.newException(
reason & ", SDL error: " & $getError())
#
## STATES ##
#
# clear screen, state that goes behind everything always probably
let clearScreenState = State(
name: "clearScreenState",
draw: proc (state: State) =
let ren = state.game.ren
ren.setDrawColor(0, 0, 0, 255)
ren.clear()
)
# title screen
let titleScreen = State(
name: "TitleScreen",
step: proc (state: State, dt: float) =
if getInput(state.game, Input.action) == InputState.pressed:
echo "title action!"
,
draw: proc (state: State) =
let ren = state.game.ren
ren.setDrawColor(0, 255, 0, 255)
ren.clear()
ren.setDrawColor(color(255, 255, 255, 255))
ren.drawLine(0.cint, 0.cint, 160.cint, 120.cint)
ren.setDrawColor(color(255, 255, 255, 255))
ren.drawLine(160.cint, 0.cint, 0.cint, 120.cint)
)
# splash screen
let splashScreen = State(
name: "SplashScreen",
step: proc (state: State, dt: float) =
if getInput(state.game, Input.action) == InputState.pressed:
echo "splash action!"
exit(state)
,
draw: proc (state: State) =
let game = state.game
game.ren.setDrawColor(255, 0, 0, 255)
game.ren.clear()
)
#
## MAIN ##
#
proc main =
sdlFailIf(not sdl2.init(INIT_VIDEO or INIT_TIMER or INIT_EVENTS)):
"SDL2 initialization failed"
defer: sdl2.quit()
sdlFailIf(not setHint("SDL_RENDER_SCALE_QUALITY", "0")):
"Linear texture filtering could not be enabled"
let window = createWindow(title = "nch",
x = SDL_WINDOWPOS_CENTERED, y = SDL_WINDOWPOS_CENTERED,
w = 160, h = 120, flags = SDL_WINDOW_SHOWN)
sdlFailIf window.isNil: "Window could not be created"
defer: window.destroy()
let renderer = window.createRenderer(index = -1,
flags = Renderer_Accelerated or Renderer_PresentVsync)
sdlFailIf renderer.isNil: "Renderer could not be created"
defer: renderer.destroy()
renderer.setDrawColor(0, 0, 0, 255)
var game = newGame(renderer)
game.setState(clearScreenState)
game.setState(titleScreen)
game.setState(splashScreen)
# game loop
while not game.exiting:
game.handleInput()
game.step()
game.draw()
game.cleanupStates()
game.fpsman.delay()
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment