+/* game_uno.c - NeonServ v5.4
+ * Copyright (C) 2011-2012 Philipp Kreil (pk910)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "../module.h"
+#include "game_uno.h"
+#include "bot_NeonFun.h"
+#include "../../IRCParser.h"
+#include "../../bots.h"
+#include "../../UserNode.h"
+#include "../../ChanUser.h"
+#include "../../tools.h"
+#include "../botid.h"
+
+struct uno_game *uno_active_games = NULL;
+
+#define UNO_COLOR_COUNT 4
+static int uno_colors[] = {UNO_COLOR_RED, UNO_COLOR_BLUE, UNO_COLOR_GREEN, UNO_COLOR_YELLOW};
+static int uno_irc_colors[] = {4, 12, 3, 7};
+static char *uno_color_chars[] = {"R", "B", "G", "Y"};
+
+static const struct {
+ const char *name;
+ unsigned char type;
+} uno_card_types[] = {
+ {"0", UNO_CARD_NUMBER_0},
+ {"1", UNO_CARD_NUMBER_1},
+ {"2", UNO_CARD_NUMBER_2},
+ {"3", UNO_CARD_NUMBER_3},
+ {"4", UNO_CARD_NUMBER_4},
+ {"5", UNO_CARD_NUMBER_5},
+ {"6", UNO_CARD_NUMBER_6},
+ {"7", UNO_CARD_NUMBER_7},
+ {"8", UNO_CARD_NUMBER_8},
+ {"9", UNO_CARD_NUMBER_9},
+ {"X", UNO_CARD_SKIP},
+ {"><", UNO_CARD_DIRECTION},
+ {"+2", UNO_CARD_ADD_2},
+ {"+4", UNO_CARD_ADD_4},
+ {"COLOR", UNO_CARD_COLOR},
+ {NULL, 0}
+};
+
+struct uno_card_deck *uno_shuffle_deck() {
+ struct uno_card *card, *last_card = NULL;
+ int card_count = 0;
+ #define ADD_CARD(ccolor,ctype) \
+ card = malloc(sizeof(*card)); \
+ card->color = ccolor; \
+ card->card = ctype; \
+ card->prev = NULL; \
+ card->next = last_card; \
+ if(last_card) \
+ last_card->prev = card; \
+ last_card = card; \
+ card_count++;
+ int colorcount = UNO_COLOR_COUNT;
+ for(colorcount--; colorcount >= 0; colorcount--) {
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_0);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_0);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_1);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_1);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_2);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_2);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_3);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_3);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_4);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_4);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_5);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_5);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_6);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_6);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_7);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_7);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_8);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_8);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_9);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_9);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_SKIP);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_SKIP);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_DIRECTION);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_DIRECTION);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_ADD_2);
+ ADD_CARD(uno_colors[colorcount], UNO_CARD_ADD_2);
+ }
+ ADD_CARD(UNO_COLOR_BLACK, UNO_CARD_ADD_4);
+ ADD_CARD(UNO_COLOR_BLACK, UNO_CARD_ADD_4);
+ ADD_CARD(UNO_COLOR_BLACK, UNO_CARD_ADD_4);
+ ADD_CARD(UNO_COLOR_BLACK, UNO_CARD_ADD_4);
+ ADD_CARD(UNO_COLOR_BLACK, UNO_CARD_COLOR);
+ ADD_CARD(UNO_COLOR_BLACK, UNO_CARD_COLOR);
+ ADD_CARD(UNO_COLOR_BLACK, UNO_CARD_COLOR);
+ ADD_CARD(UNO_COLOR_BLACK, UNO_CARD_COLOR);
+ #undef ADD_CARD
+ struct uno_card_deck *deck = malloc(sizeof(*deck));
+ deck->cards = last_card;
+ deck->count = card_count;
+ return deck;
+}
+
+struct uno_card *uno_get_card(struct uno_card_deck *deck) {
+ if(!deck->count) return NULL;
+ int card_id = (rand() % deck->count);
+ int i = 0;
+ struct uno_card *card;
+ for(card = deck->cards; card; card = card->next) {
+ if(i == card_id) {
+ if(card->prev)
+ card->prev->next = card->next;
+ else
+ deck->cards = card->next;
+ if(card->next)
+ card->next->prev = card->prev;
+ deck->count--;
+ card->next = NULL;
+ card->prev = NULL;
+ return card;
+ }
+ i++;
+ }
+ return NULL;
+}
+
+void uno_free_deck(struct uno_card_deck *deck) {
+ struct uno_card *card, *next_card;
+ if(deck->count) {
+ for(card = deck->cards; card; card = next_card) {
+ next_card = card->next;
+ free(card);
+ }
+ }
+}
+
+void uno_free_player(struct uno_player *player, struct uno_card_deck *deck) {
+ if(player->count) {
+ struct uno_card *card, *next_card;
+ for(card = player->cards; card; card = next_card) {
+ next_card = card->next;
+ if(deck) {
+ card->next = deck->cards;
+ deck->cards->prev = card;
+ deck->cards = card;
+ deck->count++;
+ } else
+ free(card);
+ }
+ }
+ if(player->prev)
+ player->prev->next = player->next;
+ if(player->next)
+ player->next->prev = player->prev;
+ free(player);
+}
+
+void uno_free_topcard(struct uno_card *card) {
+ struct uno_card *next_card;
+ for(; card; card = next_card) {
+ next_card = card->prev;
+ free(card);
+ }
+}
+
+void uno_free_game(struct uno_game *game) {
+ struct uno_player *player, *next_player;
+ for(player = game->player; player; player = next_player) {
+ next_player = player->next;
+ uno_free_player(player, NULL);
+ }
+ for(player = game->winner; player; player = next_player) {
+ next_player = player->next;
+ uno_free_player(player, NULL);
+ }
+ if(game->deck)
+ uno_free_deck(game->deck);
+ if(game->top_card)
+ uno_free_topcard(game->top_card);
+ if(game->timer)
+ timeq_del(game->timer);
+ struct uno_game *cgame, *pgame = NULL;
+ for(cgame = uno_active_games; cgame; cgame = cgame->next) {
+ if(cgame == game) {
+ if(pgame)
+ pgame->next = game->next;
+ else
+ uno_active_games = game->next;
+ break;
+ } else
+ pgame = cgame;
+ }
+ free(game);
+}
+
+struct uno_player *uno_get_next_player(struct uno_game *game) {
+ struct uno_player *player = game->active_player;
+ if(!game->reverse_direction) {
+ player = player->next;
+ if(!player)
+ player = game->player;
+ } else {
+ player = player->prev;
+ if(!player) {
+ for(player = game->player; player->next; player = player->next) {
+ //loop to the last player
+ }
+ }
+ }
+ return player;
+}
+
+void uno_show_player_cards(struct uno_game *game, struct uno_player *player) {
+ struct uno_card *card;
+ char cards_buf[MAXLEN];
+ int cards_bufpos = 0;
+ for(card = player->cards; card; card = card->next) {
+ int cardcolor = 1;
+ char *cardchar = "";
+ int i;
+ for(i = 0; i < UNO_COLOR_COUNT; i++) {
+ if(uno_colors[i] == card->color) {
+ cardcolor = uno_irc_colors[i];
+ cardchar = uno_color_chars[i];
+ break;
+ }
+ }
+ i = 0;
+ while(uno_card_types[i].name) {
+ if(uno_card_types[i].type == card->card) {
+ cards_bufpos += sprintf(cards_buf + cards_bufpos, "%s[\003%d%s%s\003]", (cards_bufpos ? " " : ""), cardcolor, cardchar, uno_card_types[i].name);
+ break;
+ }
+ i++;
+ }
+ }
+ reply(game->textbot, player->chanuser->user, "NF_UNO_YOUR_CARDS", cards_buf);
+}
+
+void uno_show_top_card(struct uno_game *game) {
+ struct uno_card *card = game->top_card;
+ char card_buf[50];
+ int cardcolor = 1;
+ char *cardchar = "";
+ int i;
+ for(i = 0; i < UNO_COLOR_COUNT; i++) {
+ if(uno_colors[i] == card->color) {
+ cardcolor = uno_irc_colors[i];
+ cardchar = uno_color_chars[i];
+ break;
+ }
+ }
+ i = 0;
+ while(uno_card_types[i].name) {
+ if(uno_card_types[i].type == card->card) {
+ if(card->card == UNO_CARD_ADD_4 || card->card == UNO_CARD_COLOR)
+ cardchar = "";
+ sprintf(card_buf, "[\003%d%s%s\003]", cardcolor, cardchar, uno_card_types[i].name);
+ break;
+ }
+ i++;
+ }
+ uno_reply(game, NULL, "NF_UNO_TOP_CARD", card_buf);
+}
+
+TIMEQ_CALLBACK(uno_game_wait_timeout) {
+ struct uno_game *game = data;
+ game->timer = NULL;
+ if(game->players == 1) {
+ //TEXT: too less users
+ uno_free_game(game);
+ return;
+ }
+ game->deck = uno_shuffle_deck();
+ uno_reply(game, NULL, "NF_UNO_START");
+ struct uno_player *player;
+ for(player = game->player; player; player = player->next) {
+ //give 7 cards
+ int i;
+ for(i = 0; i < 7; i++) {
+ if(!game->deck->count)
+ game->deck = uno_shuffle_deck();
+ struct uno_card *card = uno_get_card(game->deck);
+ if(!card) {
+ uno_reply(game, NULL, "NF_UNO_ERROR", 1);
+ uno_free_game(game);
+ return;
+ }
+ card->next = player->cards;
+ if(player->cards)
+ player->cards->prev = card;
+ player->cards = card;
+ player->count++;
+ }
+ uno_show_player_cards(game, player);
+ if((player->chanuser->user->flags & USERFLAG_ISAUTHED)) {
+ char *tmp;
+ int game_count = ((tmp = getSetting(player->chanuser->user, game->channel, "uno_games")) ? atoi(tmp) : 0);
+ int total_game_count = ((tmp = getSetting(player->chanuser->user, NULL, "uno_games")) ? atoi(tmp) : 0);
+ game_count++;
+ total_game_count++;
+ char buf[10];
+ sprintf(buf, "%d", game_count);
+ setSetting(player->chanuser->user, game->channel, "uno_games", buf);
+ sprintf(buf, "%d", total_game_count);
+ setSetting(player->chanuser->user, NULL, "uno_games", buf);
+ }
+ }
+ if(!game->deck->count)
+ game->deck = uno_shuffle_deck();
+ struct uno_card *card = uno_get_card(game->deck);
+ if(!card) {
+ uno_reply(game, NULL, "NF_UNO_ERROR", 1);
+ uno_free_game(game);
+ return;
+ }
+ game->top_card = card;
+ game->state = UNO_STATE_RUNNING;
+ uno_show_top_card(game);
+ game->active_player = game->player; //active player
+ uno_reply(game, NULL, "NF_UNO_USER_HURRY_UP", game->active_player->chanuser->user->nick);
+ game->timer = timeq_add(30, module_id, uno_player_timeout, game);
+}
+
+TIMEQ_CALLBACK(uno_player_timeout) {
+ struct uno_game *game = data;
+ game->timer = NULL;
+ //player timeout (take another card)
+ struct uno_player *player = game->active_player, *next_player = uno_get_next_player(game);
+ //add a card to the players deck
+ if(!game->deck->count)
+ game->deck = uno_shuffle_deck();
+ struct uno_card *card = uno_get_card(game->deck);
+ if(!card) {
+ uno_reply(game, NULL, "NF_UNO_ERROR", 2);
+ uno_free_game(game);
+ return;
+ }
+ card->next = player->cards;
+ if(player->cards)
+ player->cards->prev = card;
+ player->cards = card;
+ player->count++;
+ player->timeout = 1;
+ game->active_player = next_player;
+ uno_reply(game, NULL, "NF_UNO_USER_TOOK_CARD", player->chanuser->user->nick);
+ struct uno_player *cplayer;
+ for(cplayer = game->player; cplayer; cplayer = cplayer->next) {
+ if(!cplayer->timeout)
+ break;
+ }
+ if(!cplayer) {
+ uno_reply(game, NULL, "NF_UNO_TIMEOUT");
+ uno_free_game(game);
+ return;
+ }
+ uno_show_player_cards(game, player);
+ uno_show_top_card(game);
+ uno_reply(game, NULL, "NF_UNO_USER_HURRY_UP", game->active_player->chanuser->user->nick);
+ uno_show_player_cards(game, game->active_player);
+ game->timer = timeq_add(30, module_id, uno_player_timeout, game);
+}
+
+void uno_action_take_card(struct uno_game *game, struct uno_player *player) {
+ timeq_del(game->timer);
+ game->timer = NULL;
+ struct uno_player *next_player = uno_get_next_player(game);
+ //add a card to the players deck
+ int ccount;
+ if(game->take_cards_pending) {
+ //count cards to take
+ struct uno_card *card;
+ ccount = 0;
+ for(card = game->top_card; card; card = card->prev) {
+ if(card->card == UNO_CARD_ADD_2)
+ ccount += 2;
+ else if(card->card == UNO_CARD_ADD_4)
+ ccount += 4;
+ }
+ } else
+ ccount = 1;
+ int i;
+ for(i = 0; i < ccount; i++) {
+ if(!game->deck->count)
+ game->deck = uno_shuffle_deck(game->deck);
+ struct uno_card *card = uno_get_card(game->deck);
+ if(!card) {
+ uno_reply(game, NULL, "NF_UNO_ERROR", 2);
+ uno_free_game(game);
+ return;
+ }
+ card->next = player->cards;
+ if(player->cards)
+ player->cards->prev = card;
+ player->cards = card;
+ player->count++;
+ }
+ game->active_player = next_player;
+ if(game->take_cards_pending) {
+ game->take_cards_pending = 0;
+ uno_reply(game, NULL, "NF_UNO_USER_TOOK_CARDS", player->chanuser->user->nick, ccount);
+ } else
+ uno_reply(game, NULL, "NF_UNO_USER_TOOK_CARD", player->chanuser->user->nick);
+ uno_show_player_cards(game, player);
+ uno_show_top_card(game);
+ uno_reply(game, NULL, "NF_UNO_USER_HURRY_UP", game->active_player->chanuser->user->nick);
+ uno_show_player_cards(game, game->active_player);
+ game->timer = timeq_add(30, module_id, uno_player_timeout, game);
+}
+
+
+
+struct uno_card *uno_parse_card(struct uno_game *game, struct uno_player *player, char *arg1) {
+ char *b = arg1;
+ int a = 0;
+ while(*b) {
+ switch (*b) {
+ case '\003':
+ if(isdigit(b[1]))
+ b[1] = '\002';
+ if(isdigit(b[2]))
+ b[2] = '\002';
+ case '[':
+ case '\002':
+ case ']':
+ *b = '\0';
+ break;
+ default:
+ if(!a) {
+ a = 1;
+ arg1 = b;
+ }
+ }
+ b++;
+ }
+ unsigned char ctype = 255, ccolor = 0;
+ int i = 0, j;
+ char tmpbuf[50];
+ while(uno_card_types[i].name) {
+ if(!stricmp(uno_card_types[i].name, arg1)) {
+ ctype = uno_card_types[i].type;
+ break;
+ }
+ for(j = 0; j < UNO_COLOR_COUNT; j++) {
+ sprintf(tmpbuf, "%s%s", uno_color_chars[j], uno_card_types[i].name);
+ if(!stricmp(tmpbuf, arg1)) {
+ ccolor = uno_colors[j];
+ ctype = uno_card_types[i].type;
+ break;
+ }
+ }
+ i++;
+ }
+ if(ctype == 255) {
+ reply(game->textbot, player->chanuser->user, "NF_UNO_UNKNOWN_CARD", arg1);
+ return NULL;
+ }
+ struct uno_card *card;
+ for(card = player->cards; card; card = card->next) {
+ if(card->card == ctype && card->color == ccolor)
+ break;
+ }
+ if(!card) {
+ reply(game->textbot, player->chanuser->user, "NF_UNO_CARD_NOT_IN_DECK");
+ return NULL;
+ }
+ return card;
+}
+
+int uno_check_card_valid(struct uno_game *game, struct uno_card *card) {
+ if(game->take_cards_pending && card->card != game->top_card->card)
+ return 1;
+ if(card->color == UNO_COLOR_BLACK)
+ return 0;
+ if(card->color != game->top_card->color && card->card != game->top_card->card)
+ return 1;
+ return 0;
+}
+
+void uno_play_card(struct uno_game *game, struct uno_player *player, struct uno_card *card) {
+ timeq_del(game->timer);
+ game->timer = NULL;
+ if(card->prev)
+ card->prev->next = card->next;
+ else
+ player->cards = card->next;
+ if(card->next)
+ card->next->prev = card->prev;
+ player->count--;
+ if(!game->take_cards_pending) {
+ uno_free_topcard(game->top_card);
+ card->prev = NULL;
+ } else
+ card->prev = game->top_card;
+ card->next = NULL;
+ game->top_card = card;
+ uno_show_top_card(game);
+ if(player->count == 1) {
+ uno_reply(game, NULL, "NF_UNO_ONE_CARD", game->active_player->chanuser->user->nick);
+ } else if(player->count == 0) {
+ uno_reply(game, NULL, "NF_UNO_USER_WIN", game->active_player->chanuser->user->nick);
+ if(player->prev)
+ player->prev->next = player->next;
+ if(player->next)
+ player->next->prev = player->prev;
+ player->next = NULL;
+ player->prev = NULL;
+ struct uno_player *cplayer;
+ int winner_count = 0;
+ if(game->winner) {
+ for(cplayer = game->winner; cplayer->next; cplayer = cplayer->next) {
+ winner_count++;
+ }
+ } else
+ cplayer = NULL;
+ if(cplayer) {
+ cplayer->next = player;
+ } else {
+ game->winner = player;
+ if((player->chanuser->user->flags & USERFLAG_ISAUTHED)) {
+ char *tmp;
+ int win_count = ((tmp = getSetting(player->chanuser->user, game->channel, "uno_win")) ? atoi(tmp) : 0);
+ int total_win_count = ((tmp = getSetting(player->chanuser->user, NULL, "uno_win")) ? atoi(tmp) : 0);
+ win_count++;
+ total_win_count++;
+ char buf[10];
+ sprintf(buf, "%d", win_count);
+ setSetting(player->chanuser->user, game->channel, "uno_win", buf);
+ sprintf(buf, "%d", total_win_count);
+ setSetting(player->chanuser->user, NULL, "uno_win", buf);
+ }
+ }
+ player->prev = cplayer;
+ game->players--;
+ if(game->players <= 1) {
+ //game finished
+ uno_reply(game, NULL, "NF_UNO_GAME_FINISHED");
+ struct Table *table;
+ table = table_init(4, winner_count + 3, 0);
+ char *content[4];
+ content[0] = get_language_string(NULL, "NF_UNO_RANK");
+ content[1] = get_language_string(NULL, "NF_UNO_NAME");
+ content[2] = get_language_string(NULL, "NF_UNO_WON_GAMES");
+ content[3] = get_language_string(NULL, "NF_UNO_TOTAL_WON_GAMES");
+ table_add(table, content);
+ winner_count = 1;
+ char rank_buf[20], won_buf[50], total_won_buf[50];
+ char *tmp, *tmp2;
+ for(cplayer = game->winner; cplayer->next; cplayer = cplayer->next) {
+ sprintf(rank_buf, "%d", winner_count++);
+ content[0] = rank_buf;
+ content[1] = cplayer->chanuser->user->nick;
+ if((cplayer->chanuser->user->flags & USERFLAG_ISAUTHED)) {
+ sprintf(won_buf, "%d/%d", ((tmp = getSetting(cplayer->chanuser->user, game->channel, "uno_win")) ? atoi(tmp) : 0), ((tmp2 = getSetting(cplayer->chanuser->user, game->channel, "uno_games")) ? atoi(tmp2) : 0));
+ content[2] = won_buf;
+ sprintf(total_won_buf, "%d/%d", ((tmp = getSetting(cplayer->chanuser->user, NULL, "uno_win")) ? atoi(tmp) : 0), ((tmp2 = getSetting(cplayer->chanuser->user, NULL, "uno_games")) ? atoi(tmp2) : 0));
+ content[3] = total_won_buf;
+ } else {
+ content[2] = "?";
+ content[3] = "?";
+ }
+ table_add(table, content);
+ }
+ cplayer = game->player;
+ sprintf(rank_buf, "%d", winner_count++);
+ content[0] = rank_buf;
+ content[1] = cplayer->chanuser->user->nick;
+ if((cplayer->chanuser->user->flags & USERFLAG_ISAUTHED)) {
+ sprintf(won_buf, "%d/%d", ((tmp = getSetting(cplayer->chanuser->user, game->channel, "uno_win")) ? atoi(tmp) : 0), ((tmp2 = getSetting(cplayer->chanuser->user, game->channel, "uno_games")) ? atoi(tmp2) : 0));
+ content[2] = won_buf;
+ sprintf(total_won_buf, "%d/%d", ((tmp = getSetting(cplayer->chanuser->user, NULL, "uno_win")) ? atoi(tmp) : 0), ((tmp2 = getSetting(cplayer->chanuser->user, NULL, "uno_games")) ? atoi(tmp2) : 0));
+ content[3] = total_won_buf;
+ } else {
+ content[2] = "?";
+ content[3] = "?";
+ }
+ table_add(table, content);
+ char **table_lines = table_end(table);
+ int i;
+ for(i = 0; i < table->entrys; i++) {
+ uno_reply(game, NULL, table_lines[i]);
+ }
+ table_free(table);
+ uno_free_game(game);
+ return;
+ }
+ }
+ if(card->card == UNO_CARD_DIRECTION)
+ game->reverse_direction = (game->reverse_direction ? 0 : 1);
+ struct uno_player *next_player = uno_get_next_player(game);
+ game->active_player = next_player;
+ if(card->card == UNO_CARD_SKIP) {
+ uno_reply(game, NULL, "NF_UNO_USER_SKIP", game->active_player->chanuser->user->nick);
+ next_player = uno_get_next_player(game);
+ game->active_player = next_player;
+ }
+ if(card->card == UNO_CARD_ADD_2 || card->card == UNO_CARD_ADD_4) {
+ int ccount = 0;
+ for(card = game->top_card; card; card = card->prev) {
+ if(card->card == UNO_CARD_ADD_2)
+ ccount += 2;
+ else if(card->card == UNO_CARD_ADD_4)
+ ccount += 4;
+ }
+ uno_reply(game, NULL, "NF_UNO_ADD_CARD", game->active_player->chanuser->user->nick, ccount);
+ game->take_cards_pending = 1;
+ }
+ uno_reply(game, NULL, "NF_UNO_USER_HURRY_UP", game->active_player->chanuser->user->nick);
+ uno_show_player_cards(game, game->active_player);
+ game->timer = timeq_add(30, module_id, uno_player_timeout, game);
+}
+
+void uno_event_part(struct ChanUser *chanuser) {
+ struct uno_game *game;
+ for(game = uno_active_games; game; game = game->next) {
+ if(chanuser->chan == game->channel) {
+ struct uno_player *player;
+ for(player = game->player; player; player = player->next) {
+ if(player->chanuser == chanuser) {
+ uno_free_player(player, game->deck);
+ return;
+ }
+ }
+ }
+ }
+}
+
+void uno_event_quit(struct UserNode *user) {
+ struct uno_game *game;
+ for(game = uno_active_games; game; game = game->next) {
+ struct uno_player *player;
+ for(player = game->player; player; player = player->next) {
+ if(player->chanuser->user == user) {
+ uno_free_player(player, game->deck);
+ break;
+ }
+ }
+ }
+}
+
+void uno_event_freechan(struct ChanNode *chan) {
+ struct uno_game *game;
+ for(game = uno_active_games; game; game = game->next) {
+ if(game->channel == chan) {
+ uno_free_game(game);
+ }
+ }
+}