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