-
Notifications
You must be signed in to change notification settings - Fork 0
/
mipslabwork.c
745 lines (701 loc) · 30 KB
/
mipslabwork.c
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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
/* mipslabwork.c
This file written 2015 by F Lundevall
Updated 2017-04-21 by F Lundevall
This file should be changed by YOU! So you must
add comment(s) here with your name(s) and date(s):
This file modified 20180303 by Erik Martines Sanches
For copyright and licensing, see file COPYING */
#include <stdint.h> /* Declarations of uint_32 and the like */
#include <pic32mx.h> /* Declarations of system-specific addresses etc */
#include "mipslab.h" /* Declatations for these labs */
//Code by Erik Martines Sanches follows unless otherwise noted.
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
uint8_t frame[512]; //The current frame. Each byte represents an 8 pixel tall column.
bool prepare_frame_ok = false; //Don't prepare a frame until this is nonzero (set by the ISR).
unsigned int delay_for_random_seed = 0; //Time before a user presses a button on the start screen.
unsigned int timeoutcount = 0; //Helps establish the above variable.
int hidden_items = 0; //Items that are collected are hidden.
status game_status = CONTI;
int time_units_left = 8; //How many time units are left of the game.
enum direction {LEFT, RIGHT, UP, DOWN, NONE};
enum direction currentDirection = NONE; //Direction of player. Should perhaps be inside a struct.
enum direction oldDirection = NONE; //For keeping track of when to change direction.
typedef struct Position2D{
float x;
float y;
} position2D;
typedef struct Node{ //Represents an item.
bool movable; //Whether we should be able to move it.
bool drawable; //Whether to draw this item or not.
int items_collected; //How many balls it has collected.
int size; //Three sizes. [0-2]
float offset_per_time_unit; //Three speed increases possible
position2D vertices[4]; //Geometry of this node (currently 4 vertices connected by a edges).
} node;
node items[NUMBER_OF_ITEMS]; //Holds all the items, including the player.
/*Resets Timer 2 interrupt flag. Author: Erik Martines Sanches */
void resetT2IF(void) { IFSCLR(0) = 0x00000100; } //clears IFS(0) T2IF.
/*Checks if Timer 2 interrupt flag has been raised. Author: Erik Martines Sanches */
bool T2IF_raised(void){ return (((IFS(0) >> 8) & 0x1) == 1 ) ? 1 : 0; }
//These two lines are form the lab source ocde or the Toolchain FAQ:
void enable_interrupt(void) { asm volatile("ei"); }
void *stdout; //A workaround for enabling the use of srand() and rand().
//int mytime = 0x5957;
//char textstring[] = "text, more text, and even more text!";
/*Returns absolute value of x. Author: Erik Martines Sanches */
int abs1(int x){if (x < 0) { return -x; } else { return x;}}
/*Sets a bit in the frame array while preserving existing bits.
Author: Erik Martines Sanches */
void draw_pixel(int x, int y){
if(((unsigned)y < 32) && ((unsigned)x<128)){
if (y <= 7){ frame[x] |= (0x01 << (y));}
else if (y >= 8 && y <= 15) {frame[128 + x] |= (0x01 << (y % 8));}
else if (y >= 16 && y <= 23) {frame[256 + x] |= (0x01 << (y % 16));}
else if (y >= 24) {frame[384 + x] |= (0x01 << (y % 24));}
}
}
/*Helper function for plotLine().
Acquired from https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm */
void plotLineLow(int x0, int y0, int x1, int y1){
int dx = x1 - x0;
int dy = y1 - y0;
int yi = 1;
if(dy < 0){
yi = -1;
dy = -dy;
}
int D = 2 * dy - dx;
int y = y0;
int x;
for(x = x0; x <= x1; x++){
draw_pixel(x, y);
if(D > 0){
y = y + yi;
D = D - 2 * dx;
}
D = D + 2 * dy;
}
}
/*Helper function for plotLine().
Acquired from https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm */
void plotLineHigh(int x0, int y0, int x1, int y1){
int dx = x1 - x0;
int dy = y1 - y0;
int xi = 1;
if(dx < 0){
xi = -1;
dx = -dx;
}
int D = 2 * dx - dy;
int x = x0;
int y;
for(y = y0; y <= y1; y++){
draw_pixel(x, y);
if(D > 0){
x = x + xi;
D = D - 2 * dy;
}
D = D + 2 * dx;
}
}
/*Plots a line between the provided points using Bresenham's line algorithm.
Acquired from https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm */
void plotLine(int x0, int y0, int x1, int y1){
if (abs1(y1 - y0) < abs1(x1 - x0)){
if(x0 > x1){
plotLineLow(x1, y1, x0, y0);
} else {
plotLineLow(x0, y0, x1, y1);
}
} else {
if(y0 > y1){
plotLineHigh(x1, y1, x0, y0);
} else {
plotLineHigh(x0, y0, x1, y1);
}
}
}
/*Empties frame array, offsets according to input, puts edges into the frame array
Author: Erik Martines Sanches. */
void prepare_frame(void){
//Empty frame array to start on a new frame.
uint8_t *j;
for (j = frame; j < &frame[512]; ++j) { *j = 0x00; }
//First determine what tempDirection should be set to
//depending on if oldDirection and currentDirection differ.
enum direction tempDirection;
//When we switch to a different direction.
if((currentDirection != oldDirection) && (currentDirection != NONE )){
tempDirection = currentDirection;
oldDirection = tempDirection;
}
//Keep going in the same old direction, even if a button is pressed in that direction.
else { tempDirection = oldDirection; }
//Traverse the items array and put their shapes into the frame array.
node *p;
for(p = items; p < &items[NUMBER_OF_ITEMS]; ++p){ //Iterate over movable items.
if (p->movable) {
//Now offset the vertices positions accordingly.
int v;
for (v = 0; v < 4; v++){ //iterate over 4 vertices.
if (tempDirection == RIGHT) { p->vertices[v].x += p->offset_per_time_unit;}
if (tempDirection == DOWN) {p->vertices[v].y += p->offset_per_time_unit;}
if (tempDirection == UP) {p->vertices[v].y -= p->offset_per_time_unit;}
if (tempDirection == LEFT) {p->vertices[v].x -= p->offset_per_time_unit;}
}
}
//Now put the edges into the frame array.
if (p->drawable){
int i;
for (i = 0; i < 3; i++){ //iterate the first 3 vertices and connect the dots
plotLine((int)p->vertices[i].x, (int)p->vertices[i].y, (int)p->vertices[i+1].x, (int)p->vertices[i+1].y);
}
//connect last point to first.
plotLine((int)p->vertices[i].x, (int)p->vertices[i].y, (int)p->vertices[0].x, (int)p->vertices[0].y);
}
}
}
/* Buttons' status. Author: Erik Martines Sanches */
int getbtns(void){
uint8_t portDButtons = 0; //BTN 2-4 on bits RD5 - RD7
uint8_t portFButtons = 0; //BTN 1 on RF1
portDButtons = (PORTD >> 4) & 0xE; //This should move and mask the proper bits from PORTD.
portFButtons = (PORTF >> 1) & 0x1; //This should move and mask the proper bits from PORTF.
uint8_t buttonsStatus = portDButtons | portFButtons; //Combine the input from port D buttons and port F button.
return buttonsStatus;
}
/* Checks whether player covers an item and whether a player is within the screen. Changes player's
offset_per_time_unit and size. Author: Erik Martines Sanches */
void check_bounds_and_collisions(void){
node *p;
for(p = items; p < &items[NUMBER_OF_ITEMS]; ++p){ //Iterate across movable and drawable items (player).
if (p->movable && p->drawable) {
//Figure out largest and smallest x and y values so that we can use it for a bounding box of the movable item.
float smallest_player_x = p->vertices[0].x - 1; //To allow all of the player's bounding box to cover the ball, due to floating point.
float largest_player_x = p->vertices[0].x;
float smallest_player_y = p->vertices[0].y - 1; //To allow all of the player's bounding box to cover the ball, due to floating point.
float largest_player_y = p->vertices[0].y;
bool allVertsOutside = false; //Whether all vertices are outside the screen.
int count = 0; //For counting the vertices outside the screen.
position2D *v;
for (v = p->vertices; v < &p->vertices[4]; ++v){ //Iterate over four vertices to set the proper "rectangular bounding box".
if (v->x > largest_player_x){
largest_player_x = v->x;
}
if (v->x < smallest_player_x){
smallest_player_x = v->x;
}
if (v->y > largest_player_y){
largest_player_y = v->y;
}
if (v->y < smallest_player_y){
smallest_player_y = v->y;
}
//Checks when movable item is totally outside the screen.
if (v->x < 0 || v->y < 0 || v->x > 127 || v->y > 31){
count++; //A vertex is outside the screen.
}
if (count == 4) { allVertsOutside = true; }
}
if (allVertsOutside){ //Move the player to the middle, reset abilities.
*p = (node){true, true, 0, 0, INITIAL_SPEED, {{59,16}, {64,16}, {64,21}, {59,21}}};
currentDirection = NONE;
}
//Now check whether player encompasses any of the non-movable, drawable items (balls).
node *q;
for(q = items; q < &items[NUMBER_OF_ITEMS]; ++q){ //For every non-movable item.
if (q->movable == false && q->drawable == true ){//Of course this needs to check drawable I
//certainly don't want to do coverage detection with non-drawable, hidden non-movable items!
float smallest_ball_x = q->vertices[0].x; //Establish the ball's smallest and largest x and y.
float largest_ball_x = q->vertices[0].x;
float smallest_ball_y = q->vertices[0].y;
float largest_ball_y = q->vertices[0].y;
position2D *w;
for (w = q->vertices; w < &q->vertices[4]; ++w){ //Iterate over 4 vertiecs.
if (w->x > largest_ball_x) {largest_ball_x = w->x;}
if (w->x < smallest_ball_x) {smallest_ball_x = w->x;}
if (w->y > largest_ball_y) {largest_ball_y = w->y;}
if (w->y < smallest_ball_y) {smallest_ball_y = w->y;}
}
//Now check if ball is contained within player's bounding box.
if (smallest_ball_x >= smallest_player_x && largest_ball_x <= largest_player_x &&
smallest_ball_y >= smallest_player_y && largest_ball_y <= largest_player_y){
q->drawable = false; //The inner object (ball) hides.
p->items_collected++;//The outer object (player) collected one inner (ball).
hidden_items++;
//Items_collected_since_birth++;
if (p->items_collected == ITEMS_FOR_SPEED_1 || p->items_collected == ITEMS_FOR_SPEED_2){
//p->offset_per_time_unit++;
p->offset_per_time_unit += SPEED_INCREASE;
}
if (p->items_collected == ITEMS_FOR_SIZE_1 || p->items_collected == ITEMS_FOR_SIZE_2){
//This grows the square player in a cool way.
p->vertices[0].x -= GROW_BY_PIXELS;
p->vertices[0].y -= GROW_BY_PIXELS;
p->vertices[1].x += GROW_BY_PIXELS;
p->vertices[1].y -= GROW_BY_PIXELS;
p->vertices[2].y += GROW_BY_PIXELS;
p->vertices[2].x += GROW_BY_PIXELS;
p->vertices[3].x -= GROW_BY_PIXELS;
p->vertices[3].y += GROW_BY_PIXELS;
p->size++;
}
}
}
}
}
}
}
/*A float version of absolute value function. Author: Erik Martines Sanches */
float floatabs(float x){if (x < 0) { return -x; } else { return x;}}
/*This allows balls to avoid the player. Author: Erik Martines Sanches */
void avoid_player(void){
//First get first player's speed.
float players_offset_per_time_unit;
//Figure out player's largest and smallest x and y values so that we can use it for a bounding box.
float smallest_player_x; //To allow all of the player's bounding box to cover the ball, due to floating point.
float largest_player_x;
float smallest_player_y; //To allow all of the player's bounding box to cover the ball, due to floating point.
float largest_player_y;
node *r;
for(r = items; r < &items[NUMBER_OF_ITEMS]; ++r){
if(r->movable){
players_offset_per_time_unit = r->offset_per_time_unit;
//Figure out largest and smallest x and y values so that we can use it for a bounding box of the movable item.
smallest_player_x = r->vertices[0].x; //To allow all of the player's bounding box to cover the ball, due to floating point.
largest_player_x = r->vertices[0].x;
smallest_player_y = r->vertices[0].y; //To allow all of the player's bounding box to cover the ball, due to floating point.
largest_player_y = r->vertices[0].y;
position2D *o;
for (o = r->vertices; o < &r->vertices[4]; ++o){ //Iterate over four vertices to set the proper "rectangular bounding box".
if (o->x > largest_player_x){
largest_player_x = o->x;
}
if (o->x < smallest_player_x){
smallest_player_x = o->x;
}
if (o->y > largest_player_y){
largest_player_y = o->y;
}
if (o->y < smallest_player_y){
smallest_player_y = o->y;
}
}
}
}
//Now we know the "middle" of the bounding box.
position2D middle_of_player = {smallest_player_x + ((largest_player_x - smallest_player_x)/2), smallest_player_y + ((largest_player_y - smallest_player_y)/2)};
node *b; //Now iterate over all balls and offset them accordingly.
for(b = items; b < &items[NUMBER_OF_ITEMS]; ++b){
if((b->movable == false) && b->drawable){ //Balls are found as non-movable, ironically. Clean that up later.
//Get balls first vertex position in order to compare its location to the location of the player.
position2D balls_first_vertex = b->vertices[0];
//Only avoid when close.
if((floatabs(middle_of_player.x - balls_first_vertex.x) < AVOIDANCE_DISTANCE) && (floatabs(middle_of_player.y - balls_first_vertex.y) < AVOIDANCE_DISTANCE)){
if (currentDirection == UP){
if (balls_first_vertex.y > 3 && middle_of_player.y > balls_first_vertex.y){ //Player is lower than ball, ball avoids up.
int v;
for (v = 0; v < 4; v++){ //iterate over 4 vertices.
b->vertices[v].y -= players_offset_per_time_unit - BALL_DECELERATION;
}
}
else if (balls_first_vertex.y > middle_of_player.y && balls_first_vertex.y < 29){ //Player is higher than ball and ball avoids down.
int v;
for (v = 0; v < 4; v++){ //iterate over 4 vertices.
b->vertices[v].y += players_offset_per_time_unit - BALL_DECELERATION;
}
}
else if (middle_of_player.x >= balls_first_vertex.x && balls_first_vertex.x > 3){ //Player to the right of the ball, ball avoids to the left.
int v;
for (v = 0; v < 4; v++){ //iterate over 4 vertices.
b->vertices[v].x -= players_offset_per_time_unit - BALL_DECELERATION;
}
}
else if (middle_of_player.x < balls_first_vertex.x && balls_first_vertex.x < 125){ //Player is to the left of the ball, ball avoids right.
int v;
for (v = 0; v < 4; v++){ //iterate over 4 vertices.
b->vertices[v].x += players_offset_per_time_unit - BALL_DECELERATION;
}
}
}
else if (currentDirection == DOWN){
if (balls_first_vertex.y > 3 && middle_of_player.y > balls_first_vertex.y){ //Player is lower than ball, ball avoids up.
int v;
for (v = 0; v < 4; v++){ //iterate over 4 vertices.
b->vertices[v].y -= players_offset_per_time_unit - BALL_DECELERATION;
}
}
else if (balls_first_vertex.y > middle_of_player.y && balls_first_vertex.y < 29){ //Player is higher than ball and ball avoids down.
int v;
for (v = 0; v < 4; v++){ //iterate over 4 vertices.
b->vertices[v].y += players_offset_per_time_unit - BALL_DECELERATION;
}
}
else if (middle_of_player.x >= balls_first_vertex.x && balls_first_vertex.x > 3){ //Player to the right of the ball, ball avoids to the left.
int v;
for (v = 0; v < 4; v++){ //iterate over 4 vertices.
b->vertices[v].x -= players_offset_per_time_unit - BALL_DECELERATION;
}
}
else if (middle_of_player.x < balls_first_vertex.x && balls_first_vertex.x < 125){ //Player is to the left of the ball, ball avoids right.
int v;
for (v = 0; v < 4; v++){ //iterate over 4 vertices.
b->vertices[v].x += players_offset_per_time_unit - BALL_DECELERATION;
}
}
} /*The above UP and DOWN cases work well! Now do LEFT nad RIGHT.*/
else if (currentDirection == LEFT){
if (middle_of_player.x >= balls_first_vertex.x && balls_first_vertex.x > 3){ //Player to the right of the ball, ball avoids to the left.
int v;
for (v = 0; v < 4; v++){ //iterate over 4 vertices.
b->vertices[v].x -= players_offset_per_time_unit - BALL_DECELERATION;
}
}
if (middle_of_player.x < balls_first_vertex.x && balls_first_vertex.x < 125){ //Player is to the left of the ball, ball avoids right.
int v;
for (v = 0; v < 4; v++){ //iterate over 4 vertices.
b->vertices[v].x += players_offset_per_time_unit - BALL_DECELERATION;
}
}
else if (middle_of_player.y > balls_first_vertex.y && balls_first_vertex.y > 3 ){ //Player is lower than ball, ball avoids up.
int v;
for (v = 0; v < 4; v++){ //iterate over 4 vertices.
b->vertices[v].y -= players_offset_per_time_unit - BALL_DECELERATION;
}
}
else if (balls_first_vertex.y > middle_of_player.y && balls_first_vertex.y < 29){ //Player is higher than ball and ball avoids down.
int v;
for (v = 0; v < 4; v++){ //iterate over 4 vertices.
b->vertices[v].y += players_offset_per_time_unit - BALL_DECELERATION;
}
}
}
else if (currentDirection == RIGHT){
if (middle_of_player.x >= balls_first_vertex.x && balls_first_vertex.x > 3){ //Player to the right of the ball, ball avoids to the left.
int v;
for (v = 0; v < 4; v++){ //iterate over 4 vertices.
b->vertices[v].x -= players_offset_per_time_unit - BALL_DECELERATION;
}
}
if (middle_of_player.x < balls_first_vertex.x && balls_first_vertex.x < 125){ //Player is to the left of the ball, ball avoids right.
int v;
for (v = 0; v < 4; v++){ //iterate over 4 vertices.
b->vertices[v].x += players_offset_per_time_unit - BALL_DECELERATION;
}
}
if (middle_of_player.y > balls_first_vertex.y && balls_first_vertex.y > 3 ){ //Player is lower than ball, ball avoids up.
int v;
for (v = 0; v < 4; v++){ //iterate over 4 vertices.
b->vertices[v].y -= players_offset_per_time_unit - BALL_DECELERATION;
}
}
if (balls_first_vertex.y > middle_of_player.y && balls_first_vertex.y < 29){ //Player is higher than ball and ball avoids down.
int v;
for (v = 0; v < 4; v++){ //iterate over 4 vertices.
b->vertices[v].y += players_offset_per_time_unit - BALL_DECELERATION;
}
}
}
}
}
}
}
/* Polling buttons for input. Author: Erik Martines Sanches */
void process_input(void){ //This gets called on every frame for now.
switch (getbtns()){
case 0x0: currentDirection = NONE; break;
case 0x1: currentDirection = UP; break; //BTN1 right
case 0x2: currentDirection = DOWN; break; //BTN2 up
case 0x4: currentDirection = RIGHT; break; //BTN3 down
case 0x8: currentDirection = LEFT; break;//BTN4 left
}
}
#define DISPLAY_CHANGE_TO_COMMAND_MODE_ERIK (PORTFCLR = 0x10)
#define DISPLAY_CHANGE_TO_DATA_MODE_ERIK (PORTFSET = 0x10)
/*Credits shown when game is won. Author: Erik Martines Sanches*/
void credits(void) {
display_string(0, "Programming");
display_string(1, "");
display_string(2, "Erik Martines");
display_string(3, "Sanches");
DISPLAY_CHANGE_TO_COMMAND_MODE_ERIK;
spi_send_recv(0x2E); //deactivate scroll
display_update();
DISPLAY_CHANGE_TO_COMMAND_MODE_ERIK;
spi_send_recv(0x2A); //vertical and right horiz scroll
spi_send_recv(0x00); //dummy
spi_send_recv(0x00); //start page address
spi_send_recv(0x00); //5 frames between each scroll step
spi_send_recv(0x03); //end page address
spi_send_recv(0x00); //vertical scrolling offset in rows...
spi_send_recv(0x2F); //activate scroll
//Update display and wait for user input.
int counter_old = timeoutcount;
// bool changeMessage = true;
currentDirection = NONE;
quicksleep(1599999);
while(1){
process_input();
if ((currentDirection == RIGHT || currentDirection == DOWN || currentDirection == UP || currentDirection == LEFT) && ((timeoutcount - counter_old) > 30) ){
quicksleep(1599999); //To give some time for pushbutton to lift.
currentDirection = NONE;
break;
}
}
DISPLAY_CHANGE_TO_COMMAND_MODE_ERIK;
spi_send_recv(0x2E); //deactivate scroll
}
void winner(void){
timeoutcount = 0;
display_image(0, winner_text);
int counter_old = timeoutcount;
while(1){
process_input();
if ((currentDirection == RIGHT || currentDirection == DOWN || currentDirection == UP || currentDirection == LEFT) && ((timeoutcount - counter_old) > 30)){
delay_for_random_seed = timeoutcount;
quicksleep(1599999); //To give some time for pushbutton to lift.
currentDirection = NONE;
break;
}
}
}
/* Winning function. Author: Erik Martines Sanches */
void you_win(void) {
display_string(0, "Impressive. You");
display_string(1, "picked up all");
display_string(2, "the balls from");
display_string(3, "the court.");
display_update();
//Update display and wait for user input.
int counter_old = timeoutcount;
currentDirection = NONE;
while(1){
process_input();
if ((currentDirection == RIGHT || currentDirection == DOWN || currentDirection == UP || currentDirection == LEFT) && ((timeoutcount - counter_old) > 30) ){
quicksleep(1599999); //To give some time for pushbutton to lift.
currentDirection = NONE;
break;
}
}
}
/* Losing function. Author: Erik Martines Sanches */
void you_lose(void) {
if (NUMBER_OF_ITEMS - hidden_items < 3) {
display_string(0, "You lost with");
display_string(1, "one ball left!");
display_string(2, "Concentrate.");
display_string(3, "You've got this.");
}
else if (NUMBER_OF_ITEMS - hidden_items < 4) {
display_string(0, "You lost.");
display_string(1, "Just two balls");
display_string(2, "left!");
display_string(3, "Don't despair.");
} else if (NUMBER_OF_ITEMS - hidden_items < 5) {
display_string(0, "You lost.");
display_string(1, "Three balls");
display_string(2, "left. Summon");
display_string(3, "your powers.");
} else if (NUMBER_OF_ITEMS - hidden_items < 10) {
display_string(0, "You lost.");
display_string(1, "");
display_string(2, "Don't get");
display_string(3, "discouraged!");
} else if (NUMBER_OF_ITEMS - hidden_items < 15) {
display_string(0, "You lost.");
display_string(1, "But I admit,");
display_string(2, "this time it");
display_string(3, "was close.");
} else if (NUMBER_OF_ITEMS - hidden_items < 20) {
display_string(0, "You lost.");
display_string(1, "Train harder");
display_string(2, "than anyone");
display_string(3, "else!");
} else if (NUMBER_OF_ITEMS - hidden_items < 25) {
display_string(0, "Not quite.");
display_string(1, "The will is");
display_string(2, "greater than");
display_string(3, "the means.");
} else if (NUMBER_OF_ITEMS - hidden_items < 30) {
display_string(0, "You lost but");
display_string(1, "don't give up.");
display_string(2, "There is a way.");
display_string(3, "");
} else if (NUMBER_OF_ITEMS - hidden_items < 40) {
display_string(0, "You lost.");
display_string(1, "What will the");
display_string(2, "club owner say?");
display_string(3, "");
} else {
display_string(0, "You lost.");
display_string(1, "A lot of balls");
display_string(2, "were left on the");
display_string(3, "court.");
}
//Update display and wait for user input.
display_update();
int counter_old = timeoutcount;
currentDirection = NONE;
while(1){
process_input();
if ((currentDirection == RIGHT || currentDirection == DOWN || currentDirection == UP || currentDirection == LEFT) && ((timeoutcount - counter_old) > 30) ){
quicksleep(1599999); //To give some time for pushbutton to lift.
currentDirection = NONE;
break;
}
}
}
/* Updates game status depending on state. Author: Erik Martines Sanches */
void set_game_status(void){
if (timeoutcount % TICK_TIME == 0){ time_units_left--; }
if(hidden_items == NUMBER_OF_ITEMS - 1){
game_status = WON;
currentDirection = NONE;
} else if(time_units_left == 0){
game_status = LOST;
currentDirection = NONE;
PORTECLR = 0xFF;
} else {
game_status = CONTI;
}
}
/* Lights the LEDs to show time units left before game over. Author: Erik Martines Sanches */
void update_leds(void){
switch(time_units_left){
case 8: PORTECLR = 0xFF; PORTESET = 0xFF; break;
case 7: PORTECLR = 0xFF; PORTESET = 0x7F; break;
case 6: PORTECLR = 0xFF; PORTESET = 0x3F; break;
case 5: PORTECLR = 0xFF; PORTESET = 0x1F; break;
case 4: PORTECLR = 0xFF; PORTESET = 0x0F; break;
case 3: PORTECLR = 0xFF; PORTESET = 0x07; break;
case 2: PORTECLR = 0xFF; PORTESET = 0x03; break;
case 1: PORTECLR = 0xFF; PORTESET = 0x01; break;
case 0: PORTECLR = 0xFF; PORTESET = 0x00; break;
}
}
/* Waits for user input on the start screen. Uses the time taken to seed srand().
Author: Erik Martines Sanches */
void time_start_screen(void){
timeoutcount = 0;
display_image(0,icon);
int counter_old = timeoutcount;
while(1){
process_input();
if ((currentDirection == RIGHT || currentDirection == DOWN || currentDirection == UP || currentDirection == LEFT) && ((timeoutcount - counter_old) > 30)){
delay_for_random_seed = timeoutcount;
quicksleep(1599999); //To give some time for pushbutton to lift.
currentDirection = NONE;
break;
}
}
}
/* Shows the objective of the game and waits for a button press.
Author: Erik Martines Sanches */
void show_mission(){
int counter_old = timeoutcount;
currentDirection = NONE;
display_string(0, "Grab all balls");
display_string(1, "before the LEDs");
display_string(2, "turn off. Don't");
display_string(3, "go too far out.");
display_update();
while(1){
process_input();
if ((currentDirection == RIGHT || currentDirection == DOWN || currentDirection == UP || currentDirection == LEFT) && ((timeoutcount - counter_old) > 30) ){
quicksleep(1599999); //To give some time for pushbutton to lift.
currentDirection = NONE;
break;
}
}
}
/* Initially we insert the items into items array, with appropriate settings
Author: Erik Martines Sanches */
void insert_items(void){
int i;
for(i = 0; i < NUMBER_OF_ITEMS - 1; i++){ //Insert balls and leave room for player.
int random_x = rand() % 125;
int random_y = rand() % 29;
items[i] = (node){false, true, 0, 0, 0.0, {{random_x+1, random_y}, {random_x+2, random_y+1}, {random_x+1, random_y+2}, {random_x, random_y+1}}}; //Partially randomised locations.
}
items[NUMBER_OF_ITEMS-1] = (node){true, true, 0, 0, INITIAL_SPEED, {{59,16}, {64,16}, {64,21}, {59,21}}}; //Insert player in the middle of the screen.
}
/* This function is called repetitively from the main program
Author: Erik Martines Sanches*/
void labwork(void){
if(prepare_frame_ok){
prepare_frame_ok = false;
process_input();
update_leds();
prepare_frame(); //Clears frame. This also applies offsets items.
if (hidden_items >= ITEMS_FOR_AVOIDANCE){ //Balls avoid player if player is big enough and has picked up a number of balls.
node *p;
for (p = items; p < &items[NUMBER_OF_ITEMS]; ++p){
if (p->movable && p->size == 2){
avoid_player();
}
}
}
check_bounds_and_collisions(); //If outside screen, reset into start position. If over pickupable object, do pick it up and potentially tranform.
display_image(0, frame);
set_game_status(); //Whether to continue the game or not.
}
}
/* Initializes the game on every round. Author: Erik Martines Sanches */
void labinit(void) {
T2CON = 0x0; //Stop timer and clear control register.
T2CONSET = 0x70; //Set prescale to 256.
TMR2 = 0x0; //Clear timer register.
//80000000/256/10 = 31250 decimal = 0x7A12 this is for 0.1s.
//PR2 = 0x7A12;
PR2 = 0x186A; //50Hz
//Setting up interrupts for timer2:
IFSCLR(0) = 0x100; //Clear T2IF in IFS2 register.
IPCSET(2) = 0x1F; //Configuring interrupt priority and subpriority to 7 and 3 (highest).
IECSET(0) = 0x100; //Set T2IE interrupt enable bit in IEC0 register.
enable_interrupt(); //Enables interrupt.
T2CONSET = 0x8000; //Start timer.
//Setup registers for input change notification (pushbuttons).
//BTN3 Chipkit pin# 36 / RD6
// TRISDSET = 0x40; //Set pushbutton btn 3 on RD6 as input.
// CNENSET = 0x8000; //CNEN15 enable
// CNCONSET = 0x8000; //CNCON on
// CNPUESET = 0x8000; // change notice pull up enable
/* IFSCLR(0) = 0x8;//Clear INT0 IF interrupt flag
IPCSET(0) = 0x1F000000; //INT0 priority and subpriority set to max.
IECSET(0) = 0x8; //INT0 Interrupt enable. */
currentDirection = NONE;
hidden_items = 0;
timeoutcount = 0;
delay_for_random_seed = 0;
PORTECLR = 0xFF;
time_start_screen(); //Get a time period to seed srand() with.
show_mission();
srand(delay_for_random_seed);
insert_items(); //Inserts into items array.
time_units_left = 8;
currentDirection = NONE;
oldDirection = NONE;
game_status = CONTI;
}
/* Interrupt Service Routine. Author: Erik Martines Sanches*/
void user_isr(void){
/* if (((IFS(0) >> 3) & 0x1) == 1 ){ //check if interrupt flag from BTN2 pin 34, has been set
PORTECLR = 0xFF;
PORTEINV = 0x80;
draw_pixel(0,0);
IFSCLR(0) = 0x8; //clear the flag
} */
if(T2IF_raised()) {
timeoutcount++;
//if(timeoutcount == 10){
// resetT2IF();
prepare_frame_ok = true;
// timeoutcount = 0;
//} else {
resetT2IF();
//}
}
return;
}