added NeonFun Bot with UNO Game
authorpk910 <philipp@zoelle1.de>
Sat, 17 Mar 2012 20:19:07 +0000 (21:19 +0100)
committerpk910 <philipp@zoelle1.de>
Sat, 17 Mar 2012 23:17:06 +0000 (00:17 +0100)
13 files changed:
Makefile.am
src/modules/NeonFun.mod/bot_NeonFun.c [new file with mode: 0644]
src/modules/NeonFun.mod/bot_NeonFun.h [new file with mode: 0644]
src/modules/NeonFun.mod/cmd_neonfun.c [new file with mode: 0644]
src/modules/NeonFun.mod/cmd_neonfun.h [new file with mode: 0644]
src/modules/NeonFun.mod/cmd_neonfun_uno.c [new file with mode: 0644]
src/modules/NeonFun.mod/cmd_neonfun_unoplay.c [new file with mode: 0644]
src/modules/NeonFun.mod/cmd_neonfun_unotake.c [new file with mode: 0644]
src/modules/NeonFun.mod/game_uno.c [new file with mode: 0644]
src/modules/NeonFun.mod/game_uno.h [new file with mode: 0644]
src/modules/NeonFun.mod/module.c [new file with mode: 0644]
src/modules/botid.h
src/modules/global.mod/cmd_global_setbot.c

index d3f96fed532618df49e6a7b72a59b35e3f02d46c..aba1bf4da2bc41e359f66236725b5b803c3677ed 100644 (file)
@@ -7,7 +7,7 @@ checkversion:
        cd src && chmod +x version.sh && ./version.sh && cd ..
 
 noinst_PROGRAMS = neonserv
-noinst_LTLIBRARIES = libDummyServ.la libfuncmds.la libglobalcmd.la libNeonHelp.la libNeonServ.la libNeonSpam.la libstats.la
+noinst_LTLIBRARIES = libDummyServ.la libfuncmds.la libglobalcmd.la libNeonHelp.la libNeonServ.la libNeonSpam.la libstats.la libNeonFun.la
 
 libDummyServ_la_SOURCES = src/modules/DummyServ.mod/bot_DummyServ.c \
       src/modules/DummyServ.mod/module.c
@@ -149,6 +149,16 @@ libstats_la_SOURCES = src/modules/stats.mod/module.c
 libstats_la_LDFLAGS = -module -rpath /nowhere -avoid-version -no-undefined
 libstats_la_LIBADD = $(MYSQL_LIBS)
 
+libNeonFun_la_SOURCES = src/modules/NeonFun.mod/bot_NeonFun.c \
+      src/modules/NeonFun.mod/cmd_neonfun.c \
+      src/modules/NeonFun.mod/cmd_neonfun_uno.c \
+      src/modules/NeonFun.mod/cmd_neonfun_unoplay.c \
+      src/modules/NeonFun.mod/cmd_neonfun_unotake.c \
+      src/modules/NeonFun.mod/game_uno.c \
+      src/modules/NeonFun.mod/module.c
+libNeonFun_la_LDFLAGS = -module -rpath /nowhere -avoid-version -no-undefined
+libNeonFun_la_LIBADD = $(MYSQL_LIBS)
+
 neonserv_SOURCES = src/version.c \
       src/EventLogger.c \
       src/IRCEvents.c \
diff --git a/src/modules/NeonFun.mod/bot_NeonFun.c b/src/modules/NeonFun.mod/bot_NeonFun.c
new file mode 100644 (file)
index 0000000..f203f91
--- /dev/null
@@ -0,0 +1,236 @@
+/* bot_NeonFun.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 "../botid.h"
+
+#include "bot_NeonFun.h"
+#include "../../modcmd.h"
+#include "../../IRCParser.h"
+#include "../../IRCEvents.h"
+#include "../../UserNode.h"
+#include "../../ChanNode.h"
+#include "../../ChanUser.h"
+#include "../../ModeNode.h"
+#include "../../BanNode.h"
+#include "../../ClientSocket.h"
+#include "../../mysqlConn.h"
+#include "../../lang.h"
+#include "../../HandleInfoHandler.h"
+#include "../../WHOHandler.h"
+#include "../../DBHelper.h"
+#include "../../tools.h"
+#include "../../timeq.h"
+#include "../../version.h"
+#include "../../EventLogger.h"
+#include "../../bots.h"
+#include "game_uno.h"
+
+#define BOTID NEONFUN_BOTID
+#define BOTALIAS "NeonFun"
+
+static void neonfun_bot_ready(struct ClientSocket *client) {
+    MYSQL_RES *res;
+    MYSQL_ROW row;
+    
+    printf_mysql_query("SELECT `automodes` FROM `bots` WHERE `id` = '%d'", client->clientid);
+    res = mysql_use();
+    if ((row = mysql_fetch_row(res)) != NULL) {
+        putsock(client, "MODE %s +%s", client->user->nick, row[0]);
+    }
+    
+    printf_mysql_query("SELECT `channel_name`, `channel_key` FROM `bot_channels` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `botid` = '%d' AND `suspended` = '0'", client->clientid);
+    res = mysql_use();
+    
+    while ((row = mysql_fetch_row(res)) != NULL) {
+        putsock(client, "JOIN %s %s", row[0], row[1]);
+    }
+}
+
+static void neonfun_trigger_callback(int clientid, struct ChanNode *chan, char *trigger) {
+    MYSQL_RES *res;
+    MYSQL_ROW row;
+    loadChannelSettings(chan);
+    if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) {
+        strcpy(trigger, "+");
+        return;
+    }
+    printf_mysql_query("SELECT `trigger`, `defaulttrigger` FROM `bot_channels` LEFT JOIN `bots` ON `botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chan->channel_id, BOTID);
+    res = mysql_use();
+    if(!(row = mysql_fetch_row(res))) {
+        strcpy(trigger, "+");
+        return;
+    }
+    if(row[0] && *row[0])
+        strcpy(trigger, row[0]);
+    else
+        strcpy(trigger, ((row[1] && *row[1]) ? row[1] : "~"));
+}
+
+static void start_bots(int type) {
+    struct ClientSocket *client;
+    MYSQL_RES *res, *res2;
+    MYSQL_ROW row;
+    
+    if(type == MODSTATE_STARTSTOP) {
+        printf_mysql_query("SELECT `nick`, `ident`, `realname`, `server`, `port`, `pass`, `textbot`, `id`, `queue`, `ssl`, `bind` FROM `bots` WHERE `botclass` = '%d' AND `active` = '1'", BOTID);
+        res = mysql_use();
+        
+        while ((row = mysql_fetch_row(res)) != NULL) {
+            client = create_socket(row[3], atoi(row[4]), row[10], row[5], row[0], row[1], row[2]);
+            client->flags |= (strcmp(row[6], "0") ? SOCKET_FLAG_PREFERRED : 0);
+            client->flags |= (strcmp(row[8], "0") ? SOCKET_FLAG_USE_QUEUE : 0);
+            client->flags |= (strcmp(row[9], "0") ? SOCKET_FLAG_SSL : 0);
+            client->flags |= SOCKET_FLAG_SILENT;
+            client->botid = BOTID;
+            client->clientid = atoi(row[7]);
+            connect_socket(client);
+        }
+    }
+    
+    printf_mysql_query("SELECT `command`, `function`, `parameters`, `global_access`, `chan_access`, `flags` FROM `bot_binds` WHERE `botclass` = '%d'", BOTID);
+    res2 = mysql_use();
+    while ((row = mysql_fetch_row(res2)) != NULL) {
+        if(bind_cmd_to_command(BOTID, row[0], row[1])) {
+            if(row[2] && strcmp(row[2], "")) {
+                bind_set_parameters(BOTID, row[0], row[2]);
+            }
+            if(row[3]) {
+                bind_set_global_access(BOTID, row[0], atoi(row[3]));
+            }
+            if(row[4]) {
+                bind_set_channel_access(BOTID, row[0], row[4]);
+            }
+            if(strcmp(row[5], "0"))
+                bind_set_bind_flags(BOTID, row[0], atoi(row[5]));
+        }
+    }
+    bind_unbound_required_functions(BOTID);
+}
+
+static void neonfun_parted(struct ChanUser *chanuser, char *reason) {
+    uno_event_part(chanuser);
+}
+
+static void neonfun_quitted(struct UserNode *user, char *reason) {
+    uno_event_quit(user);
+}
+
+static int neonfun_freechan(struct ChanNode *chan) {
+    uno_event_freechan(chan);
+    return 0;
+}
+
+void init_NeonFun(int type) {
+    set_bot_alias(BOTID, BOTALIAS);
+    start_bots(type);
+    
+    if(type == MODSTATE_REBIND) return;
+    
+    //register events
+    bind_bot_ready(neonfun_bot_ready, module_id);
+    bind_part(neonfun_parted, module_id);
+    bind_quit(neonfun_quitted, module_id);
+    bind_freechan(neonfun_freechan, module_id);
+    
+    set_trigger_callback(BOTID, module_id, neonfun_trigger_callback);
+}
+
+void loop_NeonFun() {
+    
+}
+
+void free_NeonFun(int type) {
+    unbind_allcmd(BOTID);
+    if(type == MODSTATE_STARTSTOP) {
+        //disconnect all our bots
+        struct ClientSocket *client;
+        for(client = getBots(0, NULL); client; client = getBots(0, client)) {
+            if(client->botid == BOTID) {
+                unbind_botwise_allcmd(0, client->clientid);
+                close_socket(client);
+                break;
+            }
+        }
+    }
+}
+
+char* getSetting(struct UserNode *user, struct ChanNode *chan, const char *setting) {
+    char *uname = "";
+    int cid = 0;
+    MYSQL_RES *res;
+    MYSQL_ROW row;
+    if(user) {
+        uname = ((user->flags & USERFLAG_ISAUTHED) ? user->auth : "*");
+    }
+    if(chan) {
+        loadChannelSettings(chan);
+        if(chan->flags & CHANFLAG_CHAN_REGISTERED)
+            cid = chan->channel_id;
+    }
+    printf_mysql_query("SELECT `value` FROM `fundata` WHERE `user` = '%s' AND `cid` = '%d' AND `name` = '%s'", escape_string(uname), cid, escape_string(setting));
+    res = mysql_use();
+    if ((row = mysql_fetch_row(res)) != NULL) {
+        return row[0];
+    } else
+        return NULL;
+}
+
+void setSetting(struct UserNode *user, struct ChanNode *chan, const char *setting, const char *value) {
+    char *uname = "";
+    int cid = 0;
+    MYSQL_RES *res;
+    MYSQL_ROW row;
+    if(user) {
+        uname = ((user->flags & USERFLAG_ISAUTHED) ? user->auth : "*");
+    }
+    if(chan) {
+        loadChannelSettings(chan);
+        if(chan->flags & CHANFLAG_CHAN_REGISTERED)
+            cid = chan->channel_id;
+    }
+    printf_mysql_query("SELECT `id`, `value` FROM `fundata` WHERE `user` = '%s' AND `cid` = '%d' AND `name` = '%s'", escape_string(uname), cid, escape_string(setting));
+    res = mysql_use();
+    if ((row = mysql_fetch_row(res)) != NULL) {
+        if(strcmp(row[1], value))
+            printf_mysql_query("UPDATE `fundata` SET `value` = '%s' WHERE `id` = '%s'", escape_string(value), row[0]);
+    } else
+        printf_mysql_query("INSERT INTO `fundata` (`user`, `cid`, `name`, `value`) VALUES ('%s', '%d', '%s', '%s')", escape_string(uname), cid, escape_string(setting), escape_string(value));
+}
+
+void uno_reply(struct uno_game *game, struct UserNode *user, const char *text, ...) {
+    struct ClientSocket *client = game->textbot;
+    const char *reply_format = get_language_string(user, text);
+    if(reply_format == NULL)
+        reply_format = text;
+    char formatBuf[MAXLEN];
+    sprintf(formatBuf, "PRIVMSG %s :[UNO] %s", game->channel->name, reply_format);
+    va_list arg_list;
+    char sendBuf[MAXLEN];
+    int pos;
+    if (!(client->flags & SOCKET_FLAG_CONNECTED)) return;
+    sendBuf[0] = '\0';
+    va_start(arg_list, text);
+    pos = vsnprintf(sendBuf, MAXLEN - 2, formatBuf, arg_list);
+    va_end(arg_list);
+    if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2;
+    sendBuf[pos] = '\n';
+    sendBuf[pos+1] = '\0';
+    write_socket(client, sendBuf, pos+1);
+}
+
+#undef BOTID
+#undef BOTALIAS
diff --git a/src/modules/NeonFun.mod/bot_NeonFun.h b/src/modules/NeonFun.mod/bot_NeonFun.h
new file mode 100644 (file)
index 0000000..6e0e0f6
--- /dev/null
@@ -0,0 +1,34 @@
+/* bot_NeonFun.h - 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/>. 
+ */
+#ifndef _bot_NeonFun_h
+#define _bot_NeonFun_h
+
+#include "../../main.h"
+
+void init_NeonFun(int type);
+void loop_NeonFun();
+void free_NeonFun(int type);
+
+struct uno_game;
+struct ChanNode;
+struct UserNode;
+
+char* getSetting(struct UserNode *user, struct ChanNode *chan, const char *setting);
+void setSetting(struct UserNode *user, struct ChanNode *chan, const char *setting, const char *value);
+void uno_reply(struct uno_game *game, struct UserNode *user, const char *text, ...);
+
+#endif
\ No newline at end of file
diff --git a/src/modules/NeonFun.mod/cmd_neonfun.c b/src/modules/NeonFun.mod/cmd_neonfun.c
new file mode 100644 (file)
index 0000000..4f659c9
--- /dev/null
@@ -0,0 +1,69 @@
+/* cmd_neonfun.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 "cmd_neonfun.h"
+#include "../../modcmd.h"
+#include "../../lang.h"
+#include "../../ConfigParser.h"
+
+static const struct default_language_entry msgtab[] = {
+    {"NF_UNO_ALREADY_RUNNING", "There is already a UNO game running in %s."}, /* {ARGS: "#channel"} */
+    {"NF_UNO_ALREADY_JOINED", "You have already joined this UNO game."},
+    {"NF_UNO_USER_JOINED", "$b%s$b has joined the game."}, /* {ARGS: "TestUser"} */
+    {"NF_UNO_CREATED", "$b%s$b started an UNO game. Type $b+uno$b to join the game."}, /* {ARGS: "TestUser"} */
+    {"NF_UNO_ERROR", "UNO program error. Please contact a bot administrator. (ErrCode: %d)"}, /* {ARGS: 0} */
+    {"NF_UNO_START", "GAME START"},
+    {"NF_UNO_USER_HURRY_UP", "$b%s$b, it's your turn!"}, /* {ARGS: "TestUser"} */
+    {"NF_UNO_USER_TOOK_CARD", "$b%s$b took another card."}, /* {ARGS: "TestUser"} */
+    {"NF_UNO_USER_TOOK_CARDS", "$b%s$b took $b%d$b cards."}, /* {ARGS: "TestUser", 4} */
+    {"NF_UNO_YOUR_CARDS", "Your cards: %s"}, /* {ARGS: "[+2] [+4] [R5] ..."} */
+    {"NF_UNO_TOP_CARD", "upper card: %s"}, /* {ARGS: "[+4]"} */
+    {"NF_UNO_NOT_YOUR_TURN", "Wait your turn!"},
+    {"NF_UNO_UNKNOWN_CARD", "Unknown card [%s]."}, /* {ARGS: "xy"} */
+    {"NF_UNO_CARD_NOT_IN_DECK", "You don't have this card."},
+    {"NF_UNO_CARD_YELLOW", "yellow"},
+    {"NF_UNO_CARD_BLUE", "blue"},
+    {"NF_UNO_CARD_RED", "red"},
+    {"NF_UNO_CARD_GREEN", "green"},
+    {"NF_UNO_DEFINE_COLOR", "Please define the color you want to set as a parameter to the command (eg. $b+unoplay +4 RED$b)"},
+    {"NF_UNO_CARD_NOT_POSSIBLE", "You can't play this card."},
+    {"NF_UNO_ONE_CARD", "$b%s$b has only one card! $k4U$k$k9N$k$k12O$k"}, /* {ARGS: "TestUser"} */
+    {"NF_UNO_USER_WIN", "$b%s$b has 0 cards! $k4U$k$k9N$k$k12O$k $k4U$k$k9N$k$k12O$k!!!"}, /* {ARGS: "TestUser"} */
+    {"NF_UNO_TIMEOUT", "game stopped (no more active players)."},
+    {"NF_UNO_USER_SKIP", "skipped $b%s$b."}, /* {ARGS: "TestUser"} */
+    {"NF_UNO_GAME_FINISHED", "The UNO Game has been finished!"},
+    {"NF_UNO_ADD_CARD", "$b%s$b has to take up %d cards. There is still the possibility that he/she has another card of this type...?"},
+    {NULL, NULL}
+};
+
+void register_commands() {
+    //NeonFun Commands
+    register_default_language_table(msgtab);
+    register_command_alias(5, "NeonFun");
+    
+    #define USER_COMMAND(NAME,FUNCTION,PARAMCOUNT,PRIVS,FLAGS) register_command(5, NAME, module_id, FUNCTION, PARAMCOUNT, PRIVS, 0, FLAGS)
+    //           NAME            FUNCTION              PARAMS  PRIVS     FLAGS
+    USER_COMMAND("uno",          neonfun_cmd_uno,      0,      NULL,     CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN);
+    USER_COMMAND("unotake",      neonfun_cmd_unotake,  0,      NULL,     CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN);
+    USER_COMMAND("unoplay",      neonfun_cmd_unoplay,  1,      NULL,     CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN);
+    
+    #undef USER_COMMAND
+    
+}
+
+
diff --git a/src/modules/NeonFun.mod/cmd_neonfun.h b/src/modules/NeonFun.mod/cmd_neonfun.h
new file mode 100644 (file)
index 0000000..a97da59
--- /dev/null
@@ -0,0 +1,49 @@
+/* cmd_neonfun.h - 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/>. 
+ */
+#ifndef _cmd_neonfun_h
+#define _cmd_neonfun_h
+#include "../module.h"
+#include "../../main.h"
+#include "../../modcmd.h"
+#include "../../IRCParser.h"
+#include "../../IRCEvents.h"
+#include "../../UserNode.h"
+#include "../../ChanNode.h"
+#include "../../ChanUser.h"
+#include "../../ModeNode.h"
+#include "../../BanNode.h"
+#include "../../ClientSocket.h"
+#include "../../mysqlConn.h"
+#include "../../lang.h"
+#include "../../HandleInfoHandler.h"
+#include "../../WHOHandler.h"
+#include "../../DBHelper.h"
+#include "../../tools.h"
+#include "../../timeq.h"
+#include "../../version.h"
+#include "../../EventLogger.h"
+#include "../../bots.h"
+#include "../../ConfigParser.h"
+#include "bot_NeonFun.h"
+
+void register_commands();
+
+CMD_BIND(neonfun_cmd_uno);
+CMD_BIND(neonfun_cmd_unotake);
+CMD_BIND(neonfun_cmd_unoplay);
+
+#endif
diff --git a/src/modules/NeonFun.mod/cmd_neonfun_uno.c b/src/modules/NeonFun.mod/cmd_neonfun_uno.c
new file mode 100644 (file)
index 0000000..47df483
--- /dev/null
@@ -0,0 +1,80 @@
+/* cmd_neonfun_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 "cmd_neonfun.h"
+#include "game_uno.h"
+
+CMD_BIND(neonfun_cmd_uno) {
+    struct ChanUser *chanuser = getChanUser(user, chan);
+    if(!chanuser) return;
+    struct uno_game *game;
+    for(game = uno_active_games; game; game = game->next) {
+        if(chan == game->channel) {
+            if(game->state == UNO_STATE_WAITING)
+                break;
+            else {
+                reply(getTextBot(), user, "NF_UNO_ALREADY_RUNNING", chan->name);
+                return;
+            }
+        }
+    }
+    if(game) {
+        //check if player has already joined
+        struct uno_player *player, *last_player = NULL;
+        for(player = game->player; player; player = player->next) {
+            if(player->chanuser == chanuser) {
+                reply(getTextBot(), user, "NF_UNO_ALREADY_JOINED");
+                return;
+            } else
+                last_player = player;
+        }
+        if(!last_player) return; //error (game without players?)
+        player = malloc(sizeof(*player));
+        player->chanuser = chanuser;
+        player->count = 0;
+        player->cards = NULL;
+        player->prev = last_player;
+        player->next = NULL;
+        last_player->next = player;
+        game->players++;
+        uno_reply(game, user, "NF_UNO_USER_JOINED", user->nick);
+    } else {
+        game = malloc(sizeof(*game));
+        game->channel = chan;
+        game->textbot = getTextBot();
+        game->state = UNO_STATE_WAITING;
+        game->reverse_direction = 0;
+        game->take_cards_pending = 0;
+        game->deck = NULL;
+        game->top_card = NULL;
+        struct uno_player *player = malloc(sizeof(*player));
+        player->chanuser = chanuser;
+        player->count = 0;
+        player->cards = NULL;
+        player->prev = NULL;
+        player->next = NULL;
+        game->player = player;
+        game->winner = NULL;
+        game->active_player = NULL;
+        game->players = 1;
+        game->active_player = 0;
+        game->timer = timeq_add(30, module_id, uno_game_wait_timeout, game);
+        game->next = uno_active_games;
+        uno_active_games = game;
+        uno_reply(game, user, "NF_UNO_CREATED", user->nick);
+    }
+}
diff --git a/src/modules/NeonFun.mod/cmd_neonfun_unoplay.c b/src/modules/NeonFun.mod/cmd_neonfun_unoplay.c
new file mode 100644 (file)
index 0000000..a6b3419
--- /dev/null
@@ -0,0 +1,68 @@
+/* cmd_neonfun_unoplay.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 "cmd_neonfun.h"
+#include "game_uno.h"
+
+CMD_BIND(neonfun_cmd_unoplay) {
+    struct ChanUser *chanuser = getChanUser(user, chan);
+    if(!chanuser) return;
+    struct uno_game *game;
+    for(game = uno_active_games; game; game = game->next) {
+        if(chan == game->channel) {
+            if(game->state == UNO_STATE_WAITING)
+                return;
+            else 
+                break;
+        }
+    }
+    if(game) {
+        //check if it's the player's turn
+        if(game->active_player->chanuser != chanuser) {
+            reply(getTextBot(), user, "NF_UNO_NOT_YOUR_TURN");
+            return;
+        }
+        game->active_player->timeout = 0;
+        struct uno_card *card = uno_parse_card(game, game->active_player, argv[0]);
+        if(!card) return;
+        unsigned char new_color = card->color;
+        if(card->card == UNO_CARD_ADD_4 || card->card == UNO_CARD_COLOR) {
+            if(argc < 2) {
+                reply(game->textbot, user, "NF_UNO_DEFINE_COLOR");
+                return;
+            }
+            if(!stricmp(argv[1], "RED") || !stricmp(argv[1], "R") || !stricmp(argv[1], get_language_string(user, "NF_UNO_CARD_RED"))) {
+                new_color = UNO_COLOR_RED;
+            } else if(!stricmp(argv[1], "BLUE") || !stricmp(argv[1], "B") || !stricmp(argv[1], get_language_string(user, "NF_UNO_CARD_BLUE"))) {
+                new_color = UNO_COLOR_BLUE;
+            } else if(!stricmp(argv[1], "GREEN") || !stricmp(argv[1], "G") || !stricmp(argv[1], get_language_string(user, "NF_UNO_CARD_GREEN"))) {
+                new_color = UNO_COLOR_GREEN;
+            } else if(!stricmp(argv[1], "YELLOW") || !stricmp(argv[1], "Y") || !stricmp(argv[1], get_language_string(user, "NF_UNO_CARD_YELLOW"))) {
+                new_color = UNO_COLOR_YELLOW;
+            } else {
+                reply(game->textbot, user, "NF_UNO_DEFINE_COLOR");
+                return;
+            }
+        }
+        if(uno_check_card_valid(game, card)) {
+            reply(game->textbot, user, "NF_UNO_CARD_NOT_POSSIBLE");
+            return;
+        }
+        card->color = new_color;
+        uno_play_card(game, game->active_player, card);
+    }
+}
diff --git a/src/modules/NeonFun.mod/cmd_neonfun_unotake.c b/src/modules/NeonFun.mod/cmd_neonfun_unotake.c
new file mode 100644 (file)
index 0000000..22fb21c
--- /dev/null
@@ -0,0 +1,42 @@
+/* cmd_neonfun_unotake.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 "cmd_neonfun.h"
+#include "game_uno.h"
+
+CMD_BIND(neonfun_cmd_unotake) {
+    struct ChanUser *chanuser = getChanUser(user, chan);
+    if(!chanuser) return;
+    struct uno_game *game;
+    for(game = uno_active_games; game; game = game->next) {
+        if(chan == game->channel) {
+            if(game->state == UNO_STATE_WAITING)
+                return;
+            else 
+                break;
+        }
+    }
+    if(game) {
+        //check if it's the player's turn
+        if(game->active_player->chanuser != chanuser) {
+            reply(getTextBot(), user, "NF_UNO_NOT_YOUR_TURN");
+            return;
+        }
+        game->active_player->timeout = 0;
+        uno_action_take_card(game, game->active_player);
+    }
+}
diff --git a/src/modules/NeonFun.mod/game_uno.c b/src/modules/NeonFun.mod/game_uno.c
new file mode 100644 (file)
index 0000000..8ddd9c9
--- /dev/null
@@ -0,0 +1,657 @@
+/* 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);
+        }
+    }
+}
diff --git a/src/modules/NeonFun.mod/game_uno.h b/src/modules/NeonFun.mod/game_uno.h
new file mode 100644 (file)
index 0000000..356fc00
--- /dev/null
@@ -0,0 +1,112 @@
+/* game_uno.h - 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/>. 
+ */
+#ifndef _game_uno_h
+#define _game_uno_h
+#include "../../timeq.h"
+
+#define UNO_STATE_WAITING 1
+#define UNO_STATE_RUNNING 2
+
+#define UNO_COLOR_BLACK  0
+#define UNO_COLOR_RED    1
+#define UNO_COLOR_BLUE   2
+#define UNO_COLOR_GREEN  3
+#define UNO_COLOR_YELLOW 4
+
+#define UNO_CARD_NUMBER_0  0
+#define UNO_CARD_NUMBER_1  1
+#define UNO_CARD_NUMBER_2  2
+#define UNO_CARD_NUMBER_3  3
+#define UNO_CARD_NUMBER_4  4
+#define UNO_CARD_NUMBER_5  5
+#define UNO_CARD_NUMBER_6  6
+#define UNO_CARD_NUMBER_7  7
+#define UNO_CARD_NUMBER_8  8
+#define UNO_CARD_NUMBER_9  9
+#define UNO_CARD_SKIP      10
+#define UNO_CARD_DIRECTION 11
+#define UNO_CARD_ADD_2     12
+#define UNO_CARD_ADD_4     13
+#define UNO_CARD_COLOR     14
+
+#define UNO_IS_NUMBER_CARD(ccard) (ccard->card <= 9)
+
+struct UserNode;
+struct ChanNode;
+struct ChanUser;
+struct ClientSocket;
+extern struct uno_game *uno_active_games;
+
+struct uno_card {
+    unsigned char color;
+    unsigned char card;
+    struct uno_card *prev, *next;
+};
+
+struct uno_card_deck {
+    struct uno_card *cards;
+    int count;
+};
+
+struct uno_player {
+    struct ChanUser *chanuser;
+    struct uno_card *cards;
+    int count;
+    int timeout;
+    struct uno_player *prev, *next;
+};
+
+struct uno_game {
+    struct ChanNode *channel;
+    struct ClientSocket *textbot;
+    int state : 3;
+    int reverse_direction : 1;
+    int take_cards_pending : 1;
+    struct uno_card_deck *deck;
+    struct uno_card *top_card;
+    struct uno_player *player;
+    struct uno_player *winner;
+    struct uno_player *active_player;
+    int players;
+    
+    struct timeq_entry *timer;
+    struct uno_game *next;
+};
+
+struct uno_card_deck *uno_shuffle_deck();
+struct uno_card *uno_get_card(struct uno_card_deck *deck);
+void uno_free_deck(struct uno_card_deck *deck);
+void uno_free_player(struct uno_player *player, struct uno_card_deck *deck);
+void uno_free_topcard(struct uno_card *card);
+void uno_free_game(struct uno_game *game);
+struct uno_player *uno_get_next_player(struct uno_game *game);
+void uno_show_player_cards(struct uno_game *game, struct uno_player *player);
+void uno_show_top_card(struct uno_game *game);
+
+TIMEQ_CALLBACK(uno_game_wait_timeout);
+TIMEQ_CALLBACK(uno_player_timeout);
+
+void uno_action_take_card(struct uno_game *game, struct uno_player *player);
+struct uno_card *uno_parse_card(struct uno_game *game, struct uno_player *player, char *arg1);
+int uno_check_card_valid(struct uno_game *game, struct uno_card *card);
+void uno_play_card(struct uno_game *game, struct uno_player *player, struct uno_card *card);
+
+void uno_event_part(struct ChanUser *chanuser);
+void uno_event_quit(struct UserNode *user);
+void uno_event_freechan(struct ChanNode *chan);
+
+#endif
diff --git a/src/modules/NeonFun.mod/module.c b/src/modules/NeonFun.mod/module.c
new file mode 100644 (file)
index 0000000..7a35ecb
--- /dev/null
@@ -0,0 +1,39 @@
+/* module.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 "bot_NeonFun.h"
+#include "cmd_neonfun.h"
+
+static int module_initialize() {
+    register_commands();
+    return 0;
+}
+
+static void module_start(int type) {
+    init_NeonFun(type);
+    srand(time(NULL));
+}
+
+static void module_loop() {
+    loop_NeonFun();
+}
+
+static void module_stop(int type) {
+    free_NeonFun(type);
+}
+
+MODULE_HEADER(module_initialize, module_start, module_loop, module_stop);
index 0abb44aafe01f07de5e5537e79f0dc92c538b20b..b0209fa144057a983f4f1ee33edef93fdef45ffe 100644 (file)
@@ -21,5 +21,6 @@
 #define NEONSPAM_BOTID 2
 #define DUMMYSERV_BOTID 3
 #define NEONHELP_BOTID 4
+#define NEONFUN_BOTID 5
 
 #endif
\ No newline at end of file
index 212ffa4ec0d9eda76dbf6a48fe311be15d9d462f..8b58b726cf4c4ddb4e1bf5fa35aa936109742e14 100644 (file)
@@ -469,7 +469,7 @@ static int global_cmd_setbot_prefered(struct UserNode *user, MYSQL_ROW bot, char
                 break;
             }
         }
-        printf_mysql_query("UPDATE `bots` SET `prefered` = '%d' WHERE `id` = '%s'", val, bot[15]);
+        printf_mysql_query("UPDATE `bots` SET `textbot` = '%d' WHERE `id` = '%s'", val, bot[15]);
         ret = 1;
     }
     reply(getTextBot(), user, "\002PREFERED   \002 %s", get_language_string(user, (val ? "NS_SET_ON" : "NS_SET_OFF")));