Skip to content

Instantly share code, notes, and snippets.

@fowlmouth
Last active December 17, 2015 11:39
Show Gist options
  • Save fowlmouth/5603457 to your computer and use it in GitHub Desktop.
Save fowlmouth/5603457 to your computer and use it in GitHub Desktop.
import fowltek/entitty, fowltek/sdl2/engine
entitty_imports
import_all_sdl2_modules
import fowltek/vector_math
type TVector2f* = TVector2[float]
proc getPos* : TVector2f {.unicast.}
proc update* (dt: float) {.multicast.}
proc draw* (R: PRenderer) {.unicast.}
proc debugDraw* (R: PRenderer) {.multicast.}
proc handleEvent* (X: var TEvent): bool {.unicast.}
proc accelerate* {.unicast.}
proc rev_accel* {.unicast.}
proc debugStrCollect* (result: var seq[string]) {.multicast.}
template debugStrImpl*(ty; body: stmt): stmt {.immediate.} =
msg_impl(ty, "debugStrCollect") do (result: var seq[string]):
body
proc debugStr*(E: PEntity): seq[string] =
result = @[]
E.debugStrCollect result
type Pos* = TVector2f
msg_impl(Pos, getPos) do -> TVector2f: entity[Pos]
type Velocity* = object
vec: TVector2f
Velocity.requiresComponent Pos
msg_impl(Velocity, update) do (dt: float):
entity[Pos] += entity[Velocity].vec * dt
entity[Velocity].vec *= 0.99
msg_impl(Velocity, debugDraw) do(R: PRenderer):
let p = entity[Pos].addr
let v = entity[Velocity].vec.addr
R.lineRGBA p.x.int16, p.y.int16,
(p.x + v.x).int16, (p.y + v.y).int16,
0,255,0,255
type Circle* = object
radius: float
Circle.requiresComponent Pos
Circle.setInitializer proc(x: PEntity) = x[Circle].radius = 10.0
proc debugDraw_circ (some: PEntity; R: PRenderer) =
let pos = some[Pos].addr
R.circleRGBA pos.x.int16, pos.y.int16, some[Circle].radius.int16, 255,0,0,255
Circle.addMulticastMsg "debugDraw", debugDraw_circ
msg_impl(Circle, draw) do (R: PRenderer):
let pos = entity[Pos].addr
R.circleRGBA pos.x.int16, pos.y.int16, entity[Circle].radius.int16, 255,0,0,255
type Orientation* = object
angle_radians: float
proc turnRight* {.unicast.}
proc turnLeft* {.unicast.}
msg_impl(Orientation, "turnRight") do :
entity[Orientation].angle_radians -= 0.1
msg_impl(Orientation, "turnLeft") do :
entity[Orientation].angle_radians += 0.1
msg_impl(Orientation, debugDraw) do (R: PRenderer) :
if entity.hasComponent(Pos):
var offs = vectorForAngle(entity[Orientation].angle_radians)
if entity.hasComponent(Circle):
offs *= entity[Circle].radius
let p = entity[Pos].addr
R.lineRGBA p.x.int16, p.y.int16,
(p.x + offs.x).int16, (p.y + offs.y).int16,
255,0,0,255
type Acceleration* = object
vec: TVector2f
Acceleration.requiresComponent Velocity, Orientation
msg_impl(Acceleration, update) do (dt: float):
entity[Velocity].vec += entity[Acceleration].vec
entity[Acceleration].reset
msg_impl(Acceleration, accelerate) do :
entity[Acceleration].vec += entity[Orientation].angle_radians.vectorForAngle * 10.0
msg_impl(Acceleration, rev_accel) do :
entity[Acceleration].vec += entity[Orientation].angle_radians.vectorForAngle * -10.0
type
InputState* = object
accel: TAccel
turning: TTurn
TInputKind* = enum Pressed, Released
TAccel* = enum AccelIdle, AccelForward, AccelReverse
TTurn* = enum NoTurn, RightTurn, LeftTurn
proc setTurning*(dir: TTurn; pressed: TInputKind) {.unicast.}
proc setAccel*(acc: TAccel; pressed: TInputKind){.unicast.}
debugStrImpl(InputState):
result.add ($entity[InputState])
msg_impl(InputState, setAccel) do (acc: TAccel; kind: TInputKind):
case kind
of Pressed:
entity[InputState].accel = acc
else:
if acc == entity[InputState].accel:
entity[InputState].accel = AccelIdle
msg_impl(InputState, setTurning) do(dir: TTurn; kind: TInputKind):
case Kind
of Pressed:
entity[InputState].turning = dir
else:
if dir == entity[InputState].turning:
entity[InputState].turning = NoTurn
msg_impl(InputState, update) do (dt: float):
case entity[InputState].accel
of AccelForward: entity.accelerate()
of AccelReverse: entity.rev_accel()
else: nil
case entity[InputState].turning
of RightTurn: entity.turnRight
of LeftTurn: entity.turnLeft
else: nil
type
THIDCallback = proc(E: PEntity; X: var sdl2.TEvent): bool
HID_Controller* = object
cb: THIDCallback
connected: bool
HID_Controller.requiresComponent InputState
proc hid_nop(E: PEntity; X: var sdl2.TEvent): bool = false
HID_Controller.setInitializer proc(x: PEntity) =
x[HID_Controller].cb = hid_nop
x[HID_Controller].connected = false
msg_impl(HID_Controller, handleEvent) do (event: var sdl2.TEvent) -> bool:
if entity[HID_Controller].connected: return entity[HID_Controller].cb(entity, event)
import unsigned
proc hid_keyboard_handler (entity: PEntity; evt: var sdl2.TEvent): bool =
case evt.kind
of keyDown, keyUP:
let k = evKeyboard(evt)
if k.repeat != 0'u8: return
var p = (if k.kind == keyUp: Released else: Pressed)
result = true
case k.keysym.sym
of K_UP:
entity.setAccel AccelForward, p
of K_DOWN:
entity.setAccel AccelReverse, p
of K_LEFT:
entity.setTurning LeftTurn, p
of K_RIGHT:
entity.setTurning RightTurn, p
else: result = false
else:nil
proc setupKeyboard*(x: var HID_Controller) =
x.cb = hid_keyboard_handler
x.connected = true
type # Keeps an entity within a boundary
Bounded = object
rect: sdl2.TRect
checkEnt: proc(entity: PEntity; bounds: var Bounded)
#defcomponent Bounded
Bounded.requiresComponent Pos, Velocity
proc right*(some: ptr TRect): cint {.inline.} = some.x + some.w
proc bottom*(some: ptr TRect): cint {.inline.} = some.y + some.h
proc newBounded(x, y, w, h: int): Bounded =
result.rect = rect(x.cint, y.cint, w.cint, h.cint)
proc setToroidal(bounds: var Bounded) =
bounds.checkEnt = proc(entity: PEntity; bounds: var Bounded) =
let pos = entity[Pos].addr
let bounds = bounds.rect.addr
if pos.x.cint < bounds.x:
pos.x = bounds.right.float
elif pos.x.cint > bounds.right:
pos.x = bounds.x.float
if pos.y.cint < bounds.y:
pos.y = bounds.bottom.float
elif pos.y.cint > bounds.bottom:
pos.y = bounds.y.float
proc toroidalBounds(x, y, w, h: int): Bounded =
result = newBounded(x,y,w,h)
result.setToroidal
proc setBouncy(bounds: var Bounded) =
bounds.checkEnt = proc(entity: PEntity; bounds: var Bounded) =
let pos = entity[Pos].addr
let vel = entity[Velocity].vec.addr
let bounds = bounds.rect.addr
template flipReset(field, toVal): stmt =
pos.field = toVal
vel.field = - vel.field
if pos.x.cint < bounds.x:
flipReset(x, bounds.x.float)
elif pos.x.cint > bounds.right:
flipReset(x, bounds.right.float)
if pos.y.cint < bounds.y:
flipReset(y, bounds.y.float)
elif pos.y.cint > bounds.bottom:
flipReset(y, bounds.bottom.float) # """
proc bouncyBounds(x, y, w, h: int): Bounded =
result = newBounded(x, y, w, h)
result.setBouncy
Bounded.setInitializer proc(x: PEntity) = x[Bounded].setToroidal()
msg_impl(Bounded, update) do(dt: float):
entity[Bounded].checkEnt entity, entity[Bounded]
import fowltek/idgen
var idg = newIDGen[int]()
assert idg.get == 0 #waste the first id, yes on purpose
var engy = newSdlEngine()
var em = newEntityManager()
var ents = newSeq[TEntity](0)
var connected_HIDs = newSeq[int]()
proc add_entity* (e: TEntity): int =
let id = idg.get
ents.ensureLen id+1
ents[id] = e
ents[id].userdata = cast[pointer](id) #teehee
return id
proc del_entity* (e_id: int): int =
destroy ents[e_id]
ents[e_id].userdata = nil
idg.release e_id
proc connectKeyboard(e: PEntity) =
assert e.hasComponent(HID_Controller)
e[HID_Controller].setupKeyboard
connected_HIDs.add cast[int](e.userData)
echo repr(connected_HIDS)
proc id* (E: PEntity): int{.inline.} = cast[int](E.userdata)
template eachEntity(body: stmt): stmt {.immediate,dirty.}=
for e_id{.inject.} in 1.. <ents.len:
template entity: expr = ents[e_id]
if ents[e_id].id != -1: body
block:
var e1 = em.newEntity(Pos, Acceleration, Velocity, Circle, Orientation,
InputState, HID_Controller, Bounded)
e1[Pos].x = 100
e1[Pos].y = 100
e1[Circle].radius = 6.0
var e1_id = add_entity(e1)
ents[e1_id].connectKeyboard
var running = true
var dt: float
while running:
while engy.pollHandle:
case engy.evt.kind
of QuitEvent: running = false
of MouseMotion:
let m = evMouseMOtion(engy.evt)
of KeyDown, KeyUP:
block key_handled:
for ID in 0 .. < connectedHIDs.len:
if ents[connectedHIDS[ID]].handleEvent(engy.evt):
#echo "event handled. breaking."
break key_handled
if evKeyboard(engy.evt).keysym.sym == K_Escape:
running = false
else: nil
dt = engy.frameDeltaFlt
eachEntity:
entity.update dt
engy.setDrawColor 0,0,0,255
engy.clear
#for e_id in 0 .. < ents.len: ents[e_id].draw engy
eachEntity:
entity.debugdraw engy
engy.MLstringRGBA 10, 10, ents[1].debugStr, 0,255,0,255
engy.present
engy.destroy
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment