Skip to content

Commit d97b914

Browse files
authored
Add quick drop for blocks with a preview of where the current block will fall (#7)
1 parent 3ab12ea commit d97b914

File tree

5 files changed

+113
-77
lines changed

5 files changed

+113
-77
lines changed

demo/14-quick-drop.gif

214 KB
Loading

readme.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,10 @@
8181

8282
![Image of increased fall speed](demo/13-fallspeed.gif)
8383

84-
- [ ] Handle quick fall
85-
- [ ] Show piece fall preview
84+
- [X] [Handle quick drop and show drop preview](https://github.com/scottnm/tetrust/commit/FILL)
85+
86+
![Image of quick drop and drop preview](demo/14-quick-drop.gif)
87+
8688
- [ ] Add start screen
8789
- [ ] Add high score logging
8890
- [ ] Add high score screen from start

src/game.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ impl GameState {
141141
self.game_phase = GamePhase::StartNextBlock
142142
}
143143
} else {
144-
self.active_block_pos.y += 1;
144+
self.move_active_block_down();
145145
}
146146
}
147147

@@ -151,7 +151,7 @@ impl GameState {
151151
}
152152
}
153153

154-
pub fn move_block_horizontal(&mut self, horizontal_motion: i32) {
154+
pub fn move_active_block_horizontal(&mut self, horizontal_motion: i32) {
155155
match self.game_phase {
156156
GamePhase::MoveBlock => {
157157
if self.can_active_block_move(horizontal_motion) {
@@ -162,6 +162,16 @@ impl GameState {
162162
}
163163
}
164164

165+
pub fn move_active_block_down(&mut self) {
166+
self.active_block_pos.y += 1;
167+
}
168+
169+
pub fn quick_drop(&mut self) {
170+
while !self.has_active_block_landed() {
171+
self.move_active_block_down();
172+
}
173+
}
174+
165175
pub fn rotate_block(&mut self, relative_rotation: i32) {
166176
// no rotation means no rotation. noop.
167177
if relative_rotation == 0 {

src/main.rs

+71-46
Original file line numberDiff line numberDiff line change
@@ -113,51 +113,56 @@ fn main() {
113113
let mut last_frame_time = time::Instant::now();
114114
let mut last_input_handled = time::Instant::now();
115115

116-
const BOARD_RECT: Rect = Rect {
117-
left: 1,
118-
top: 1,
119-
width: 10,
116+
// standard tetris board size
117+
const BOARD_WIDTH: i32 = 10;
118+
const BOARD_HEIGHT: i32 = 20;
119+
120+
let (window_height, window_width) = window.get_max_yx();
121+
let board_rect: Rect = Rect {
122+
left: (window_width / 2) - BOARD_WIDTH - 2, // arrange the board on the left side of the middle of the screen
123+
top: (window_height - BOARD_HEIGHT) / 2, // center the board within the window
124+
width: BOARD_WIDTH,
120125
height: 20,
121126
};
122127

123-
const BOARD_FRAME_RECT: Rect = Rect {
124-
left: BOARD_RECT.left - 1,
125-
top: BOARD_RECT.top - 1,
126-
width: BOARD_RECT.width + 2,
127-
height: BOARD_RECT.height + 2,
128+
let board_frame_rect: Rect = Rect {
129+
left: board_rect.left - 1,
130+
top: board_rect.top - 1,
131+
width: board_rect.width + 2,
132+
height: board_rect.height + 2,
128133
};
129134

130-
const TITLE_RECT: Rect = Rect {
131-
left: BOARD_FRAME_RECT.right() + 2,
132-
top: BOARD_FRAME_RECT.top,
135+
let title_rect: Rect = Rect {
136+
left: board_frame_rect.right() + 2,
137+
top: board_frame_rect.top,
133138
width: (TITLE.len() + 4) as i32,
134139
height: 3,
135140
};
136141

137-
const PREVIEW_FRAME_RECT: Rect = Rect {
138-
left: TITLE_RECT.left,
139-
top: TITLE_RECT.bottom() + 2,
142+
let preview_frame_rect: Rect = Rect {
143+
left: title_rect.left,
144+
top: title_rect.bottom() + 2,
140145
width: 6,
141146
height: 6,
142147
};
143148

144-
const PREVIEW_RECT: Rect = Rect {
145-
left: PREVIEW_FRAME_RECT.left + 1,
146-
top: PREVIEW_FRAME_RECT.top + 1,
147-
width: PREVIEW_FRAME_RECT.width - 2,
148-
height: PREVIEW_FRAME_RECT.height - 2,
149+
let preview_rect: Rect = Rect {
150+
left: preview_frame_rect.left + 1,
151+
top: preview_frame_rect.top + 1,
152+
width: preview_frame_rect.width - 2,
153+
height: preview_frame_rect.height - 2,
149154
};
150155

151-
const SCORE_FRAME_RECT: Rect = Rect {
152-
left: PREVIEW_FRAME_RECT.left,
153-
top: PREVIEW_FRAME_RECT.bottom() + 2,
156+
let score_frame_rect: Rect = Rect {
157+
left: preview_frame_rect.left,
158+
top: preview_frame_rect.bottom() + 2,
154159
width: 14,
155160
height: 4,
156161
};
157162

158163
let mut game_state = GameState::new(
159-
BOARD_RECT.width,
160-
BOARD_RECT.height,
164+
board_rect.width,
165+
board_rect.height,
161166
Box::new(ThreadRangeRng::new()),
162167
);
163168

@@ -166,13 +171,15 @@ fn main() {
166171
move_right: bool,
167172
rot_left: bool,
168173
rot_right: bool,
174+
drop: bool,
169175
}
170176

171177
let mut inputs = Inputs {
172178
move_left: false,
173179
move_right: false,
174180
rot_left: false,
175181
rot_right: false,
182+
drop: false,
176183
};
177184

178185
let mut game_over_blit_timer = Option::<time::Instant>::None;
@@ -189,6 +196,7 @@ fn main() {
189196
// check for movement inputs
190197
'a' => inputs.move_left = true,
191198
'd' => inputs.move_right = true,
199+
's' => inputs.drop = true,
192200
'j' => inputs.rot_left = true,
193201
'l' => inputs.rot_right = true,
194202

@@ -211,7 +219,7 @@ fn main() {
211219
if inputs.move_right {
212220
horizontal_motion += 1;
213221
}
214-
game_state.move_block_horizontal(horizontal_motion);
222+
game_state.move_active_block_horizontal(horizontal_motion);
215223

216224
let mut relative_rotation: i32 = 0;
217225
if inputs.rot_left {
@@ -222,11 +230,16 @@ fn main() {
222230
}
223231
game_state.rotate_block(relative_rotation);
224232

233+
if inputs.drop {
234+
game_state.quick_drop();
235+
}
236+
225237
inputs = Inputs {
226238
move_left: false,
227239
move_right: false,
228240
rot_left: false,
229241
rot_right: false,
242+
drop: false,
230243
};
231244
}
232245

@@ -239,53 +252,65 @@ fn main() {
239252
window.erase();
240253

241254
// Render the tetris title
242-
draw_frame(&window, &TITLE_RECT);
243-
draw_text_centered(&window, TITLE, TITLE_RECT.center_x(), TITLE_RECT.center_y());
255+
draw_frame(&window, &title_rect);
256+
draw_text_centered(&window, TITLE, title_rect.center_x(), title_rect.center_y());
244257

245258
// Render next piece preview
246259
draw_text_centered(
247260
&window,
248261
"Next",
249-
PREVIEW_FRAME_RECT.center_x(),
250-
PREVIEW_FRAME_RECT.top - 1,
262+
preview_frame_rect.center_x(),
263+
preview_frame_rect.top - 1,
251264
);
252-
draw_frame(&window, &PREVIEW_FRAME_RECT);
265+
draw_frame(&window, &preview_frame_rect);
253266
render_block(
254267
&window,
255268
Vec2::zero(),
256-
PREVIEW_RECT.left,
257-
PREVIEW_RECT.top,
269+
preview_rect.left,
270+
preview_rect.top,
258271
game_state.preview_block(),
259272
);
260273

261274
// Render the score pane
262275
draw_text_centered(
263276
&window,
264277
&format!("Level: {:05}", game_state.level()),
265-
SCORE_FRAME_RECT.center_x(),
266-
SCORE_FRAME_RECT.center_y() - 1,
278+
score_frame_rect.center_x(),
279+
score_frame_rect.center_y() - 1,
267280
);
268281
draw_text_centered(
269282
&window,
270283
&format!("Score: {:05}", game_state.score()),
271-
SCORE_FRAME_RECT.center_x(),
272-
SCORE_FRAME_RECT.center_y(),
284+
score_frame_rect.center_x(),
285+
score_frame_rect.center_y(),
273286
);
274-
draw_frame(&window, &SCORE_FRAME_RECT);
287+
draw_frame(&window, &score_frame_rect);
288+
289+
// Render the board frame
290+
draw_frame(&window, &board_frame_rect);
275291

276292
// Render the active piece
277-
draw_frame(&window, &BOARD_FRAME_RECT);
278293
if let Some((block, block_pos)) = game_state.active_block() {
279-
render_block(&window, block_pos, BOARD_RECT.left, BOARD_RECT.top, block);
294+
// TOOD: mayhaps refactor this into its own helper?
295+
// render the active piece's drop trail
296+
for cell in &block.cells() {
297+
let start_row = cell.y + block_pos.y;
298+
let col = cell.x + block_pos.x;
299+
for row in start_row..board_rect.height {
300+
window.mvaddch(row + board_rect.top, col + board_rect.left, '-');
301+
}
302+
}
303+
304+
render_block(&window, block_pos, board_rect.left, board_rect.top, block);
280305
}
281306

282307
// Render the settled pieces
283308
game_state.for_each_settled_piece(|block_type: BlockType, cell_pos: Vec2| {
284309
render_cell(
285310
&window,
286311
cell_pos,
287-
BOARD_RECT.left,
288-
BOARD_RECT.top,
312+
board_rect.left,
313+
board_rect.top,
289314
block_type,
290315
);
291316
});
@@ -306,8 +331,8 @@ fn main() {
306331
draw_text_centered(
307332
&window,
308333
"Game Over",
309-
BOARD_RECT.center_x(),
310-
BOARD_RECT.center_y(),
334+
board_rect.center_x(),
335+
board_rect.center_y(),
311336
);
312337
window.attroff(pancurses::A_BLINK);
313338
}
@@ -317,8 +342,8 @@ fn main() {
317342
draw_text_centered(
318343
&window,
319344
"PAUSE",
320-
BOARD_RECT.center_x(),
321-
BOARD_RECT.center_y(),
345+
board_rect.center_x(),
346+
board_rect.center_y(),
322347
);
323348
window.attroff(pancurses::A_BLINK);
324349
}

0 commit comments

Comments
 (0)