1 /*
2  *     gtkatlantic - the gtk+ monopd client, enjoy network monopoly games
3  *
4  *
5  *  Copyright © 2002-2015 Sylvain Rochet
6  *
7  *  gtkatlantic is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; see the file COPYING. If not, see
19  *  <http://www.gnu.org/licenses/>.
20  */
21 
22 #include "config.h"
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <gtk/gtk.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <time.h>
31 #include <sys/time.h>
32 #include <gtk/gtk.h>
33 
34 #include <libxml/parser.h>
35 
36 #include "global.h"
37 #include "game.h"
38 #include "load.h"
39 #include "interface.h"
40 #include "client.h"
41 #include "trade.h"
42 #include "engine.h"
43 #include "callback.h"
44 
45 #ifdef WIN32
46 #include <windows.h>
47 #include <io.h>
48 #include <shlobj.h>
49 #define mkdir(p, m) _mkdir(p)
50 #endif
51 
52 /* create home directory (~/.gtkatlantic) *
53  * return TRUE if exist/created, return FALSE if cannot be created */
create_home_directory(void)54 void create_home_directory(void) {
55 	gchar *path;
56 	struct stat s;
57 
58 #ifndef WIN32
59 	path = g_strconcat(getenv("HOME"), "/", ".", PACKAGE, NULL);
60 #else /* WIN32 */
61 	TCHAR home[MAX_PATH];
62 	if (FAILED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, home))) {
63 		return;
64 	}
65 	path = g_strconcat(home, "/", PACKAGE, NULL);
66 #endif /* WIN32 */
67 	if (stat(path, &s) < 0) {
68 		mkdir(path, 0777);
69 		if (stat(path, &s) < 0) {
70 			g_free(path);
71 			return;
72 		}
73 	}
74 
75 	global->path_home = path;
76 
77 	path = g_strconcat(global->path_home, "/themes", NULL);
78 	if (stat(path, &s) < 0) {
79 		mkdir(path, 0777);
80 	}
81 	g_free(path);
82 }
83 
84 
85 /* read config files */
read_config_files()86 void read_config_files()   {
87 
88 	gchar *filename;
89 
90 	/* Default configuration */
91 	config->nickname = g_strdup("anonymous");
92 
93 	config->metaserver_autoconnect = TRUE;
94 	config->metaserver_sendclientversion = TRUE;
95 
96 	config->getgames_host = g_strdup("localhost");
97 	config->getgames_port = 1234;
98 	config->getgames_autoconnect = FALSE;
99 
100 	config->game_playerlist_position = GAME_PLAYERLIST_POS_LEFT;
101 	config->game_token_animation_speed = 300;
102 	config->game_token_transparency = FALSE;
103 
104 	/* read user config file */
105 	if (global->path_home) {
106 		filename = g_strconcat(global->path_home, "/gtkatlantic.conf", NULL);
107 		parse_file_config(filename);
108 		g_free(filename);
109 	}
110 }
111 
112 
113 /* this function made a new connection for metaserver (list & games) */
create_connection_metaserver()114 void create_connection_metaserver()  {
115 	GtkTreeIter iter;
116 	gboolean valid;
117 	gchar *cmd;
118 
119 	/* already connected */
120 	if (global->metaserver_connect) {
121 		return;
122 	}
123 
124 	/* remove all servers */
125 	gtk_list_store_clear(global->server_store);
126 
127 	/* remove all games previously fetched from metaserver */
128 	valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(global->game_store), &iter);
129 	while (valid) {
130 		gint32 source;
131 		GtkTreeIter curiter = iter;
132 
133 		valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(global->game_store), &iter); /* get next iter so we can safely remove the current entry */
134 
135 		gtk_tree_model_get(GTK_TREE_MODEL(global->game_store), &curiter, GAMELIST_COLUMN_SOURCE, &source, -1);
136 		if (source == GAMELIST_SOURCE_METASERVER)  {
137 			gtk_list_store_remove(global->game_store, &curiter);
138 		}
139 	}
140 
141 	GtkWidget *MenuItem = g_object_get_data(G_OBJECT(global->Menu), "connect_to_metaserver");
142 	gtk_widget_set_sensitive(MenuItem, FALSE);
143 	MenuItem = g_object_get_data(G_OBJECT(global->Menu), "public_server_list");
144 	gtk_widget_set_sensitive(MenuItem, TRUE);
145 
146 	if (config->metaserver_sendclientversion) {
147 		cmd = g_strdup("CHECKCLIENT " PACKAGE " " VERSION "\nFOLLOW\n");
148 	} else {
149 		cmd = g_strdup("FOLLOW\n");
150 	}
151 
152 	client_connect(CONNECT_TYPE_METASERVER, METASERVER_HOST, METASERVER_PORT, cmd);
153 }
154 
155 
156 /* this function create a new connection to get games list */
create_connection_get_games(gchar * host,gint32 port)157 void create_connection_get_games(gchar *host, gint32 port)  {
158 
159 	/* remove all games previously fetched from custom server */
160 	if (global->game_store) {
161 		GtkTreeIter iter;
162 		gboolean valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(global->game_store), &iter);
163 		while (valid) {
164 			gint32 source;
165 			GtkTreeIter curiter = iter;
166 
167 			valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(global->game_store), &iter); /* get next iter so we can remove the current entry safely */
168 
169 			gtk_tree_model_get(GTK_TREE_MODEL(global->game_store), &curiter, GAMELIST_COLUMN_SOURCE, &source, -1);
170 			if (source == GAMELIST_SOURCE_CUSTOM)  {
171 				gtk_list_store_remove(global->game_store, &curiter);
172 			}
173 		}
174 	}
175 
176 	client_connect(CONNECT_TYPE_MONOPD_GETGAME, host, port, NULL);
177 }
178 
179 
180 /* insert test in Chat Box */
text_insert_chat(gchar * text,gint32 length)181 void text_insert_chat(gchar* text, gint32 length)
182 {
183 	GtkTextBuffer *textbuff;
184 	GtkTextIter textiter;
185 	GtkTextMark *textmark;
186 
187 	if(global->phase < PHASE_GAMECREATE)
188 		return;
189 
190 	textbuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(currentgame->Chat));
191 	gtk_text_buffer_get_end_iter(textbuff, &textiter);
192 	if (gtk_text_iter_get_offset(&textiter)) {
193 		gtk_text_buffer_insert(textbuff, &textiter, "\n", -1);
194 	}
195 	gtk_text_buffer_insert(textbuff, &textiter, text, length);
196 
197 	/* Scroll to the end mark */
198 	textmark = gtk_text_buffer_get_mark(textbuff, "endmark");
199 	gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(currentgame->Chat), textmark,
200 			0.0, FALSE, 0.0, 0.0);
201 }
202 
203 
game_new(gint32 id)204 game *game_new(gint32 id) {
205 	game *game;
206 
207 	game = g_malloc0(sizeof(*game));
208 	game->gameid = id;
209 	printf("New game: %d\n", game->gameid);
210 
211 	game->next = game_h;
212 	game_h = game;
213 	return game;
214 }
215 
216 
game_free(gint32 id)217 void game_free(gint32 id) {
218 	game **prevp, *g;
219 
220 	for (prevp = &game_h; (g = *prevp); prevp = &g->next) {
221 		if (g->gameid == id) {
222 			*prevp = g->next;
223 			printf("Deleted game: %d\n", g->gameid);
224 			g_free(g);
225 			break;
226 		}
227 	}
228 }
229 
230 
game_find(gint32 id)231 game *game_find(gint32 id) {
232 	game *g;
233 
234 	for (g = game_h; g; g = g->next) {
235 		if (g->gameid == id) {
236 			return g;
237 		}
238 	}
239 	return NULL;
240 }
241 
242 
243 /* free all game ressources, return to get games page */
game_quit()244 void game_quit()  {
245 	gint32 i;
246 	game *g;
247 
248 	client_disconnect(global->game_connect);
249 
250 	currentgame->ChatBox = NULL;
251 	currentgame->Chat = NULL;
252 
253 	if(currentgame->WinEstateTree)  gtk_widget_destroy(currentgame->WinEstateTree);
254 
255 	if(currentgame->timeout_token)  g_source_remove(currentgame->timeout_token);
256 
257 	game_delete_cookie();
258 
259 	/* remove all players */
260 	while (player_h) {
261 		game_free_player(player_h);
262 	}
263 
264 	for(i = 0 ; i < data->number_estates ; i++)  {
265 
266 		if(currentgame->estate[i].name) g_free(currentgame->estate[i].name);
267 	}
268 
269 	for(i = 0 ; i < MAX_GROUPS ; i++)  {
270 
271 		if(currentgame->group[i].name) g_free(currentgame->group[i].name);
272 	}
273 
274 	for(i = 0 ; i < MAX_TRADES ; i++)  {
275 
276 		trade_destroy_slot(i);
277 	}
278 
279 	for(i = 0 ; i < MAX_COMMANDS ; i++)  {
280 
281 		if(currentgame->command[i].open  &&  currentgame->command[i].command)
282 			g_free(currentgame->command[i].command);
283 	}
284 
285 	for(i = 0 ; i < MAX_CARDS ; i++)  {
286 
287 		if (currentgame->card[i].title) {
288 			g_free(currentgame->card[i].title);
289 		}
290 	}
291 
292 	eng_frame_destroy( currentgame->board_frame );
293 
294 	game_free_pngs();
295 
296 	/* remove all games */
297 	g = game_h;
298 	while (g) {
299 		game *cur = g;
300 		g = g->next;
301 		printf("Deleted game: %d\n", cur->gameid);
302 		g_free(cur);
303 	}
304 	game_h = NULL;
305 
306 	currentgame = NULL;
307 
308 	interface_create_getgamespage(true);
309 }
310 
311 
312 /* create a new player slot */
game_new_player(guint32 id)313 player *game_new_player(guint32 id) {
314 	player *player;
315 
316 	player = g_malloc0(sizeof(*player));
317 	player->playerid = id;
318 	player->tokenid = -1;
319 	printf("New player: %d\n", player->playerid);
320 
321 	player->next = player_h;
322 	player_h = player;
323 	return player;
324 }
325 
326 
player_from_id(gint32 playerid)327 player *player_from_id(gint32 playerid) {
328 	player *p;
329 
330 	for (p = player_h; p; p = p->next) {
331 		if ((gint32)p->playerid == playerid) {
332 			return p;
333 		}
334 	}
335 	return NULL;
336 }
337 
338 
player_from_name(gchar * name)339 player *player_from_name(gchar *name)  {
340 	player *p;
341 
342 	if (!name) return NULL;
343 	if (strlen(name) <= 0) return NULL;
344 
345 	for (p = player_h; p; p = p->next) {
346 		if (!p->name) continue;
347 
348 		if (!strcmp(p->name, name)) {
349 			return p;
350 		}
351 	}
352 	return NULL;
353 }
354 
355 
get_playerlistcard_id_with_estate(gint32 estate)356 gint32 get_playerlistcard_id_with_estate(gint32 estate)  {
357 
358 	gint32 i;
359 
360 	for(i = 0 ; i < data->number_playerlist_card ; i++)  {
361 
362 		if(data->playerlist_card[i].estateid == estate) {
363 
364 			return(i);
365 			break;
366 		}
367 	}
368 
369 	return(-1);
370 }
371 
372 
373 /* return a valid command slotid */
game_get_valid_command_slot(gint32 * commandslot)374 gboolean game_get_valid_command_slot(gint32 *commandslot)  {
375 
376 	gint32 i;
377 
378 	for(i = 0; i < MAX_COMMANDS ; i++)
379 		if(!currentgame->command[i].open) {
380 
381 			*commandslot = i;
382 			return TRUE;
383 		}
384 
385 	return FALSE;
386 }
387 
388 
389 /* return commandslot if button command is displayed
390    return <0 if button not is not displayed           */
get_command_button_slot_with_command(gchar * command)391 gint32 get_command_button_slot_with_command(gchar *command) {
392 
393 	gint32 i;
394 
395 	for(i = 0 ; i < MAX_COMMANDS ; i ++)  {
396 
397 		if(currentgame->command[i].open)  {
398 
399 			if(! strncmp(command, currentgame->command[i].command, strlen(command)) )
400 				return i;
401 		}
402 	}
403 
404 	return -1;
405 }
406 
407 
408 /* send specific chat message, like version */
parse_specific_chat_message(gchar * message)409 void parse_specific_chat_message(gchar *message)   {
410 
411 	gchar *msg, *sendstr;
412 	player *my_player;
413 
414 	struct timeval tv;
415 	time_t t_now;
416 
417 	my_player = player_from_id(global->my_playerid);
418 
419 	if(message[0] != '!') return;
420 
421 	msg = message;
422 	msg++;
423 
424 	if(! strncmp(msg, "version", 7))  {
425 
426 		msg += 7;
427 		while( strlen(msg) )  {
428 
429 			if(msg[0] != ' ') break;
430 			else  msg++;
431 		}
432 
433 		if (strlen(msg) == 0 || !strcmp(msg, my_player->name)) {
434 
435 			sendstr = g_strdup_printf("GtkAtlantic %s\n", VERSION);
436 			client_send(global->game_connect, sendstr);
437 			g_free(sendstr);
438 		}
439 	}
440 
441 
442 	else if(! strncmp(msg, "date", 4))  {
443 
444 		msg += 4;
445 		while( strlen(msg) )  {
446 
447 			if(msg[0] != ' ') break;
448 			else  msg++;
449 		}
450 
451 		if (strlen(msg) == 0 || !strcmp(msg, my_player->name)) {
452 
453 			gettimeofday(&tv, NULL);
454 			t_now = tv.tv_sec;
455 			sendstr = g_strdup_printf("%s", ctime(&t_now) );
456 			client_send(global->game_connect, sendstr);
457 			g_free(sendstr);
458 		}
459 	}
460 
461 	else if(! strncmp(msg, "ping", 4))  {
462 
463 		msg += 4;
464 		while( strlen(msg) )  {
465 
466 			if(msg[0] != ' ') break;
467 			else  msg++;
468 		}
469 
470 		if (strlen(msg) == 0 || !strcmp(msg, my_player->name)) {
471 
472 			sendstr = g_strdup_printf("pong\n");
473 			client_send(global->game_connect, sendstr);
474 			g_free(sendstr);
475 		}
476 	}
477 }
478 
479 
480 /* parse specific send message */
parse_specific_send_message(gchar * message)481 void parse_specific_send_message(gchar *message)   {
482 
483 	gchar *msg, *tmp;
484 	gint32 i;
485 
486 	struct timeval tv;
487 	struct tm *local_tm;
488 	time_t t_now;
489 
490 	if(message[0] != '/') return;
491 
492 	msg = message;
493 	msg++;
494 
495 	if(! strncmp(msg, "assets", 6))  {
496 
497 		msg += 6;
498 		while( strlen(msg) )  {
499 
500 			if(msg[0] != ' ') break;
501 			else  msg++;
502 		}
503 
504 		if(strlen(msg) == 0)  {
505 			player *p;
506 			for (p = player_h; p; p = p->next) {
507 				if (p->spectator) continue;
508 				if (p->game != currentgame->gameid) continue;
509 
510 				tmp = g_strdup_printf("<%s> %d", p->name, game_get_assets_player(p->playerid) );
511 				text_insert_chat(tmp, strlen(tmp) );
512 				g_free(tmp);
513 			}
514 		}
515 
516 		player *p;
517 		if ((p = player_from_name(msg))) {
518 			tmp = g_strdup_printf("<%s> %d", p->name, game_get_assets_player(p->playerid) );
519 			text_insert_chat(tmp, strlen(tmp));
520 			g_free(tmp);
521 		}
522 	}
523 
524 
525 	else if(! strncmp(msg, "nick", 4))  {
526 
527 		msg += 4;
528 		while( strlen(msg) )  {
529 
530 			if(msg[0] != ' ') break;
531 			else  msg++;
532 		}
533 
534 		if (strlen(msg) != 0 && strcmp(player_from_id(global->my_playerid)->name, msg)) {
535 
536 			tmp = g_strdup_printf(".n%s\n", msg);
537 			client_send(global->game_connect, tmp);
538 			g_free(tmp);
539 		}
540 	}
541 
542 
543 	else if(! strncmp(msg, "date", 4))  {
544 		gint32 len;
545 
546 		gettimeofday(&tv, NULL);
547 		t_now = tv.tv_sec;
548 
549 		tmp = g_strdup_printf("%s", ctime(&t_now) );
550 		len = strlen(tmp);
551 
552 		for(i = 0 ; i < len ; i++)  {
553 
554 			if(tmp[i] == '\n')  {
555 				tmp[i] = '\0';
556 				break;
557 			}
558 		}
559 		text_insert_chat(tmp, strlen(tmp) );
560 		g_free(tmp);
561 	}
562 
563 
564 	else if(! strncmp(msg, "elapsed", 7))  {
565 
566 		if(currentgame->status >= GAME_STATUS_RUN)  {
567 
568 			gettimeofday(&tv, NULL);
569 			t_now = tv.tv_sec - currentgame->start_time;
570 		}
571 		else  t_now = 0;
572 
573 		local_tm = gmtime(&t_now);
574 
575 		tmp = g_strdup_printf("%.2d:%.2d:%.2d", local_tm->tm_hour, local_tm->tm_min, local_tm->tm_sec);
576 		text_insert_chat(tmp, strlen(tmp) );
577 		g_free(tmp);
578 	}
579 
580 	else if(! strncmp(msg, "me", 2))  {
581 
582 		msg += 2;
583 		while( strlen(msg) )  {
584 
585 			if(msg[0] != ' ') break;
586 			else  msg++;
587 		}
588 
589 		if(strlen(msg) != 0)  {
590 
591 			tmp = g_strdup_printf("[ACTION] %s\n", msg);
592 			client_send(global->game_connect, tmp);
593 			g_free(tmp);
594 		}
595 	}
596 }
597 
598 
599 /* sort playerlist by playerid (ascending) */
game_sort_playerlist_by_playerid(void)600 void game_sort_playerlist_by_playerid(void)  {
601 	player *sorted_h = NULL;
602 	while (player_h) {
603 		player **pp, *p, **spp, *sp;
604 		for (pp = &player_h, sp = *pp, spp = pp; (p = *pp); pp = &p->next) {
605 			if (sp->playerid < p->playerid) {
606 				sp = p;
607 				spp = pp;
608 			}
609 		}
610 		*spp = sp->next; /* remove from list */
611 		sp->next = sorted_h;
612 		sorted_h = sp;
613 	}
614 	player_h = sorted_h;
615 }
616 
617 
618 /* sort playerlist by turnorder (ascending) */
game_sort_playerlist_by_turnorder(void)619 void game_sort_playerlist_by_turnorder(void)  {
620 	player *sorted_h = NULL;
621 	while (player_h) {
622 		player **pp, *p, **spp, *sp;
623 		for (pp = &player_h, sp = *pp, spp = pp; (p = *pp); pp = &p->next) {
624 			if (sp->turnorder < p->turnorder) {
625 				sp = p;
626 				spp = pp;
627 			}
628 		}
629 		*spp = sp->next; /* remove from list */
630 		sp->next = sorted_h;
631 		sorted_h = sp;
632 	}
633 	player_h = sorted_h;
634 }
635 
636 
game_switch_status()637 void game_switch_status() {
638 	guint32 i;
639 
640 	client_disconnect(global->customserver_connect);
641 	client_disconnect(global->metaserver_connect);
642 
643 	switch (currentgame->status) {
644 		case GAME_STATUS_CONFIG:
645 			interface_create_gameconfigpage();
646 			break;
647 
648 		case GAME_STATUS_INIT:
649 			interface_create_gameinitpage();
650 			if (game_load_pngs()) exit(-1);
651 			break;
652 
653 		case GAME_STATUS_RUN:
654 			if (game_load_pngs()) exit(-1);
655 			interface_create_gameboardpage();
656 			game_write_cookie();
657 			break;
658 
659 		case GAME_STATUS_END:
660 			game_delete_cookie();
661 			if (currentgame->timeout_token) {
662 				g_source_remove(currentgame->timeout_token);
663 				currentgame->timeout_token = 0;
664 			}
665 			/* destroy all open trades */
666 			for(i = 0 ; i < MAX_TRADES ; i++) {
667 				trade_destroy_slot(i);
668 			}
669 			break;
670 
671 		default:
672 			return;
673 	}
674 }
675 
676 
677 /* write cookie on disk */
game_write_cookie()678 void game_write_cookie()  {
679 
680 	gchar *filename, *errormsg;
681 	FILE *file;
682 
683 	if (!global->path_home) {
684 		return;
685 	}
686 
687 	filename = g_strconcat(global->path_home, "/cookie", NULL);
688 
689 	file = fopen(filename, "wt");
690 	if (!file) {
691 		errormsg = g_strdup_printf("Can't write cookie file at \"%s\"", filename);
692 		interface_set_infolabel(errormsg, "b00000", false);
693 		g_free(errormsg);
694 		g_free(filename);
695 		return;
696 	}
697 
698 	fprintf(file, "%s\n%d\n%s\n",
699 		global->game_connect->host,
700 		global->game_connect->port,
701 		global->cookie);
702 
703 	g_free(filename);
704 	fclose(file);
705 }
706 
707 
game_delete_cookie(void)708 void game_delete_cookie(void) {
709 	if (!global->path_home) {
710 		return;
711 	}
712 
713 	gchar *filename = g_strconcat(global->path_home, "/cookie", NULL);
714 	unlink(filename);
715 	g_free(filename);
716 }
717 
718 
game_cookie_available(void)719 gboolean game_cookie_available(void) {
720 	struct stat st;
721 	int r;
722 
723 	if (!global->path_home) {
724 		return false;
725 	}
726 
727 	gchar *filename = g_strconcat(global->path_home, "/cookie", NULL);
728 	r = stat(filename, &st);
729 	g_free(filename);
730 
731 	return (r == 0);
732 }
733 
734 
735 /* return a valid card slotid */
game_get_valid_card_slot(gint32 * cardslot)736 gboolean game_get_valid_card_slot(gint32 *cardslot)  {
737 
738 	gint32 i;
739 
740 	for(i = 0; i < MAX_CARDS ; i++)
741 		if(!currentgame->card[i].owner) {
742 
743 			*cardslot = i;
744 			return TRUE;
745 		}
746 
747 	return FALSE;
748 }
749 
750 
751 /* return the card slot by cardid */
get_card_slot_with_cardid(gint32 cardid)752 gint32 get_card_slot_with_cardid(gint32 cardid)  {
753 
754 	gint32 i;
755 
756 	for(i = 0 ; i < MAX_CARDS ; i++)  {
757 
758 		if(currentgame->card[i].cardid == cardid) {
759 
760 			return(i);
761 			break;
762 		}
763 	}
764 
765 	return(-1);
766 }
767 
768 
game_nb_players()769 gint32 game_nb_players() {
770 	gint32 c = 0;
771 	player *p;
772 
773 	for (p = player_h; p; p = p->next) {
774 		if (p->spectator) continue;
775 		if (p->game != currentgame->gameid) continue;
776 		c++;
777 	}
778 	return c;
779 }
780 
781 /* return a valid trade slotid */
game_get_valid_trade_slot(gint32 * tradeslot)782 gboolean game_get_valid_trade_slot(gint32 *tradeslot)  {
783 
784 	gint32 i;
785 
786 	for(i = 0; i < MAX_TRADES ; i++)
787 		if(! currentgame->trade[i].open) {
788 
789 			*tradeslot = i;
790 			return TRUE;
791 		}
792 
793 	return FALSE;
794 }
795 
796 
797 /* return trade slot by tradeid */
get_trade_slot_with_tradeid(gint32 tradeid)798 gint32 get_trade_slot_with_tradeid(gint32 tradeid)  {
799 
800 	gint32 i;
801 
802 	for(i = 0 ; i < MAX_TRADES ; i++)  {
803 
804 		if(! currentgame->trade[i].open) continue;
805 
806 		if(currentgame->trade[i].tradeid == tradeid) {
807 
808 			return(i);
809 			break;
810 		}
811 	}
812 
813 	return(-1);
814 }
815 
816 
817 
818 /* return estate identifier by name of this estate */
get_estateid_by_estatename(gchar * name)819 gint32 get_estateid_by_estatename(gchar *name)  {
820 
821 	gint32 i;
822 
823 	for(i = 0 ; i < data->number_estates ; i++)  {
824 
825 		if(! strcmp(currentgame->estate[i].name, name) )  {
826 
827 			return(i);
828 			break;
829 		}
830 	}
831 
832 	return(-1);
833 }
834 
835 
836 /* build player list in game config phase */
game_buildplayerlist()837 void game_buildplayerlist()   {
838 
839 	if (!currentgame) {
840 		return;
841 	}
842 
843 	if (global->my_playerid == currentgame->master) {
844 		GtkWidget *MenuItem = g_object_get_data(G_OBJECT(global->Menu), "upgrade_spectator");
845 		gtk_widget_destroy(gtk_menu_item_get_submenu(GTK_MENU_ITEM(MenuItem)));
846 		GtkWidget *SubMenu = gtk_menu_new();
847 		gtk_menu_item_set_submenu(GTK_MENU_ITEM(MenuItem), SubMenu);
848 
849 		gtk_widget_set_sensitive(MenuItem, FALSE);
850 		player *p;
851 		for (p = player_h; p; p = p->next) {
852 			if (!p->spectator) continue;
853 			if (p->game != currentgame->gameid) continue;
854 
855 			GtkWidget *item = gtk_menu_item_new_with_label(p->name);
856 			g_object_set_data(G_OBJECT(item), "player", p);
857 			g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(CallBack_menu_AddPlayer), NULL);
858 			gtk_menu_shell_append(GTK_MENU_SHELL(SubMenu), item);
859 			gtk_widget_set_sensitive(MenuItem, TRUE);
860 		}
861 		gtk_widget_show_all(SubMenu);
862 	}
863 
864 	/* build player list */
865 	if (global->phase == PHASE_GAMECREATE) {
866 		GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(currentgame->PlayerList)));
867 		GtkTreeIter iter;
868 		player *p;
869 
870 		gtk_list_store_clear(store);
871 		game_sort_playerlist_by_playerid();
872 
873 		for (p = player_h; p; p = p->next) {
874 			if (p->spectator) continue;
875 			if (p->game != currentgame->gameid)  continue;
876 
877 			gtk_list_store_append(store, &iter);
878 			gtk_list_store_set(store, &iter,
879 				PLAYERLIST_COLUMN_NAME, p->name,
880 				PLAYERLIST_COLUMN_HOST, p->host,
881 				-1);
882 		}
883 	}
884 }
885 
886 
887 /* return assets of a player,
888  *   assets is: money + sale price of houses + unmortgaged value of estates
889  */
game_get_assets_player(gint32 playerid)890 gint32 game_get_assets_player(gint32 playerid)  {
891 	gint32 i, assets;
892 
893 	player *p = player_from_id(playerid);
894 	if (p == NULL)  return 0;
895 
896 	/* money */
897 	assets = p->money;
898 
899 	/* estates + houses */
900 	for (i = 0; i < data->number_estates; i++)  {
901 
902 		if (currentgame->estate[i].owner != playerid) continue;
903 
904 		/* estates */
905 		if (!currentgame->estate[i].mortgaged)
906 			assets += currentgame->estate[i].mortgageprice;
907 
908 		/* houses */
909 		if (currentgame->estate[i].houses <= 0) continue;
910 
911 		assets += currentgame->estate[i].houses * currentgame->estate[i].sellhouseprice;
912 	}
913 
914 	return assets;
915 }
916 
917 
918 /* update token position, handle superposed token */
game_update_tokens()919 void game_update_tokens()  {
920 
921 	gint32 i, nb_tokens, x1, y1, x2, y2, xs, ys, xp, yp, k;
922 	player *p, *pp;
923 
924 
925 	/* === UNJAILED === */
926 
927 	for (i = 0; i < data->number_estates; i++) {
928 
929 		/* search number of unjailed tokens on this estate */
930 		for (nb_tokens = 0, p = player_h; p; p = p->next) {
931 			if (p->spectator) continue;
932 			if (p->game != currentgame->gameid) continue;
933 			if (p->bankrupt) continue;
934 			if (p->jailed) continue;
935 			if (p->location != i) continue;
936 
937 			nb_tokens++;
938 		}
939 
940 		/* move token */
941 		if (nb_tokens == 0) continue;
942 
943 		for (k = 0, xp = -1, yp = -1, pp = NULL, p = player_h; p; p = p->next) {
944 			if (p->spectator) continue;
945 			if (p->game != currentgame->gameid) continue;
946 			if (p->bankrupt) continue;
947 			if (p->jailed) continue;
948 			if (p->location != i) continue;
949 
950 			x1 = data->estate[p->location].x1token;
951 			x2 = data->estate[p->location].x2token;
952 			y1 = data->estate[p->location].y1token;
953 			y2 = data->estate[p->location].y2token;
954 
955 			if(nb_tokens == 1)  {
956 
957 				eng_pic_set_x(p->token_pic, x1);
958 				eng_pic_set_y(p->token_pic, y1);
959 
960 				break;
961 			}
962 
963 			xs = ( ( (x2 - x1) * k) / (nb_tokens -1) ) + x1;
964 			if(pp  &&  abs(xs - xp) > (data->pngfile_token_width[pp->tokenid] + 2) ) {
965 
966 				if(xs - xp > 0)
967 					xs = xp + data->pngfile_token_width[pp->tokenid] + 2;
968 				else
969 					xs = xp - data->pngfile_token_width[pp->tokenid] - 2;
970 			}
971 
972 			ys = ( ( (y2 - y1) * k) / (nb_tokens -1) ) + y1;
973 			if(pp  &&  abs(ys - yp) > (data->pngfile_token_height[pp->tokenid] + 2) ) {
974 
975 				if(ys - yp > 0)
976 					ys = yp + data->pngfile_token_height[pp->tokenid] + 2;
977 				else
978 					ys = yp - data->pngfile_token_height[pp->tokenid] - 2;
979 			}
980 
981 			eng_pic_set_x(p->token_pic, xs);
982 			eng_pic_set_y(p->token_pic, ys);
983 
984 			xp = xs;
985 			yp = ys;
986 			pp = p;
987 
988 			k++;
989 		}
990 	}
991 
992 
993 	/* === JAILED === */
994 
995 	for (i = 0; i < data->number_estates; i++) {
996 
997 		/* search number of jailed tokens on this estate */
998 		for (nb_tokens = 0, p = player_h; p; p = p->next) {
999 			if (p->spectator) continue;
1000 			if (p->game != currentgame->gameid) continue;
1001 			if (p->bankrupt) continue;
1002 			if (!p->jailed) continue;
1003 			if (p->location != i) continue;
1004 
1005 			nb_tokens++;
1006 		}
1007 
1008 		/* move token */
1009 		if (nb_tokens == 0) continue;
1010 
1011 		for (k = 0, xp = -1, yp = -1, pp = NULL, p = player_h; p; p = p->next) {
1012 			if (p->spectator) continue;
1013 			if (p->game != currentgame->gameid) continue;
1014 			if (p->bankrupt) continue;
1015 			if (!p->jailed) continue;
1016 			if (p->location != i) continue;
1017 
1018 			x1 = data->estate[p->location].x1jail;
1019 			x2 = data->estate[p->location].x2jail;
1020 			y1 = data->estate[p->location].y1jail;
1021 			y2 = data->estate[p->location].y2jail;
1022 
1023 			if(nb_tokens == 1)  {
1024 
1025 				eng_pic_set_x(p->token_pic, x1);
1026 				eng_pic_set_y(p->token_pic, y1);
1027 
1028 				break;
1029 			}
1030 
1031 			xs = ( ( (x2 - x1) * k) / (nb_tokens -1) ) + x1;
1032 			if(pp  &&  abs(xs - xp) > (data->pngfile_token_width[pp->tokenid] + 2) ) {
1033 
1034 				if(xs - xp > 0)
1035 					xs = xp + data->pngfile_token_width[pp->tokenid] + 2;
1036 				else
1037 					xs = xp - data->pngfile_token_width[pp->tokenid] - 2;
1038 			}
1039 
1040 			ys = ( ( (y2 - y1) * k) / (nb_tokens -1) ) + y1;
1041 			if(pp  &&  abs(ys - yp) > (data->pngfile_token_height[pp->tokenid] + 2) ) {
1042 
1043 				if(ys - yp > 0)
1044 					ys = yp + data->pngfile_token_height[pp->tokenid] + 2;
1045 				else
1046 					ys = yp - data->pngfile_token_height[pp->tokenid] - 2;
1047 			}
1048 
1049 			eng_pic_set_x(p->token_pic, xs);
1050 			eng_pic_set_y(p->token_pic, ys);
1051 
1052 			xp = xs;
1053 			yp = ys;
1054 			pp = p;
1055 
1056 			k++;
1057 		}
1058 	}
1059 
1060 	update_display();
1061 }
1062 
1063 
1064 /* initiate token movement */
game_initiate_token_movement()1065 void game_initiate_token_movement() {
1066 
1067 	if (global->phase != PHASE_GAMEPLAY) {
1068 		return;
1069 	}
1070 	if (currentgame->status != GAME_STATUS_RUN) {
1071 		return;
1072 	}
1073 
1074 	/* do nothing if token timer is running */
1075 	if (currentgame->timeout_token) {
1076 		return;
1077 	}
1078 
1079 	game_move_tokens(NULL);
1080 }
1081 
1082 
1083 /* animate token movement */
game_move_tokens(gpointer ptr)1084 gboolean game_move_tokens(gpointer ptr)  {
1085 	gchar *sendstr;
1086 	gboolean update = FALSE, more = FALSE;
1087 	player *p;
1088 	(void)ptr;
1089 
1090 	for (p = player_h; p; p = p->next) {
1091 		if (p->spectator) continue;
1092 		if (p->game != currentgame->gameid) continue;
1093 
1094 		if (p->directmove) {
1095 			p->location = p->location_to;
1096 			p->directmove = 0;
1097 		} else if (p->location != p->location_to) {
1098 			p->location++;
1099 			if (p->location >= data->number_estates) {
1100 				p->location = 0;
1101 			}
1102 			if (p->location != p->location_to) {
1103 				more = TRUE;
1104 			}
1105 		} else {
1106 			continue;
1107 		}
1108 
1109 		sendstr = g_strdup_printf(".t%d\n", p->location);
1110 		client_send(global->game_connect, sendstr);
1111 		g_free(sendstr);
1112 
1113 		update = TRUE;
1114 	}
1115 
1116 	if (update) {
1117 		game_update_tokens();
1118 
1119 		if (more) {
1120 			if (currentgame->timeout_token == 0) {
1121 				currentgame->timeout_token = g_timeout_add(config->game_token_animation_speed, game_move_tokens, NULL);
1122 			}
1123 			return TRUE;
1124 		}
1125 	}
1126 
1127 	currentgame->timeout_token = 0;
1128 	return FALSE;
1129 }
1130 
1131 
game_free_player(player * p)1132 void game_free_player(player *p)  {
1133 	player **prevp, *pl;
1134 
1135 	for (prevp = &player_h; (pl = *prevp); prevp = &pl->next) {
1136 		if (pl != p) {
1137 			continue;
1138 		}
1139 
1140 		*prevp = p->next;
1141 
1142 		/* delete a player */
1143 		eng_frame_destroy(p->playerlist_token_frame);
1144 		eng_frame_destroy(p->playerlist_cards_frame);
1145 
1146 		if (p->name) g_free(p->name);
1147 		if (p->host) g_free(p->host);
1148 		if (p->image) g_free(p->image);
1149 
1150 		printf("Deleted player: %d\n", p->playerid);
1151 		g_free(p);
1152 		return;
1153 	}
1154 }
1155 
game_exit(void)1156 void game_exit(void) {
1157 
1158 	if (global->phase >= PHASE_GAMECREATE)  {
1159 		client_send(global->game_connect, ".gx\n");
1160 	}
1161 
1162 	game_delete_cookie();
1163 	game_free_pngs();
1164 }
1165