aboutsummaryrefslogtreecommitdiffstats
path: root/pong
diff options
context:
space:
mode:
Diffstat (limited to 'pong')
-rw-r--r--pong/Cargo.toml16
-rw-r--r--pong/README37
-rw-r--r--pong/pong.tdy199
-rw-r--r--pong/src/main.rs90
4 files changed, 342 insertions, 0 deletions
diff --git a/pong/Cargo.toml b/pong/Cargo.toml
new file mode 100644
index 0000000..acf7d67
--- /dev/null
+++ b/pong/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "pong"
+version = "0.1.0"
+authors = ["Gustav Sörnäs <gustav@sornas.net>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[profile.release]
+lto = "thin"
+
+[dependencies]
+tihdy = { path = ".."}
+tihdy_derive = { path = "../tihdy_derive" }
+
+macroquad = "0.2"
diff --git a/pong/README b/pong/README
new file mode 100644
index 0000000..be718ec
--- /dev/null
+++ b/pong/README
@@ -0,0 +1,37 @@
+PONG
+====
+
+Made for Global Game Jam 2021 at LiU Game Jam's site.
+
+The language wasn't really ready for a normal game so we wanted to make Pong
+(the easiest game we could think of) while fixing what we needed to make it
+work.
+
+Building
+--------
+
+Clone the entire tihdy-repo, cd into the pong-directory and then cargo run.
+
+Modifying
+---------
+
+All game logic is located in pong.tdy. Most of the configuration should be
+straight forward. If you want to modify the controls, you need to change both
+the string passed from pong.tdy as well as the respective function in src/main.rs
+(key_down).
+
+If you want to add more rust functions (for example, to draw text to the screen)
+you need to add it like the already existing functions. All functions need a
+return type and a return value, even if it is a Type::Void and Value::Nil. (See
+e.g. clear for an example.) For a draw_text you probably want to take a
+Value::String(s), Value::Float(x), Value::Float(y) and so on. Inside the block
+you then write your normal Rusty code.
+
+After writing your function you also need to link it so you can use it from the
+game file. Add it to 'functions' like the other linked functions inside main()
+in src/main.rs.
+
+After linking, the only thing left to do is call it from the game logic. You'll
+get a type error if you pass the wrong parameters. Additional parameter
+combinations can be set (much like normal function overloading) should you have
+the need by adding more "arms" to the extern_function!().
diff --git a/pong/pong.tdy b/pong/pong.tdy
new file mode 100644
index 0000000..15998f3
--- /dev/null
+++ b/pong/pong.tdy
@@ -0,0 +1,199 @@
+PADDLE_W := 0.5
+PADDLE_H := 3.0
+
+blob Paddle {
+ x: float
+ y: float
+}
+
+BALL_S := 0.4
+BALL_STARTV := 5.0
+
+blob Ball {
+ x: float
+ y: float
+
+ vx: float
+ vy: float
+}
+
+blob Player {
+ paddle: Paddle
+ score: float
+}
+
+blob State {
+ p1: Player
+ p2: Player
+ ball: Ball
+}
+
+ball_speed := 2.0
+SPEED := 10.0
+
+abs := fn a: float -> float {
+ if a < 0.0 {
+ ret -a
+ }
+ ret a
+}
+
+and := fn a: bool, b: bool -> bool {
+ if a {
+ ret b
+ }
+ ret false
+}
+
+or := fn a: bool, b: bool -> bool {
+ if a {
+ ret true
+ }
+ ret b
+}
+
+sign := fn a: float -> float {
+ if a < 0.0 {
+ ret -1.0
+ }
+ ret 1.0
+}
+
+rect_overlap := fn ax: float, ay: float, aw: float, ah: float, bx: float, by: float, bw: float, bh: float -> bool {
+ acx := ax + aw / 2.
+ acy := ay + ah / 2.
+
+ bcx := bx + bw / 2.
+ bcy := by + bh / 2.
+
+ dx := abs(acx - bcx) + (-aw - bw) / 2.
+ dy := abs(acy - bcy) + (-ah - bh) / 2.
+ ret and(dx < 0., dy < 0.)
+}
+
+ball_and_paddle_check := fn pad: Paddle, ball: Ball {
+
+ if rect_overlap(ball.x, ball.y, BALL_S, BALL_S, pad.x, pad.y, PADDLE_W, PADDLE_H) {
+ SPEEDUP := 0.5
+ ballcy := ball.y + BALL_S / 2.
+ padcy := pad.y + PADDLE_H / 2.
+ if ball.x < pad.x {
+ ball.vx = -abs(ball.vx) - SPEEDUP
+ ball.vy = ball.vy + (ballcy - padcy) * 3.
+ } else {
+ ball.vx = abs(ball.vx) + SPEEDUP
+ ball.vy = ball.vy + (ballcy - padcy) * 3.
+ }
+ }
+}
+
+update := fn state: State {
+ delta := get_delta()
+ speed := delta * SPEED
+
+ if key_down("w") {
+ state.p1.paddle.y = state.p1.paddle.y - speed
+ }
+ if key_down("s") {
+ state.p1.paddle.y = state.p1.paddle.y + speed
+ }
+
+ if state.p1.paddle.y < 0.0 {
+ state.p1.paddle.y = 0.0
+ }
+
+ if state.p1.paddle.y > 20.0 - PADDLE_H {
+ state.p1.paddle.y = 20.0 - PADDLE_H
+ }
+
+ if key_down("i") {
+ state.p2.paddle.y = state.p2.paddle.y - speed
+ }
+ if key_down("k") {
+ state.p2.paddle.y = state.p2.paddle.y + speed
+ }
+
+ if state.p2.paddle.y < 0.0 {
+ state.p2.paddle.y = 0.0
+ }
+
+ if state.p2.paddle.y > 20.0 - PADDLE_H {
+ state.p2.paddle.y = 20.0 - PADDLE_H
+ }
+
+ state.ball.x = state.ball.x + delta * state.ball.vx
+ state.ball.y = state.ball.y + delta * state.ball.vy
+
+ if state.ball.y < 0. {
+ state.ball.vy = abs(state.ball.vy)
+ }
+
+ if state.ball.y > 20. - 0.2 {
+ state.ball.vy = -abs(state.ball.vy)
+ }
+
+ if state.ball.x < 0.0 {
+ state.ball.x = 10.0
+ state.ball.y = 10.0
+ state.ball.vx = -BALL_STARTV
+ state.ball.vy = 0.
+ state.p1.score = state.p1.score + 1.0
+ }
+
+ if state.ball.x > 20.0 {
+ state.ball.x = 10.0
+ state.ball.y = 10.0
+ state.ball.vx = BALL_STARTV
+ state.ball.vy = 0.
+ state.p2.score = state.p2.score + 1.0
+ }
+
+ ball_and_paddle_check(state.p1.paddle, state.ball)
+ ball_and_paddle_check(state.p2.paddle, state.ball)
+}
+
+draw := fn state: State {
+ clear()
+
+ draw_rectangle(state.p1.paddle.x, state.p1.paddle.y, PADDLE_W, PADDLE_H)
+ draw_rectangle(state.p2.paddle.x, state.p2.paddle.y, PADDLE_W, PADDLE_H)
+
+ for y := 0.0, y < state.p1.score, y = y + 1.0 {
+ draw_rectangle(15.0, y * 0.5, 0.5, 0.5)
+ }
+
+ for y := 0.0, y < state.p2.score, y = y + 1.0 {
+ draw_rectangle(5.0, y * 0.5, 0.5, 0.5)
+ }
+
+ draw_rectangle(state.ball.x, state.ball.y, BALL_S, BALL_S)
+}
+
+init := fn {
+ state := State()
+ state.ball = Ball()
+ state.ball.x = 10.0
+ state.ball.y = 10.0
+ state.ball.vx = BALL_STARTV
+ state.ball.vy = 0.0
+
+ state.p1 = Player()
+ state.p1.score = 0.0
+ state.p1.paddle = Paddle()
+ state.p1.paddle.x = 1.
+ state.p1.paddle.y = 10.
+
+ state.p2 = Player()
+ state.p2.score = 0.0
+ state.p2.paddle = Paddle()
+ state.p2.paddle.x = 19.
+ state.p2.paddle.y = 10.
+
+ for i := 0, i == i, i = i + 1 {
+ update(state)
+ draw(state)
+ yield
+ }
+}
+
+init()
diff --git a/pong/src/main.rs b/pong/src/main.rs
new file mode 100644
index 0000000..2ab4b4a
--- /dev/null
+++ b/pong/src/main.rs
@@ -0,0 +1,90 @@
+use macroquad::{Camera2D, DARKPURPLE, KeyCode, SKYBLUE, clear_background, draw_rectangle, get_frame_time, is_key_down, next_frame, set_camera, vec2};
+use std::path::Path;
+use tihdy::{Type, Value};
+use tihdy::vm::OpResult;
+use tihdy_derive::extern_function;
+
+const SCREEN_WIDTH: f32 = 20.0;
+const SCREEN_HEIGHT: f32 = 20.0;
+
+extern_function!(log
+ [Value::Float(f1), Value::Float(f2)] -> Type::Void => {
+ println!("({}, {})", f1, f2);
+ Ok(Value::Nil)
+ },
+);
+
+extern_function!(get_delta
+ [] -> Type::Float => {
+ Ok(Value::Float(get_frame_time() as f64))
+ },
+);
+
+extern_function!(key_down
+ [Value::String(s)] -> Type::Bool => {
+ let s: &str = s;
+ Ok(Value::Bool(match s {
+ "w" => is_key_down(KeyCode::W),
+ "s" => is_key_down(KeyCode::S),
+ "i" => is_key_down(KeyCode::I),
+ "k" => is_key_down(KeyCode::K),
+ _ => false,
+ }))
+ },
+);
+
+extern_function!(my_draw_rectangle
+ [Value::Float(x), Value::Float(y), Value::Float(w), Value::Float(h)] -> Type::Void => {
+ draw_rectangle(*x as f32, *y as f32, *w as f32, *h as f32, DARKPURPLE);
+ Ok(Value::Nil)
+ },
+);
+
+extern_function!(clear
+ [] -> Type::Void => {
+ clear_background(SKYBLUE);
+ Ok(Value::Nil)
+ },
+);
+
+#[macroquad::main("Pong")]
+async fn main() {
+
+ let functions: Vec<(String, tihdy::RustFunction)> = vec![
+ ("log".to_string(), log),
+ ("get_delta".to_string(), get_delta),
+ ("key_down".to_string(), key_down),
+ ("draw_rectangle".to_string(), my_draw_rectangle),
+ ("clear".to_string(), clear),
+ ];
+
+ let vm = tihdy::compile_file(Path::new("pong.tdy"), false, functions);
+ if let Err(errs) = vm {
+ for err in errs {
+ println!("{}", err);
+ }
+ return;
+ }
+
+ set_camera(Camera2D {
+ zoom: vec2(1. / SCREEN_WIDTH * 2., -1. / SCREEN_HEIGHT * 2.),
+ target: vec2(SCREEN_WIDTH / 2., SCREEN_HEIGHT / 2.),
+ ..Default::default()
+ });
+
+ let mut vm = vm.unwrap();
+ loop {
+ match vm.run() {
+ Err(e) => {
+ println!("{:?}", e);
+ break;
+ }
+ Ok(OpResult::Yield) => {
+ next_frame().await
+ }
+ _ => {
+ break;
+ }
+ }
+ }
+}