2 * A very stupid seabattle game written using a very stupid game library
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * Luiz Fernando N. Capitulino
19 * <lcapitulino@gmail.com>
26 #include <sys/types.h>
30 * Screen size, note that (x, y) = (w, h)
32 #define DEF_SCREEN_W 640
33 #define DEF_SCREEN_H 480
36 * Ship related definitions
40 #define SHIP_WATER_HIT 1
42 #define SHIP_SUB_HIT 3
44 #define SHIP_DEST_HIT 5
46 #define SHIP_ARCC_HIT 7
47 #define SHIP_LAST SHIP_ARCC_HIT
48 #define SHIP_MAX (SHIP_LAST + 1)
54 #define SHIP_DEST_NR 4
55 #define SHIP_ARCC_NR 3
56 #define SHIP_DEF_NR (SHIP_SUB_NR + SHIP_DEST_NR + SHIP_ARCC_NR)
59 * Low-level arrays sizes
61 #define MAP_ARRAY_W (DEF_SCREEN_W / SHIP_W / 2)
62 #define MAP_ARRAY_H (DEF_SCREEN_H / SHIP_H)
67 static BITMAP
*buffer
, *ships
[SHIP_MAX
];
69 static char *ships_pics
[] = {
80 struct player_object
{
82 int map_array
[MAP_ARRAY_H
][MAP_ARRAY_W
];
85 static struct player_object player_user
, player_comp
;
88 * Misc functions and macros
91 #define ROUNDDOWN(a, n) \
93 uint32_t __a = (uint32_t) (a); \
94 (typeof(a)) (__a - __a % (n)); \
98 * die(): die with a nice message from the user
100 static void die(const char *fmt
, ...)
105 vfprintf(stderr
, fmt
, va
);
112 * Allegro and generic game related functions
116 * game_color_depth(): Set the color depth to be used
118 static void game_color_depth(void)
122 depth
= desktop_color_depth();
125 set_color_depth(depth
);
129 * game_srand_init(): Initialize the random number
132 static void game_srand_init(void)
138 fd
= open("/dev/urandom", O_RDONLY
);
145 bytes
= read(fd
, &seed
, sizeof(unsigned int));
146 if (bytes
!= sizeof(unsigned int))
147 die("Could not read enough bytes\n");
149 die("seed is zero\n");
156 * game_rand(): Return two random generated numbers, in
157 * the range [0, MAP_ARRAY_H). Can only be called after
158 * a call to game_srand_init()
160 static void game_srand(int *x
, int *y
)
162 *x
= rand() % MAP_ARRAY_W
;
163 *y
= rand() % MAP_ARRAY_H
;
167 * game_print(): Print a single message in the screen
170 static void game_print(const char *s
)
172 textprintf_centre_ex(screen
, font
, DEF_SCREEN_W
/ 2, 120,
173 makecol(0, 255, 0), -1,
177 static void player_init(struct player_object
*p
);
180 * game_init(): Initialize every single thing we need
181 * to make this game to work
183 static void game_init(void)
190 err
= set_gfx_mode(GFX_AUTODETECT_WINDOWED
, DEF_SCREEN_W
,
193 allegro_message(allegro_error
);
200 buffer
= create_bitmap(SCREEN_W
, SCREEN_H
);
202 allegro_message(allegro_error
);
207 for (i
= 0; i
< SHIP_MAX
; i
++) {
208 ships
[i
] = load_bitmap(ships_pics
[i
], NULL
);
210 allegro_message(allegro_error
);
215 player_init(&player_user
);
216 player_init(&player_comp
);
222 * game_install_mouse(): install the mouse handling registering
225 static void game_install_mouse(void (*callback
)(int flags
))
227 mouse_callback
= callback
;
232 * game_remove_mouse(): remove a mouse handler and its
233 * registered callback
235 static void game_remove_mouse(void)
239 mouse_callback
= NULL
;
243 * game_deinit(): free all allocated memory
245 static void game_deinit(void)
251 for (i
= 0; i
< SHIP_MAX
; i
++)
252 destroy_bitmap(ships
[i
]);
254 destroy_bitmap(buffer
);
258 * Ship related functions
262 * ship_draw(): Draw a ship in the double buffer
264 static void ship_draw(int ship
, int x
, int y
)
266 if (ship
< SHIP_FIRST
|| ship
> SHIP_LAST
)
267 die("%s: unknown ship code: %d\n", __FUNCTION__
, ship
);
269 draw_sprite(buffer
, ships
[ship
], x
, y
);
273 * ship_is_hidden(): Return true is ship must not be visible
276 static int ship_is_hidden(int ship
)
289 * ship_is_hit(): Return true is ship is of the hit type
291 static int ship_is_hit(int ship
)
305 * ship_get_size(): Return the size of a given ship, if the
306 * ship doesn't exist return -1
308 static int ship_get_size(int ship
)
323 * Player object methods
327 * player_str(): Return a string which describes the player_object
330 static const char *player_str(const struct player_object
*p
)
332 return (p
== &player_user
? "user" : "computer");
336 * player_ship_nr_def(): Set the default number of ships
338 static void player_ship_nr_def(struct player_object
*p
)
340 p
->ship_nr
= SHIP_DEF_NR
;
344 * player_init(): Initialize a player object
346 static void player_init(struct player_object
*p
)
350 for (y
= 0; y
< MAP_ARRAY_H
; y
++)
351 for (x
= 0; x
< MAP_ARRAY_W
; x
++)
352 p
->map_array
[y
][x
] = 0;
354 player_ship_nr_def(p
);
358 * player_array_dump(): Dump low-level array contents
360 static void player_array_dump(struct player_object
*p
)
364 map
= &p
->map_array
[0][0];
366 printf("%s map:\n", player_str(p
));
367 for (i
= 0; i
< MAP_ARRAY_H
; i
++) {
368 for (j
= 0; j
< MAP_ARRAY_W
; j
++, map
++)
377 * player_ship_nr(): Return the number of ships the
378 * object currently has
380 static int player_ship_nr(const struct player_object
*p
)
386 * player_ship_nr_dec(): Decrement by 1 the number of ships the
387 * object currently has
389 static void player_ship_nr_dec(struct player_object
*p
)
395 * player_array_set(): set 'ship' in the (x, y) positions
396 * of the provided player's map array.
398 * If the position was really set (ie, the previous element
399 * was SHIP_WATER) we return 1, otherwise return 0.
401 static int player_array_set(struct player_object
*p
,
402 int ship
, int x
, int y
)
404 if (p
->map_array
[y
][x
] == SHIP_WATER
) {
405 p
->map_array
[y
][x
] = ship
;
413 * player_ship_set(): set 'ship' in the (x, y) positions
414 * of the provided player's map array.
416 * If the position was really set (ie, the previous element
417 * stored in there was different) we return 1 and decrement
418 * ship_nr, otherwise just return 0
420 static int player_ship_set(struct player_object
*p
,
421 int ship
, int x
, int y
)
423 if (player_array_set(p
, ship
, x
, y
)) {
424 player_ship_nr_dec(p
);
432 * player_ship_complex_set(): Wrapper for player_ship_set() that
433 * supports loading ships with more than one part. Detects
434 * collisions and other problems.
436 * Return 1 if the position was set, 0 otherwise
438 static int player_ship_complex_set(struct player_object
*p
,
439 int ship
, int x
, int y
)
443 parts
= ship_get_size(ship
);
445 die("ship: %d has %d parts\n", ship
, parts
);
447 // Do not allow ships to cross the map
448 if ((x
+ parts
) > MAP_ARRAY_W
)
451 // Check if a collision is possible: left to right
452 for (i
= 0; i
< parts
; i
++)
453 if (p
->map_array
[y
][x
+ i
] != SHIP_WATER
)
456 // Okay, just set it then
457 for (i
= 0; i
< parts
; i
++)
458 player_ship_set(p
, ship
, x
+ i
, y
);
465 * player_map_draw(): Draw a player's map in the screen
467 * Note that we're smart, we know how to hide ships
469 static void player_map_draw(struct player_object
*p
,
470 int start_x
, int start_y
, int hide
)
474 map
= &p
->map_array
[0][0];
476 for (y
= start_y
; y
< (MAP_ARRAY_H
+ start_y
); y
++)
477 for (x
= start_x
; x
< (MAP_ARRAY_W
+ start_x
); x
++, map
++) {
478 if (hide
&& ship_is_hidden(*map
)) {
479 ship_draw(SHIP_WATER
,x
* SHIP_W
,y
* SHIP_H
);
483 ship_draw(*map
, x
* SHIP_W
, y
* SHIP_H
);
490 * player_shoot(): Standard shooting function
492 * Return 1 if the shoot was valid, 0 otherwise
494 static int player_shoot(struct player_object
*p
, int x
, int y
)
498 ship
= &p
->map_array
[y
][x
];
499 if (*ship
< SHIP_FIRST
|| *ship
> SHIP_LAST
) {
500 die("%s: unknow ship code from '%s': %d\n", __FUNCTION__
,
504 if (ship_is_hit(*ship
))
507 *ship
+= 1; // SHIP_*_HIT
509 if (*ship
!= SHIP_WATER_HIT
)
510 player_ship_nr_dec(p
);
520 * map_to_array(): Transforms (x, y) map positions into
521 * (x, y) low-level array positions
523 static void map_to_array(int mx
, int my
, int *ax
, int *ay
)
525 *ax
= ROUNDDOWN(mx
, SHIP_W
) / SHIP_W
;
526 *ay
= ROUNDDOWN(my
, SHIP_H
) / SHIP_H
;
530 * map_clear(): Clear the main map
532 static void map_clear(void)
534 clear_bitmap(buffer
);
538 * User (player) related functions
542 * mouse_set_user_map(): Limit the mouse pointer in the user
545 static void mouse_set_user_map(void)
547 set_mouse_range(0, 0, MAP_ARRAY_W
* SHIP_W
- 1,
548 SCREEN_H
- SHIP_H
- 1);
552 * user_setup_mouse_callback(): Mouse callback used by the
555 static void user_setup_mouse_callback(int event
)
559 if (event
!= MOUSE_FLAG_LEFT_DOWN
)
562 ship_nr
= player_ship_nr(&player_user
);
566 map_to_array(mouse_x
, mouse_y
, &x
, &y
);
569 player_ship_set(&player_user
, SHIP_SUB
, x
, y
);
570 else if (ship_nr
> 3)
571 player_ship_complex_set(&player_user
, SHIP_DEST
, x
, y
);
573 player_ship_complex_set(&player_user
, SHIP_ARCC
, x
, y
);
577 * user_setup(): Setup user map and array
579 static void user_setup(void)
581 player_array_dump(&player_user
);
582 mouse_set_user_map();
584 game_install_mouse(user_setup_mouse_callback
);
587 if (!player_ship_nr(&player_user
))
590 if (player_ship_nr(&player_user
) < 0) {
591 die("%s: user ships: %d\n", __FUNCTION__
,
592 player_ship_nr(&player_user
));
596 die("%s: user abort\n", __FUNCTION__
);
599 player_map_draw(&player_user
, 0, 0, 0);
600 draw_sprite(screen
, buffer
, 0, 0);
604 player_array_dump(&player_user
);
606 // Reset user's ship number, since it was set
607 // to zero by the setup code
608 player_ship_nr_def(&player_user
);
612 * user_shoot(): fire!!
614 static int user_shoot(int x
, int y
)
616 // XXX: Not sure why is this -1 thing needed...
619 return player_shoot(&player_comp
, x
, y
);
622 static void comp_shoot(void);
625 * user_shoot_mouse_callback(): Mouse callback used by the
628 static void user_shoot_mouse_callback(int event
)
632 if (event
!= MOUSE_FLAG_LEFT_DOWN
)
635 if (!player_ship_nr(&player_comp
))
638 if (!player_ship_nr(&player_user
))
641 map_to_array(mouse_x
, mouse_y
, &x
, &y
);
643 ret
= user_shoot(x
, y
);
645 // If the shoot was invalid, we let the user
650 if (!player_ship_nr(&player_comp
))
654 * Okay, user's shoot was valid. Now it's the
657 * It's a bit kludge to call the computer's
658 * shoot function from the user one, but the computer
659 * can only shoot just after the user.
665 * Computer (player) related functions
669 * mouse_set_comp_map(): Limit the mouse pointer in the
672 static void mouse_set_comp_map(void)
674 set_mouse_range(MAP_ARRAY_W
* SHIP_W
+ SHIP_W
- 1, 0,
675 SCREEN_W
- 1, SCREEN_H
- SHIP_H
- 1);
680 * comp_setup(): Setup computer's array
682 static void comp_setup(void)
686 while (player_ship_nr(&player_comp
)) {
690 ship_nr
= player_ship_nr(&player_comp
);
692 player_ship_set(&player_comp
, SHIP_SUB
, x
, y
);
693 else if (ship_nr
> 3)
694 player_ship_complex_set(&player_comp
, SHIP_DEST
,x
,y
);
696 player_ship_complex_set(&player_comp
, SHIP_ARCC
,x
,y
);
699 player_array_dump(&player_comp
);
701 // Reset computer's ship number, since it was set
702 // to zero by the setup code
703 player_ship_nr_def(&player_comp
);
707 * comp_shoot(): fire!!
709 static void comp_shoot(void)
715 ret
= player_shoot(&player_user
, x
, y
);
728 game_install_mouse(user_shoot_mouse_callback
);
729 mouse_set_comp_map();
731 while (!key
[KEY_ESC
] && !key
[KEY_ENTER
]) {
733 player_map_draw(&player_user
, 0, 0, 0);
734 player_map_draw(&player_comp
, MAP_ARRAY_W
+ 1, 0, 1);
736 draw_sprite(screen
, buffer
, 0, 0);
738 if (!player_ship_nr(&player_comp
)) {
740 game_print("USER'S VICTORY!!");
743 if (!player_ship_nr(&player_user
)) {
745 game_print("COMPUTER'S VICTORY!!");