forked from amethyst/bracket-lib
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ex17-wasm-external.rs
214 lines (191 loc) · 7.37 KB
/
ex17-wasm-external.rs
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
// This the first roguelike-ish example - a walking @. We build a very simple map,
// and you can use the cursor keys to move around a world.
//
// Comments that duplicate previous examples have been removed for brevity.
//////////////////////////////////////////////////////////////
rltk::add_wasm_support!();
use rltk::{Console, GameState, Rltk, VirtualKeyCode, RGB};
// We'll allow map tiles to be either a wall or a floor. We're deriving PartialEq so we don't
// have to match on it every time. We'll make it a copy type because it's really just an int.
#[derive(PartialEq, Copy, Clone)]
#[allow(dead_code)]
enum TileType {
Wall,
Floor,
}
// We're extending State to include a minimal map and player coordinates.
struct State {
map: Vec<TileType>,
player_position: usize,
}
// We're storing all the tiles in one big array, so we need a way to map an X,Y coordinate to
// a tile. Each row is stored sequentially (so 0..80, 81..160, etc.). This takes an x/y and returns
// the array index.
pub fn xy_idx(x: i32, y: i32) -> usize {
(y as usize * 80) + x as usize
}
// It's a great idea to have a reverse mapping for these coordinates. This is as simple as
// index % 80 (mod 80), and index / 80
pub fn idx_xy(idx: usize) -> (i32, i32) {
(idx as i32 % 80, idx as i32 / 80)
}
// Since we have some content, we should also include a map builder. A 'new'
// function is a common Rust way to do this.
impl State {
#[allow(dead_code)]
pub fn new() -> State {
let mut state = State {
map: vec![TileType::Floor; 80 * 50],
player_position: xy_idx(40, 25),
};
// Make the boundaries walls
for x in 0..80 {
state.map[xy_idx(x, 0)] = TileType::Wall;
state.map[xy_idx(x, 49)] = TileType::Wall;
}
for y in 0..50 {
state.map[xy_idx(0, y)] = TileType::Wall;
state.map[xy_idx(79, y)] = TileType::Wall;
}
// Now we'll randomly splat a bunch of walls. It won't be pretty, but it's a decent illustration.
// First, obtain the thread-local RNG:
let mut rng = rltk::RandomNumberGenerator::new();
for _ in 0..400 {
// rand provides a gen_range function to get numbers in a range.
let x = rng.roll_dice(1, 80) - 1;
let y = rng.roll_dice(1, 50) - 1;
let idx = xy_idx(x, y);
// We don't want to add a wall on top of the player
if state.player_position != idx {
state.map[idx] = TileType::Wall;
}
}
// We'll return the state with the short-hand
state
}
// Handle player movement. Delta X and Y are the relative move
// requested by the player. We calculate the new coordinates,
// and if it is a floor - move the player there.
pub fn move_player(&mut self, delta_x: i32, delta_y: i32) {
let current_position = idx_xy(self.player_position);
let new_position = (current_position.0 + delta_x, current_position.1 + delta_y);
let new_idx = xy_idx(new_position.0, new_position.1);
if self.map[new_idx] == TileType::Floor {
self.player_position = new_idx;
}
}
}
// Implement the game loop
impl GameState for State {
fn tick(&mut self, ctx: &mut Rltk) {
// Handle keyboard inputs.
match ctx.key {
None => {} // Nothing happened
Some(key) => {
// A key is pressed or held
match key {
// We're matching a key code from GLFW (the GL library underlying RLTK),
// and applying movement via the move_player function.
// Numpad
VirtualKeyCode::Numpad8 => self.move_player(0, -1),
VirtualKeyCode::Numpad4 => self.move_player(-1, 0),
VirtualKeyCode::Numpad6 => self.move_player(1, 0),
VirtualKeyCode::Numpad2 => self.move_player(0, 1),
// Numpad diagonals
VirtualKeyCode::Numpad7 => self.move_player(-1, -1),
VirtualKeyCode::Numpad9 => self.move_player(1, -1),
VirtualKeyCode::Numpad1 => self.move_player(-1, 1),
VirtualKeyCode::Numpad3 => self.move_player(1, 1),
// Cursors
VirtualKeyCode::Up => self.move_player(0, -1),
VirtualKeyCode::Down => self.move_player(0, 1),
VirtualKeyCode::Left => self.move_player(-1, 0),
VirtualKeyCode::Right => self.move_player(1, 0),
_ => {} // Ignore all the other possibilities
}
}
}
// New: Handle web buttons
if let Some(btn) = &ctx.web_button {
match btn.trim() {
"go_nw" => self.move_player(-1, -1),
"go_n" => self.move_player(0, -1),
"go_ne" => self.move_player(1, -1),
"go_w" => self.move_player(-1, 0),
"go_e" => self.move_player(1, 0),
"go_sw" => self.move_player(-1, 1),
"go_s" => self.move_player(0, 1),
"go_se" => self.move_player(1, 1),
_ => {}
}
}
// Clear the screen
ctx.cls();
// Iterate the map array, incrementing coordinates as we go.
let mut y = 0;
let mut x = 0;
for tile in &self.map {
// Render a tile depending upon the tile type
match tile {
TileType::Floor => {
ctx.print_color(
x,
y,
RGB::from_f32(0.5, 0.5, 0.5),
RGB::from_f32(0., 0., 0.),
".",
);
}
TileType::Wall => {
ctx.print_color(
x,
y,
RGB::from_f32(0.0, 1.0, 0.0),
RGB::from_f32(0., 0., 0.),
"#",
);
}
}
// Move the coordinates
x += 1;
if x > 79 {
x = 0;
y += 1;
}
}
// Render the player @ symbol
let ppos = idx_xy(self.player_position);
ctx.print_color(
ppos.0,
ppos.1,
RGB::from_f32(1.0, 1.0, 0.0),
RGB::from_f32(0., 0., 0.),
"@",
);
}
}
#[cfg(target_arch = "wasm32")]
fn main() {
let context = Rltk::init_simple8x8(
80,
50,
"RLTK Example 17 - WASM External Events",
"resources",
);
let gs = State::new();
rltk::register_html_button("go_nw");
rltk::register_html_button("go_n");
rltk::register_html_button("go_ne");
rltk::register_html_button("go_w");
rltk::register_html_button("go_e");
rltk::register_html_button("go_sw");
rltk::register_html_button("go_s");
rltk::register_html_button("go_se");
rltk::main_loop(context, gs);
}
#[cfg(not(target_arch = "wasm32"))]
fn main() {
println!("Sorry, this example is intended for Web Assembly (wasm32-unknown-unknown) only.");
println!("It would actually run in a window, but the purpose is to demonstrate binding to external HTML elements,");
println!("so there isn't really a lot of point!")
}