1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <string.h>
5 #include <time.h>
6 
7 #include "types.h"
8 #include "util.h"
9 
10 #include "Bill.h"
11 #include "Bucket.h"
12 #include "Computer.h"
13 #include "Cable.h"
14 #include "Game.h"
15 #include "Horde.h"
16 #include "Network.h"
17 #include "OS.h"
18 #include "Scorelist.h"
19 #include "Spark.h"
20 #include "UI.h"
21 
22 #define SCREENSIZE 400
23 
24 /* Game states */
25 #define STATE_PLAYING 1
26 #define STATE_BETWEEN 2
27 #define STATE_END 3
28 #define STATE_WAITING 4
29 
30 /* Score related constants */
31 #define SCORE_ENDLEVEL -1
32 #define SCORE_BILLPOINTS 5
33 
34 static unsigned int state;
35 static int efficiency;
36 static int score, level, iteration;
37 static Picture *logo, *icon, *about;
38 static MCursor *defaultcursor, *downcursor;
39 static Bill *grabbed;
40 static const char *gui;
41 static int screensize = SCREENSIZE;
42 
43 static void
setup_level(int newlevel)44 setup_level(int newlevel) {
45 	level = newlevel;
46 	Horde_setup();
47 	grabbed = NULL;
48 	UI_set_cursor(defaultcursor);
49 	Network_setup();
50 	iteration = 0;
51 	efficiency = 0;
52 }
53 
54 void
Game_start(int newlevel)55 Game_start(int newlevel) {
56 	state = STATE_PLAYING;
57 	score = 0;
58 	UI_restart_timer();
59 	UI_set_pausebutton(1);
60 	setup_level(newlevel);
61 }
62 
63 void
Game_quit()64 Game_quit() {
65 	Scorelist_write();
66 	exit(0);
67 }
68 
69 static void
update_info(void)70 update_info(void) {
71 	char str[80];
72 	int on_screen = Horde_get_counter(HORDE_COUNTER_ON);
73 	int off_screen = Horde_get_counter(HORDE_COUNTER_OFF);
74 	int base = Network_get_counter(NETWORK_COUNTER_BASE);
75 	int off = Network_get_counter(NETWORK_COUNTER_OFF);
76 	int win = Network_get_counter(NETWORK_COUNTER_WIN);
77 	int units = Network_num_computers();
78 	sprintf(str, "Bill:%d/%d  System:%d/%d/%d  Level:%d  Score:%d",
79 		on_screen, off_screen, base, off, win, level, score);
80 	UI_draw_str(str, 5, screensize - 5);
81 	efficiency += ((100 * base - 10 * win) / units);
82 }
83 
84 void
Game_warp_to_level(int lev)85 Game_warp_to_level(int lev) {
86 	if (state == STATE_PLAYING) {
87 		if (lev <= level)
88 			return;
89 		setup_level(lev);
90 	}
91 	else {
92 		if (lev <= 0)
93 			return;
94 		Game_start(lev);
95 	}
96 }
97 
98 void
Game_add_high_score(const char * str)99 Game_add_high_score(const char *str) {
100 	Scorelist_recalc(str, level, score);
101 }
102 
103 void
Game_button_press(int x,int y)104 Game_button_press(int x, int y) {
105 	int counter;
106 
107 	if (state != STATE_PLAYING)
108 		return;
109 	UI_set_cursor(downcursor);
110 
111 	if (Bucket_clicked(x, y)) {
112 		Bucket_grab(x, y);
113 		return;
114 	}
115 
116 	grabbed = Horde_clicked_stray(x, y);
117 	if (grabbed != NULL) {
118 		OS_set_cursor(grabbed->cargo);
119 		return;
120 	}
121 
122 	counter = Horde_process_click(x, y);
123 	score += (counter * counter * SCORE_BILLPOINTS);
124 }
125 
126 void
Game_button_release(int x,int y)127 Game_button_release(int x, int y) {
128 	int i;
129 	UI_set_cursor(defaultcursor);
130 
131 	if (state != STATE_PLAYING)
132 		return;
133 
134 	if (grabbed == NULL) {
135 		Bucket_release(x, y);
136 		return;
137 	}
138 
139 	for (i = 0; i < Network_num_computers(); i++) {
140 		Computer *computer = Network_get_computer(i);
141 
142 		if (Computer_on(computer, x, y) &&
143 		    Computer_compatible(computer, grabbed->cargo) &&
144 		    (computer->os == OS_WINGDOWS || computer->os == OS_OFF)) {
145 			int counter;
146 
147 			Network_inc_counter(NETWORK_COUNTER_BASE, 1);
148 			if (computer->os == OS_WINGDOWS)
149 				counter = NETWORK_COUNTER_WIN;
150 			else
151 				counter = NETWORK_COUNTER_OFF;
152 			Network_inc_counter(counter, -1);
153 			computer->os = grabbed->cargo;
154 			Horde_remove_bill(grabbed);
155 			grabbed = NULL;
156 			return;
157 		}
158 	}
159 	Horde_add_bill(grabbed);
160 	grabbed = NULL;
161 }
162 
163 static void
draw_logo(void)164 draw_logo(void) {
165 	UI_clear();
166 	UI_draw(logo,
167 		(screensize - UI_picture_width(logo)) / 2,
168 		(screensize - UI_picture_height(logo)) / 2);
169 }
170 
171 void
Game_update()172 Game_update() {
173 	char str[40];
174 
175 	switch (state) {
176 	case STATE_PLAYING:
177 		UI_clear();
178 		Bucket_draw();
179 		Network_update();
180 		Network_draw();
181 		Horde_update(iteration);
182 		Horde_draw();
183 		update_info();
184 		if (Horde_get_counter(HORDE_COUNTER_ON) +
185 		    Horde_get_counter(HORDE_COUNTER_OFF) == 0) {
186 			score += (level * efficiency / iteration);
187 			state = STATE_BETWEEN;
188 		}
189 		if ((Network_get_counter(NETWORK_COUNTER_BASE) +
190 		     Network_get_counter(NETWORK_COUNTER_OFF)) <= 1)
191 			state = STATE_END;
192 		break;
193 	case STATE_END:
194 		UI_set_cursor(defaultcursor);
195 		UI_clear();
196 		Network_toasters();
197 		Network_draw();
198 		UI_refresh();
199 		UI_popup_dialog(DIALOG_ENDGAME);
200 		if (Scorelist_ishighscore(score)) {
201 			UI_popup_dialog(DIALOG_ENTERNAME);
202 			Scorelist_update();
203 		}
204 		UI_popup_dialog(DIALOG_HIGHSCORE);
205 		draw_logo();
206 		UI_kill_timer();
207 		UI_set_pausebutton(0);
208 		state = STATE_WAITING;
209 		break;
210 	case STATE_BETWEEN:
211 		UI_set_cursor(defaultcursor);
212 		sprintf(str, "After Level %d:\nScore: %d", level, score);
213 		UI_update_dialog(DIALOG_SCORE, str);
214 		UI_popup_dialog(DIALOG_SCORE);
215 		state = STATE_PLAYING;
216 		setup_level(++level);
217 		break;
218 	}
219 	UI_refresh();
220 	iteration++;
221 }
222 
223 int
Game_score()224 Game_score() {
225 	return score;
226 }
227 
228 int
Game_level()229 Game_level() {
230 	return level;
231 }
232 
233 int
Game_screensize()234 Game_screensize() {
235 	return screensize;
236 }
237 
238 double
Game_scale(int dimensions)239 Game_scale(int dimensions) {
240 	double scale = (double)screensize / SCREENSIZE;
241 	double d = 1;
242 	for ( ; dimensions > 0; dimensions--)
243 		d *= scale;
244 	return (d);
245 }
246 
247 /*
248  * Note - don't use getopt, since it might reorder the args or do something
249  * that the UI-specific argument parser doesn't like.
250  */
251 static void
parse_args(int argc,char ** argv)252 parse_args(int argc, char **argv) {
253 	char *s;
254 	char *endp;
255 	int i;
256 
257 	for (i = 1; i < argc; i++) {
258 		if (strncasecmp(argv[i], "-l", 2) == 0) {
259 			if (strlen(argv[i]) == 2 && i == argc - 1)
260 				fatal("-l takes an argument");
261 			if (strlen(argv[i]) > 2)
262 				s = argv[i] + 2;
263 			else
264 				s = argv[++i];
265 			level = strtol(s, &endp, 10);
266 			if (*endp != '\0' || level <= 0)
267 				fatal("invalid level '%s'", s);
268 		} else if (strcmp(argv[i], "--gui") == 0) {
269 			if (i == argc - 1)
270 				fatal("--gui takes an argument");
271 			gui = argv[++i];
272 		} else if (strcmp(argv[i], "--size") == 0) {
273 			if (i == argc - 1)
274 				fatal("--size takes an argument");
275 			s = argv[++i];
276 			screensize = strtol(s, &endp, 10);
277 			if (*endp != '\0' || screensize <= 0)
278 				fatal("invalid screensize '%s'", s);
279 			if (screensize < SCREENSIZE)
280 				fatal("screensize must be larger than '%d'",
281 				       SCREENSIZE);
282 		} else if (strcmp(argv[1], "-v") == 0 ||
283 			   strcmp(argv[1], "--version") == 0)
284 		{
285 			printf ("XBill version 2.1\n\n");
286 			exit(0);
287 		} else if (strcmp(argv[1], "-h") == 0 ||
288 			   strcmp(argv[1], "--help") == 0)
289 		{
290 			printf("XBill version 2.1\n");
291 			printf("Options:\n");
292 			printf("-l <n>\tStart at level n.\n");
293 			printf("--gui <gui> \tUse a specific gui "
294 			       "(athena, motif, gtk)\n");
295 			printf("--size <size>\t\tUse a larger playing area.\n");
296 			printf("-v\t\tPrint version number and exit.\n");
297 			printf("-h\t\tPrint help and exit.\n");
298 			printf("All standard toolkit options are also ");
299 			printf("supported.\n\n");
300 			exit(0);
301 		}
302 	}
303 }
304 
305 int
main(int argc,char ** argv)306 main(int argc, char **argv) {
307 	srand(time(NULL));
308 	parse_args(argc, argv);
309 	UI_initialize(gui, &argc, argv);
310 	UI_make_main_window(screensize);
311 	UI_graphics_init();
312 	UI_load_picture("logo", 0, &logo);
313 	UI_load_picture("icon", 0, &icon);
314 	UI_load_picture("about", 0, &about);
315 	draw_logo();
316 	UI_refresh();
317 	UI_make_dialogs(logo, icon, about);
318 	UI_set_icon(icon);
319 
320 	Scorelist_read();
321 	Scorelist_update();
322 
323 	UI_load_cursor("hand_up", CURSOR_SEP_MASK, &defaultcursor);
324 	UI_load_cursor("hand_down", CURSOR_SEP_MASK, &downcursor);
325 	UI_set_cursor(defaultcursor);
326 
327 	Bill_load_pix();
328 	OS_load_pix();
329 	Computer_load_pix();
330 	Bucket_load_pix();
331 	Spark_load_pix();
332 
333 	state = STATE_WAITING;
334 	if (level)
335 		Game_start(level);
336 	else
337 		UI_set_pausebutton(0);
338 	UI_main_loop();
339 	exit(0);
340 }
341