-
Notifications
You must be signed in to change notification settings - Fork 1
/
player.ml
138 lines (124 loc) · 3.5 KB
/
player.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
open Graphics
open Util
open Math2d
open Collide
open Mlgrope
open Backend
open Frontend
exception TouchedGoalException of game_state * vec
exception OutOfBoundsException of game_state
type game = {
size : vec;
time : float;
paused : bool;
score : int;
state : game_state;
}
let star_score = 100
(* Adds zeros before a number n to reach padding digits *)
let pad_int padding n =
let s = string_of_int n in
let len = String.length s in
let start_at = padding - len in
if start_at < 0 then s else
String.init padding (fun i ->
if i >= start_at then String.get s (i - start_at) else '0'
)
let draw_score size score =
let (x, y) = ints_of_vec size in
let s = pad_int 5 score in
Graphics.set_color Graphics.black;
let (w, h) = Graphics.text_size s in
Graphics.moveto (x - w) (y - h);
Graphics.draw_string s
let is_bubble_at pos ball e =
match e with
| Bubble(bubble) -> Collide.circle_point ball.position bubble.radius pos
| _ -> false
let is_rope_at lastpos pos ball e =
match e with
| Rope{position} | Elastic{position} ->
is_some (Collide.segments position ball.position lastpos pos)
| _ -> false
let is_in_bounds size b =
let (has_bubble, has_rope) = List.fold_left (fun (has_bubble, has_rope) e ->
match e with
| Bubble(_) -> (true, has_rope)
| Rope(_) | Elastic(_) -> (has_bubble, true)
| _ -> (has_bubble, has_rope)
) (false, false) b.links in
if has_bubble && b.position.y <= 0. then true else
if has_rope then true else
Collide.box_point vec0 size b.position
let compute_score gs =
List.fold_left (fun (score, gs) e ->
match e with
| Ball(b) ->
let (score, links) = List.fold_left (fun (score, links) e ->
match e with
| Star(_) -> (score + star_score, links)
| _ -> (score, e::links)
) (score, []) b.links
in
(score, Ball{b with links}::gs)
| _ -> (score, e::gs)
) (0, []) gs
let step g =
let t = get_time () in
let dt = t -. g.time in
let state = if g.paused then g.state else (
try
Backend.move g.state dt
with Backend.TouchedGoalException(pos) -> raise (TouchedGoalException (g.state, pos))
) in
let (state, in_bounds) = List.fold_left (fun (l, n) e ->
match e with
| Ball(b) ->
if is_in_bounds g.size b then
(e::l, n+1)
else
(l, n)
| _ -> (e::l, n)
) ([], 0) state in
if in_bounds = 0 then raise (OutOfBoundsException state) else
let (score, state) = compute_score state in
let g = {g with time = t; score = g.score + score; state} in
Frontend.draw g.state;
draw_score g.size g.score;
g
let handle_click ball lastpos pos =
let is_bubble = is_bubble_at pos ball in
let is_rope = is_rope_at lastpos pos ball in
{ball with links = List.filter (fun e ->
not (is_bubble e) && not (is_rope e)
) ball.links}
let handle_event g s s' =
match s' with
| {button = true; mouse_x; mouse_y} ->
let pos = Frontend.mouse_of_status s' in
let lastpos = match s with
| {button = true; mouse_x; mouse_y} -> {x = float_of_int mouse_x; y = float_of_int mouse_y}
| _ -> pos
in
let state = List.map (fun e ->
match e with
| Ball(b) -> Ball(handle_click b lastpos pos)
| _ -> e
) g.state in
{g with state}
| {keypressed = true; key = '\027'} -> raise Exit
| {keypressed = true; key = ' '} -> {g with paused = not g.paused}
| {keypressed = true; key = 'f'} ->
let g = step {g with paused = false} in
{g with paused = true}
| _ -> g
let run size state =
let g = {
size;
time = get_time ();
paused = false;
score = 0;
state;
} in
Printf.printf "Press Esc to quit, space to pause\n%!";
Frontend.run step handle_event size g