Created
February 13, 2011 18:31
-
-
Save manveru/824927 to your computer and use it in GitHub Desktop.
Pong in Nimrod
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import math | |
import vector | |
import sdl | |
type | |
OBall = object | |
vector: TVector2 | |
velocity: TVector2 | |
radius: float | |
speed: float | |
color: uint32 | |
OPaddle = object | |
vector: TVector2 | |
target: TVector2 | |
height, width, speed: float | |
color: uint32 | |
OWorld = object | |
screen: sdl.PSurface | |
width: int16 | |
height: int16 | |
pause: bool | |
running: bool | |
ball: ref OBall | |
paddle: ref OPaddle | |
enemy: ref OPaddle | |
highlight: TVector2 | |
score: tuple[paddle, enemy: int] | |
proc newPaddle(x, y, w, h: float): ref OPaddle = | |
new(result) | |
result.vector = (x, y) | |
result.target = (x, y) | |
result.height = h | |
result.width = w | |
result.color = 0x6666ff | |
result.speed = 8.0 | |
proc rect(paddle: ref OPaddle): sdl.TRect = | |
result.x = int16(paddle.vector.x - (paddle.width / 2.0)) | |
result.y = int16(paddle.vector.y - (paddle.height / 2.0)) | |
result.w = uint16(paddle.width) | |
result.h = uint16(paddle.height) | |
proc draw(paddle: ref OPaddle, world: ref OWorld) = | |
var rect = paddle.rect | |
discard world.screen.fillRect(addr(rect), paddle.color) | |
proc updatePaddle(paddle: ref OPaddle, world: ref OWorld) = | |
var goal = paddle.target - paddle.vector | |
if goal.length > paddle.speed: | |
goal = goal.normalize * paddle.speed | |
var future = paddle.vector + goal | |
if future.y < (paddle.height / 2.0): | |
return | |
if (future.y + (paddle.height / 2.0)) > float(world.height): | |
return | |
paddle.vector = future | |
proc updateEnemy(paddle: ref OPaddle, world: ref OWorld) = | |
if world.ball.velocity.x > 0.0: | |
var targetX = paddle.vector.x | |
var targetY = world.ball.vector.y | |
var goal = (targetX, targetY) - paddle.vector | |
if goal.length > paddle.speed: | |
goal = goal.normalize * paddle.speed | |
paddle.target = goal | |
var future = paddle.vector + paddle.target | |
var halfHeight = float(paddle.height) / 2.0 | |
if future.y < halfHeight or (future.y + halfHeight) > float(world.height): | |
return | |
paddle.vector = future | |
type THit = tuple[hit: bool, place: TVector2] | |
proc hitCore(paddle: ref OPaddle, x0, y0, x1, y1: float, past, future: TVector2): THit = | |
# line between past and future | |
var x2 = past.x | |
var y2 = past.y | |
var x3 = future.x | |
var y3 = future.y | |
var d = (x1-x0)*(y3-y2) - (y1-y0)*(x3-x2) | |
if abs(d) < 0.001: | |
result.hit = false | |
return | |
# never hit since parallel | |
var ab = ((y0-y2)*(x3-x2) - (x0-x2)*(y3-y2)) / d | |
if ab > 0.0 and ab < 1.0: | |
var cd = ((y0-y2)*(x1-x0) - (x0-x2)*(y1-y0)) / d | |
if cd > 0.0 and cd < 1.0: | |
var linx = x0 + ab*(x1-x0) | |
var liny = y0 + ab*(y1-y0) | |
result.hit = true | |
result.place = (linx, liny) | |
return | |
result.hit = false | |
proc hitEnemy(paddle: ref OPaddle, past, future: TVector2): THit = | |
var vector = paddle.vector | |
var halfWidth = paddle.width / 2.0 | |
var halfHeight = paddle.height / 2.0 | |
var x0 = vector.x - halfWidth | |
var x1 = vector.x + halfWidth | |
var y0 = vector.y - halfHeight | |
var y1 = vector.y + halfHeight | |
return paddle.hitCore(x0, y0, x1, y1, past, future) | |
proc hitPaddle(paddle: ref OPaddle, past, future: TVector2): THit = | |
var vector = paddle.vector | |
var halfWidth = paddle.width / 2.0 | |
var halfHeight = paddle.height / 2.0 | |
var x0 = vector.x - halfWidth | |
var x1 = vector.x + halfWidth | |
var y0 = vector.y - halfHeight | |
var y1 = vector.y + halfHeight | |
return paddle.hitCore(x0, y0, x1, y1, past, future) | |
proc newBall(x, y: float): ref OBall = | |
new(result) | |
result.vector = (x, y) | |
result.speed = 8.0 | |
result.radius = 2.0 | |
result.color = 0xffffff | |
result.velocity = (2.0, 2.0) | |
proc rect(ball: ref OBall): sdl.TRect = | |
var size = ball.radius * 2.0 | |
result.x = int16(ball.vector.x - ball.radius) | |
result.y = int16(ball.vector.y - ball.radius) | |
result.w = uint16(size) | |
result.h = uint16(size) | |
proc draw(ball: ref OBall, world: ref OWorld) = | |
var rect = ball.rect | |
discard world.screen.fillRect(addr(rect), ball.color) | |
proc update(ball: ref OBall, world: ref OWorld) = | |
var velocity = ball.velocity | |
var future = ball.vector + velocity | |
var radius = ball.radius | |
if velocity.y < 0.0 and future.y < radius: | |
velocity.y = -velocity.y | |
if velocity.y > 0.0 and future.y >= float(world.height) - radius: | |
velocity.y = -velocity.y | |
if velocity.x < 0.0: | |
var paddle = world.paddle | |
var collision = paddle.hitPaddle(ball.vector, future) | |
if collision.hit: | |
velocity.x = -velocity.x | |
ball.speed = ball.speed + 0.1 | |
echo(ball.speed) | |
velocity = velocity.normalize * ball.speed | |
world.highlight = collision.place | |
elif future.x <= radius: | |
echo("Enemy scores") | |
ball.speed = ball.speed + 0.1 | |
ball.speed.echo() | |
inc(world.score.enemy) | |
velocity.x = -velocity.x | |
if velocity.x > 0.0: | |
var enemy = world.enemy | |
var collision = enemy.hitEnemy(ball.vector, future) | |
if collision.hit: | |
velocity.x = -velocity.x | |
ball.speed = ball.speed + 0.1 | |
echo(ball.speed) | |
velocity = velocity.normalize * ball.speed | |
world.highlight = collision.place | |
elif future.x >= float(world.width): | |
echo("Player scores") | |
ball.speed = ball.speed + 0.1 | |
ball.speed.echo() | |
inc(world.score.paddle) | |
velocity.x = -velocity.x | |
ball.velocity = velocity | |
ball.vector = ball.vector + velocity | |
proc drawScore(world: ref OWorld) = | |
var rect: sdl.TRect | |
rect.x = int16(world.paddle.width + world.paddle.vector.x) | |
rect.y = int16(3) | |
rect.w = int16(3) | |
rect.h = int16(3) | |
var score = world.score | |
for p in countdown(score.paddle, 1): | |
rect.x = int16(rect.x + 6) | |
discard world.screen.fillRect(addr(rect), 0x6666ff) | |
if rect.x > world.width: | |
echo("You Win!") | |
world.running = false | |
rect.x = int16(world.enemy.vector.x - world.enemy.width) | |
rect.y = int16(world.height - 6) | |
for p in countdown(score.enemy, 1): | |
rect.x = int16(rect.x - 6) | |
discard world.screen.fillRect(addr(rect), 0xff6666) | |
if rect.x <= 0: | |
echo("You Lose!") | |
world.running = false | |
proc draw(world: ref OWorld) = | |
discard world.screen.FillRect(nil, 0x0) | |
var center: sdl.TRect | |
center.x = int16(world.width / 2) - int16(1) | |
center.y = int16(0) | |
center.h = world.height | |
center.w = uint16(2) | |
discard world.screen.fillRect(addr(center), 0x333333) | |
var high: sdl.TRect | |
high.x = int16(world.highlight.x - 5.0) | |
high.y = int16(world.highlight.y - 5.0) | |
high.h = uint16(10) | |
high.w = uint16(10) | |
discard world.screen.fillRect(addr(high), 0xff0000) | |
world.ball.draw(world) | |
world.paddle.draw(world) | |
world.enemy.draw(world) | |
world.drawScore() | |
discard world.screen.flip | |
proc update(world: ref OWorld) = | |
world.ball.update(world) | |
world.paddle.updatePaddle(world) | |
world.enemy.updateEnemy(world) | |
proc fail(msg: string) = | |
echo(msg) | |
quit(1) | |
if sdl.Init(sdl.INIT_EVERYTHING) != 0: | |
fail "couldn't initialize sdl" | |
if sdl.enableUnicode(1) != 0: | |
fail "couldn't initialize unicode" | |
if sdl.enableKeyRepeat(25, 25) != 0: | |
fail "couldn't set key repetition" | |
sdl.wmSetCaption("Pong", "") | |
var world: ref OWorld | |
new(world) | |
world.width = int16(640) | |
world.height = int16(480) | |
world.screen = sdl.SetVideoMode(world.width, world.height, 32, 0) | |
world.ball = newBall(100.0, 100.0) | |
world.paddle = newPaddle(10.0, float(world.height) / 2.0, 10.0, 30.0) | |
world.enemy = newPaddle(float(world.width) - 10.0, float(world.height) / 2.0, 10.0, 30.0) | |
world.pause = false | |
world.running = true | |
world.score = (0, 0) | |
while world.running == true: | |
var event: sdl.TEvent | |
var eventp = addr(event) | |
if eventp.pollEvent() == 1: | |
case event.kind: | |
of sdl.KeyDown, sdl.KeyUp: | |
var key = $EvKeyboard(eventp).keysym.sym.getkeyname() | |
case key | |
of "p": | |
world.pause = not world.pause | |
of "j": | |
world.paddle.target = world.paddle.vector + (0.0, world.paddle.speed) | |
of "k": | |
world.paddle.target = world.paddle.vector - (0.0, world.paddle.speed) | |
of "escape", "q": | |
world.running = false | |
else: | |
echo(key) | |
else: | |
nil | |
sdl.delay(25) | |
world.update() | |
world.draw() | |
sdl.quit() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment