emehta-contrib/wumpus.c

1030 lines
23 KiB
C

/*
* Wumpus - 0.2.0
* Copyright (c) 2006, 2011 William Pitcock <nenolod -at- nenolod.net>
* Portions copyright (c) 2006 Kiyoshi Aman <kiyoshi.aman -at- gmail.com>
*
* Rights to this code are as documented in doc/LICENSE.
*
* Hunt the Wumpus game implementation.
*
*/
#include "atheme-compat.h"
DECLARE_MODULE_V1
(
"contrib/wumpus", false, _modinit, _moddeinit,
PACKAGE_STRING,
"William Pitcock <nenolod -at- nenolod.net>"
);
/* contents */
typedef enum {
E_NOTHING = 0,
E_WUMPUS,
E_PIT,
E_BATS,
E_ARROWS,
E_CRYSTALBALL
} contents_t;
/* room_t: Describes a room that the wumpus or players could be in. */
struct room_ {
int id; /* room 3 or whatever */
mowgli_list_t exits; /* old int count == exits.count */
contents_t contents;
mowgli_list_t players; /* player_t players */
};
typedef struct room_ room_t;
/* player_t: A player object. */
struct player_ {
user_t *u;
room_t *location;
int arrows;
int hp;
bool has_moved;
};
typedef struct player_ player_t;
struct game_ {
int wumpus;
int mazesize;
mowgli_list_t players;
bool running;
bool starting;
room_t *rmemctx; /* memory page context */
service_t *svs;
int wump_hp;
int speed;
unsigned int wantsize;
mowgli_eventloop_timer_t *move_timer;
mowgli_eventloop_timer_t *start_game_timer;
};
typedef struct game_ game_t;
game_t wumpus;
struct __wumpusconfig
{
char *chan;
char *nick;
char *user;
char *host;
char *real;
} wumpus_cfg = {
"#wumpus",
"Wumpus",
"wumpus",
"services.int",
"Hunt the Wumpus"
};
/* ------------------------------ utility functions */
/* returns 1 or 2 depending on if the wumpus is 1 or 2 rooms away */
static int
distance_to_wumpus(player_t *player)
{
mowgli_node_t *n, *tn;
MOWGLI_ITER_FOREACH(n, player->location->exits.head)
{
room_t *r = (room_t *) n->data;
if (r->contents == E_WUMPUS)
return 1;
MOWGLI_ITER_FOREACH(tn, r->exits.head)
{
room_t *r2 = (room_t *) tn->data;
if (r2->contents == E_WUMPUS)
return 2;
/* we don't evaluate exitpoints at this depth */
}
}
return 0;
}
/* can we move or perform an action on this room? */
static bool
adjacent_room(player_t *p, int id)
{
mowgli_node_t *n;
MOWGLI_ITER_FOREACH(n, p->location->exits.head)
{
room_t *r = (room_t *) n->data;
if (r->id == id)
return true;
}
return false;
}
/* finds a player in the list */
static player_t *
find_player(user_t *u)
{
mowgli_node_t *n;
MOWGLI_ITER_FOREACH(n, wumpus.players.head)
{
player_t *p = n->data;
if (p->u == u)
return p;
}
return NULL;
}
/* adds a player to the game */
static player_t *
create_player(user_t *u)
{
player_t *p;
if (find_player(u))
{
notice(wumpus_cfg.nick, u->nick, "You are already playing the game!");
return NULL;
}
if (wumpus.running)
{
notice(wumpus_cfg.nick, u->nick, "The game is already in progress. Sorry!");
return NULL;
}
p = smalloc(sizeof(player_t));
memset(p, '\0', sizeof(player_t));
p->u = u;
p->arrows = 10;
p->hp = 30;
mowgli_node_add(p, mowgli_node_create(), &wumpus.players);
return p;
}
/* destroys a player object and removes them from the game */
static void
resign_player(player_t *player)
{
mowgli_node_t *n;
if (player == NULL)
return;
if (player->location)
{
n = mowgli_node_find(player, &player->location->players);
mowgli_node_delete(n, &player->location->players);
mowgli_node_free(n);
}
n = mowgli_node_find(player, &wumpus.players);
mowgli_node_delete(n, &wumpus.players);
mowgli_node_free(n);
free(player);
}
/* ------------------------------ game functions */
/* builds the maze, and returns false if the maze is too small */
static bool
build_maze(unsigned int size)
{
unsigned int i, j;
room_t *w;
if (size < 10)
return false;
slog(LG_DEBUG, "wumpus: building maze of %d chambers", size);
/* allocate rooms */
wumpus.mazesize = size;
wumpus.rmemctx = scalloc(size, sizeof(room_t));
for (i = 0; i < size; i++)
{
room_t *r = &wumpus.rmemctx[i];
memset(r, '\0', sizeof(room_t));
r->id = i;
/* rooms have 3 exit points, exits are one-way */
for (j = 0; j < 3 && r->exits.count < 3; j++)
{
int t = rand() % size;
/* make sure this isn't a tunnel to itself */
while (t == r->id)
{
mowgli_node_t *rn;
t = rand() % size;
/* also check that this path doesn't already exist. */
MOWGLI_ITER_FOREACH(rn, r->exits.head)
{
room_t *rm = (room_t *) rn->data;
if (rm->id == t)
t = r->id;
}
}
slog(LG_DEBUG, "wumpus: creating link for route %d -> %d", i, t);
mowgli_node_add(&wumpus.rmemctx[t], mowgli_node_create(), &r->exits);
}
slog(LG_DEBUG, "wumpus: finished creating exit paths for chamber %d", i);
}
/* place the wumpus in the maze */
wumpus.wumpus = rand() % size;
w = &wumpus.rmemctx[wumpus.wumpus];
w->contents = E_WUMPUS;
/* pits */
for (j = 0; j < size; j++)
{
/* 42 will do very nicely */
if (rand() % (42 * 2) == 0)
{
room_t *r = &wumpus.rmemctx[j];
r->contents = E_PIT;
slog(LG_DEBUG, "wumpus: added pit to chamber %d", j);
}
}
/* bats */
for (i = 0; i < 2; i++)
{
for (j = 0; j < size; j++)
{
/* 42 will do very nicely */
if (rand() % 42 == 0)
{
room_t *r = &wumpus.rmemctx[j];
r->contents = E_BATS;
slog(LG_DEBUG, "wumpus: added bats to chamber %d", j);
}
}
}
/* arrows */
for (i = 0; i < 3; i++)
{
for (j = 0; j < size; j++)
{
/* 42 will do very nicely */
if (rand() % 42 == 0)
{
room_t *r = &wumpus.rmemctx[j];
r->contents = E_ARROWS;
slog(LG_DEBUG, "wumpus: added arrows to chamber %d", j);
}
}
}
/* find a place to put the crystal ball */
w = &wumpus.rmemctx[rand() % size];
w->contents = E_CRYSTALBALL;
slog(LG_DEBUG, "wumpus: added crystal ball to chamber %d", w->id);
/* ok, do some sanity checking */
for (j = 0; j < size; j++)
if (wumpus.rmemctx[j].exits.count < 3)
{
slog(LG_DEBUG, "wumpus: sanity checking failed");
return false;
}
slog(LG_DEBUG, "wumpus: built maze");
return true;
}
/* init_game depends on these */
static void move_wumpus(void *unused);
static void look_player(player_t *p);
static void end_game(void);
/* sets the game up */
static void
init_game(unsigned int size)
{
mowgli_node_t *n;
if (!build_maze(size))
{
msg(wumpus_cfg.nick, wumpus_cfg.chan, "Maze generation failed, please try again.");
end_game();
return;
}
/* place players in random positions */
MOWGLI_ITER_FOREACH(n, wumpus.players.head)
{
player_t *p = (player_t *) n->data;
p->location = &wumpus.rmemctx[rand() % wumpus.mazesize];
mowgli_node_add(p, mowgli_node_create(), &p->location->players);
look_player(p);
}
/* timer initialization */
wumpus.move_timer = mowgli_timer_add(base_eventloop, "move_wumpus", move_wumpus, NULL, 60);
msg(wumpus_cfg.nick, wumpus_cfg.chan, "The game has started!");
wumpus.running = true;
wumpus.speed = 60;
wumpus.wump_hp = 70;
wumpus.start_game_timer = NULL;
}
/* starts the game */
static void
start_game(void *unused)
{
wumpus.starting = false;
if (wumpus.players.count < 2)
{
msg(wumpus_cfg.nick, wumpus_cfg.chan, "Not enough players to play. :(");
return;
}
if (wumpus.wantsize >= 300)
wumpus.wantsize = 300;
init_game(wumpus.wantsize);
}
/* destroys game objects */
static void
end_game(void)
{
mowgli_node_t *n, *tn;
int i;
/* destroy players */
MOWGLI_ITER_FOREACH_SAFE(n, tn, wumpus.players.head)
resign_player((player_t *) n->data);
/* free memory vector */
if (wumpus.rmemctx)
{
/* destroy links between rooms */
for (i = 0; i < wumpus.mazesize; i++)
{
room_t *r = &wumpus.rmemctx[i];
MOWGLI_ITER_FOREACH_SAFE(n, tn, r->exits.head)
mowgli_node_delete(n, &r->exits);
}
free(wumpus.rmemctx);
wumpus.rmemctx = NULL;
}
wumpus.wumpus = -1;
wumpus.running = false;
mowgli_timer_destroy(base_eventloop, wumpus.move_timer);
wumpus.move_timer = NULL;
/* game is now ended */
}
/* gives the player information about their surroundings */
static void
look_player(player_t *p)
{
mowgli_node_t *n;
return_if_fail(p != NULL);
return_if_fail(p->location != NULL);
notice(wumpus_cfg.nick, p->u->nick, "You are in room %d.", p->location->id);
MOWGLI_ITER_FOREACH(n, p->location->exits.head)
{
room_t *r = (room_t *) n->data;
notice(wumpus_cfg.nick, p->u->nick, "You can move to room %d.", r->id);
}
if (distance_to_wumpus(p))
notice(wumpus_cfg.nick, p->u->nick, "You smell a wumpus!");
/* provide warnings */
MOWGLI_ITER_FOREACH(n, p->location->exits.head)
{
room_t *r = (room_t *) n->data;
if (r->contents == E_WUMPUS)
notice(wumpus_cfg.nick, p->u->nick, "You smell a wumpus!");
if (r->contents == E_PIT)
notice(wumpus_cfg.nick, p->u->nick, "You feel a draft!");
if (r->contents == E_BATS)
notice(wumpus_cfg.nick, p->u->nick, "You hear bats!");
if (r->players.count > 0)
notice(wumpus_cfg.nick, p->u->nick, "You smell humans!");
}
}
/* shoot and kill other players */
static void
shoot_player(player_t *p, int target_id)
{
room_t *r;
player_t *tp;
/* chance to hit; moved up here for convenience. */
int hit = rand() % 3;
if (!p->arrows)
{
notice(wumpus_cfg.nick, p->u->nick, "You have no arrows!");
return;
}
if (adjacent_room(p, target_id) == false)
{
notice(wumpus_cfg.nick, p->u->nick, "You can't shoot into room %d from here.", target_id);
return;
}
if (p->location->id == target_id)
{
notice(wumpus_cfg.nick, p->u->nick, "You can only shoot into adjacent rooms!");
return;
}
r = &wumpus.rmemctx[target_id];
tp = r->players.head ? r->players.head->data : NULL;
p->arrows--;
if ((!tp) && (r->contents != E_WUMPUS))
{
notice(wumpus_cfg.nick, p->u->nick, "You shoot at nothing.");
return;
}
if (tp)
{
if ((hit < 2) && (tp->hp <= 10))
{
msg(wumpus_cfg.nick, wumpus_cfg.chan, "\2%s\2 has been killed by \2%s\2!",
tp->u->nick, p->u->nick);
resign_player(tp);
}
else if ((tp->hp > 0) && (hit < 2)) {
notice(wumpus_cfg.nick, tp->u->nick,
"You were hit by an arrow from room %d.",p->location->id);
notice(wumpus_cfg.nick, p->u->nick, "You hit something.");
tp->hp -= 10;
}
else
{
notice(wumpus_cfg.nick, tp->u->nick, "You have been shot at from room %d.",
p->location->id);
notice(wumpus_cfg.nick, p->u->nick, "You miss what you were shooting at.");
}
}
else if (r->contents == E_WUMPUS) /* Shootin' at the wumpus, we are... */
{
if (((wumpus.wump_hp > 0) && wumpus.wump_hp <= 5) && (hit < 2))
/* we killed the wumpus */
{
notice(wumpus_cfg.nick, p->u->nick, "You have killed the wumpus!");
msg(wumpus_cfg.nick, wumpus_cfg.chan, "The wumpus was killed by \2%s\2.",
p->u->nick);
msg(wumpus_cfg.nick, wumpus_cfg.chan,
"%s has won the game! Congratulations!", p->u->nick);
end_game();
}
else if ((wumpus.wump_hp > 5) && (hit < 2))
{
notice(wumpus_cfg.nick, p->u->nick,
"You shoot the Wumpus, but he shrugs it off and seems angrier!");
wumpus.wump_hp -= 5;
wumpus.speed -= 3;
move_wumpus(NULL);
mowgli_timer_destroy(base_eventloop, wumpus.move_timer);
wumpus.move_timer = mowgli_timer_add(base_eventloop, "move_wumpus", move_wumpus, NULL, wumpus.speed);
}
else
{
notice(wumpus_cfg.nick, p->u->nick, "You miss what you were shooting at.");
move_wumpus(NULL);
}
}
}
/* move_wumpus depends on this */
static void regen_obj(contents_t);
/* check for last-man-standing win condition. */
static void
check_last_person_alive(void)
{
if (wumpus.players.count == 1)
{
player_t *p = (player_t *) wumpus.players.head->data;
msg(wumpus_cfg.nick, wumpus_cfg.chan, "%s won the game! Congratulations!", p->u->nick);
end_game();
}
else if (wumpus.players.count == 0)
{
msg(wumpus_cfg.nick, wumpus_cfg.chan, "Everyone lost. Sucks. :(");
end_game();
}
}
/* move the wumpus, the wumpus moves every 60 seconds */
static void
move_wumpus(void *unused)
{
mowgli_node_t *n, *tn;
room_t *r, *tr;
int w_kills = 0;
/* can we do any of this? if this is null, we really shouldn't be here */
if (wumpus.rmemctx == NULL)
{
slog(LG_DEBUG, "wumpus: move_wumpus() called while game not running!");
mowgli_timer_destroy(base_eventloop, wumpus.move_timer);
return;
}
msg(wumpus_cfg.nick, wumpus_cfg.chan, "You hear footsteps...");
/* start moving */
r = &wumpus.rmemctx[wumpus.wumpus]; /* memslice describing the wumpus's current location */
regen_obj(r->contents);
r->contents = E_NOTHING;
tr = mowgli_node_nth_data(&r->exits, rand() % MOWGLI_LIST_LENGTH(&r->exits));
#ifdef DEBUG_AI
msg(wumpus_cfg.nick, wumpus_cfg.chan, "I moved to chamber %d", tr->id);
#endif
slog(LG_DEBUG, "wumpus: the wumpus is now in room %d! (was in %d)",
tr->id, wumpus.wumpus);
wumpus.wumpus = tr->id;
tr->contents = E_WUMPUS;
#ifdef DEBUG_AI
msg(wumpus_cfg.nick, wumpus_cfg.chan, "On my next turn, I can move to:");
r = &wumpus.rmemctx[wumpus.wumpus];
MOWGLI_ITER_FOREACH(n, r->exits.head)
{
room_t *tr = (room_t *) n->data;
msg(wumpus_cfg.nick, wumpus_cfg.chan, "- %d", tr->id);
}
#endif
MOWGLI_ITER_FOREACH_SAFE(n, tn, wumpus.players.head)
{
player_t *p = (player_t *) n->data;
if (wumpus.wumpus == p->location->id)
{
notice(wumpus_cfg.nick, p->u->nick, "The wumpus has joined your room and eaten you. Sorry.");
w_kills++;
/* player_t *p has been eaten and is no longer in the game */
resign_player(p);
}
else
{
/* prepare for the next turn */
p->has_moved = false;
}
}
/* report any wumpus kills */
if (w_kills)
msg(wumpus_cfg.nick, wumpus_cfg.chan, "You hear the screams of %d surprised adventurer%s.", w_kills,
w_kills != 1 ? "s" : "");
check_last_person_alive();
}
/* regenerates objects */
static void
regen_obj(contents_t obj)
{
wumpus.rmemctx[rand() % wumpus.mazesize].contents = obj;
}
/* handles movement requests from players */
static void
move_player(player_t *p, int id)
{
mowgli_node_t *n;
if (adjacent_room(p, id) == false)
{
notice(wumpus_cfg.nick, p->u->nick, "Sorry, you cannot get to room %d from here.", id);
return;
}
/* What about bats? We check for this first because yeah... */
if (wumpus.rmemctx[id].contents == E_BATS)
{
int target_id = rand() % wumpus.mazesize;
notice(wumpus_cfg.nick, p->u->nick, "Bats have picked you up and taken you to room %d.",
target_id);
msg(wumpus_cfg.nick, wumpus_cfg.chan, "You hear a surprised yell.");
/* move the bats */
wumpus.rmemctx[id].contents = E_NOTHING;
wumpus.rmemctx[target_id].contents = E_BATS;
id = target_id;
/* and fall through, sucks if you hit the two conditions below :-P */
}
/* Is the wumpus in here? */
if (wumpus.wumpus == id)
{
notice(wumpus_cfg.nick, p->u->nick, "You see the wumpus approaching you. You scream for help, but it is too late.");
msg(wumpus_cfg.nick, wumpus_cfg.chan, "You hear a blood-curdling scream.");
/* player_t *p has been killed by the wumpus, remove him from the game */
resign_player(p);
check_last_person_alive();
return;
}
/* What about a pit? */
if (wumpus.rmemctx[id].contents == E_PIT)
{
notice(wumpus_cfg.nick, p->u->nick, "You have fallen into a bottomless pit. Sorry.");
msg(wumpus_cfg.nick, wumpus_cfg.chan, "You hear a faint wail, which gets fainter and fainter.");
/* player_t *p has fallen down a hole, remove him from the game */
resign_player(p);
check_last_person_alive();
return;
}
/* and arrows? */
if (wumpus.rmemctx[id].contents == E_ARROWS)
{
if (p->arrows == 0)
{
notice(wumpus_cfg.nick, p->u->nick, "You found some arrows. You pick them up and continue on your way.");
p->arrows += 5;
}
else
notice(wumpus_cfg.nick, p->u->nick, "You found some arrows. You don't have any room to take them however, "
"so you break them in half and continue on your way.");
wumpus.rmemctx[id].contents = E_NOTHING;
regen_obj(E_ARROWS);
}
/* crystal ball */
if (wumpus.rmemctx[id].contents == E_CRYSTALBALL)
{
notice(wumpus_cfg.nick, p->u->nick, "You find a strange pulsating crystal ball. You examine it, and it shows room %d with the wumpus in it.",
wumpus.wumpus);
notice(wumpus_cfg.nick, p->u->nick, "The crystal ball then vanishes into the miasma.");
wumpus.rmemctx[id].contents = E_NOTHING;
wumpus.rmemctx[rand() % wumpus.mazesize].contents = E_CRYSTALBALL;
}
/* we recycle the mowgli_node_t here for speed */
n = mowgli_node_find(p, &p->location->players);
mowgli_node_delete(n, &p->location->players);
mowgli_node_free(n);
p->location = &wumpus.rmemctx[id];
mowgli_node_add(p, mowgli_node_create(), &p->location->players);
/* provide player with information, including their new location */
look_player(p);
/* tell players about joins. */
if (p->location->players.count > 1)
{
MOWGLI_ITER_FOREACH(n, p->location->players.head)
{
if (n->data != p)
{
player_t *tp = (player_t *) n->data;
notice(wumpus_cfg.nick, tp->u->nick, "%s has joined room %d with you.",
p->u->nick, id);
notice(wumpus_cfg.nick, p->u->nick, "You see %s!",
tp->u->nick);
}
}
}
}
/* ------------------------------ -*-atheme-*- code */
static void cmd_start(sourceinfo_t *si, int parc, char *parv[])
{
if (wumpus.running || wumpus.starting)
{
notice(wumpus_cfg.nick, si->su->nick, "A game is already in progress. Sorry.");
return;
}
msg(wumpus_cfg.nick, wumpus_cfg.chan, "\2%s\2 has started the game! Use \2/msg Wumpus JOIN\2 to play! You have\2 60 seconds\2.",
si->su->nick);
wumpus.starting = true;
wumpus.wantsize = 100;
if (parv[0])
wumpus.wantsize = atoi(parv[0]);
wumpus.start_game_timer = mowgli_timer_add_once(base_eventloop, "start_game", start_game, NULL, 60);
}
/* reference tuple for the above code: cmd_start */
command_t wumpus_start = { "START", "Starts the game.", AC_NONE, 1, cmd_start, { .path = "" } };
static void cmd_join(sourceinfo_t *si, int parc, char *parv[])
{
player_t *p;
if (!wumpus.starting || wumpus.running)
{
notice(wumpus_cfg.nick, si->su->nick, "You cannot use this command right now. Sorry.");
return;
}
p = create_player(si->su);
if (p)
msg(wumpus_cfg.nick, wumpus_cfg.chan, "\2%s\2 has joined the game!", si->su->nick);
}
command_t wumpus_join = { "JOIN", "Joins the game.", AC_NONE, 0, cmd_join, { .path = "" } };
static void cmd_look(sourceinfo_t *si, int parc, char *parv[])
{
player_t *p = find_player(si->su);
if (p == NULL)
{
notice(wumpus_cfg.nick, si->su->nick, "You must be playing the game in order to use this command.");
return;
}
if (!wumpus.running)
{
notice(wumpus_cfg.nick, si->su->nick, "You cannot use this command right now. Sorry.");
return;
}
look_player(p);
}
command_t wumpus_look = { "LOOK", "View surroundings.", AC_NONE, 0, cmd_look, { .path = "" } };
static void cmd_move(sourceinfo_t *si, int parc, char *parv[])
{
player_t *p = find_player(si->su);
char *id = parv[0];
if (!p)
{
notice(wumpus_cfg.nick, si->su->nick, "You must be playing the game in order to use this command.");
return;
}
if (!id)
{
notice(wumpus_cfg.nick, si->su->nick, "You must provide a room to move to.");
return;
}
if (!wumpus.running)
{
notice(wumpus_cfg.nick, si->su->nick, "The game must be running in order to use this command.");
return;
}
move_player(p, atoi(id));
}
command_t wumpus_move = { "MOVE", "Move to another room.", AC_NONE, 1, cmd_move, { .path = "" } };
static void cmd_shoot(sourceinfo_t *si, int parc, char *parv[])
{
player_t *p = find_player(si->su);
char *id = parv[0];
if (!p)
{
notice(wumpus_cfg.nick, si->su->nick, "You must be playing the game in order to use this command.");
return;
}
if (!id)
{
notice(wumpus_cfg.nick, si->su->nick, "You must provide a room to shoot at.");
return;
}
if (!wumpus.running)
{
notice(wumpus_cfg.nick, si->su->nick, "The game must be running in order to use this command.");
return;
}
shoot_player(p, atoi(id));
}
command_t wumpus_shoot = { "SHOOT", "Shoot at another room.", AC_NONE, 1, cmd_shoot, { .path = "" } };
static void cmd_resign(sourceinfo_t *si, int parc, char *parv[])
{
player_t *p = find_player(si->su);
if (!p)
{
notice(wumpus_cfg.nick, si->su->nick, "You must be playing the game in order to use this command.");
return;
}
if (!wumpus.running)
{
notice(wumpus_cfg.nick, si->su->nick, "The game must be running in order to use this command.");
return;
}
msg(wumpus_cfg.nick, wumpus_cfg.chan, "\2%s\2 has quit the game!", p->u->nick);
resign_player(p);
}
command_t wumpus_resign = { "RESIGN", "Resign from the game.", AC_NONE, 0, cmd_resign, { .path = "" } };
static void cmd_reset(sourceinfo_t *si, int parc, char *parv[])
{
if (wumpus.running)
{
msg(wumpus_cfg.nick, wumpus_cfg.chan, "\2%s\2 has ended the game.", si->su->nick);
end_game();
wumpus.running = false;
wumpus.starting = false;
}
}
command_t wumpus_reset = { "RESET", "Resets the game.", AC_IRCOP, 0, cmd_reset, { .path = "" } };
static void cmd_help(sourceinfo_t *si, int parc, char *parv[])
{
command_help(si, si->service->commands);
}
command_t wumpus_help = { "HELP", "Displays this command listing.", AC_NONE, 0, cmd_help, { .path = "help" } };
static void cmd_who(sourceinfo_t *si, int parc, char *parv[])
{
mowgli_node_t *n;
notice(wumpus_cfg.nick, si->su->nick, "The following people are playing:");
MOWGLI_ITER_FOREACH(n, wumpus.players.head)
{
player_t *p = (player_t *) n->data;
notice(wumpus_cfg.nick, si->su->nick, "- %s", p->u->nick);
}
}
command_t wumpus_who = { "WHO", "Displays who is playing the game.", AC_NONE, 0, cmd_who, { .path = "" } };
/* removes quitting players */
static void
user_deleted(user_t *u)
{
player_t *p;
if ((p = find_player(u)) != NULL)
{
msg(wumpus_cfg.nick, wumpus_cfg.chan, "\2%s\2 has quit the game!", p->u->nick);
resign_player(p);
}
}
static void
join_wumpus_channel(server_t *s)
{
join(wumpus_cfg.chan, wumpus.svs->me->nick);
hook_del_server_eob(join_wumpus_channel);
}
/* start handler */
void
_modinit(module_t *m)
{
wumpus.svs = service_add("Wumpus", NULL);
service_set_chanmsg(wumpus.svs, false);
if (cold_start)
{
hook_add_event("server_eob");
hook_add_server_eob(join_wumpus_channel);
}
else if (me.connected)
join(wumpus_cfg.chan, wumpus.svs->me->nick);
hook_add_event("user_delete");
hook_add_user_delete(user_deleted);
service_bind_command(wumpus.svs, &wumpus_help);
service_bind_command(wumpus.svs, &wumpus_start);
service_bind_command(wumpus.svs, &wumpus_join);
service_bind_command(wumpus.svs, &wumpus_move);
service_bind_command(wumpus.svs, &wumpus_shoot);
service_bind_command(wumpus.svs, &wumpus_resign);
service_bind_command(wumpus.svs, &wumpus_reset);
service_bind_command(wumpus.svs, &wumpus_who);
service_bind_command(wumpus.svs, &wumpus_look);
}
void
_moddeinit(module_unload_intent_t intent)
{
/* cleanup after ourselves if necessary */
if (wumpus.running)
end_game();
service_delete(wumpus.svs);
hook_del_user_delete(user_deleted);
service_unbind_command(wumpus.svs, &wumpus_help);
service_unbind_command(wumpus.svs, &wumpus_start);
service_unbind_command(wumpus.svs, &wumpus_join);
service_unbind_command(wumpus.svs, &wumpus_move);
service_unbind_command(wumpus.svs, &wumpus_shoot);
service_unbind_command(wumpus.svs, &wumpus_resign);
service_unbind_command(wumpus.svs, &wumpus_reset);
service_unbind_command(wumpus.svs, &wumpus_who);
service_unbind_command(wumpus.svs, &wumpus_look);
if (wumpus.move_timer)
mowgli_timer_destroy(base_eventloop, wumpus.move_timer);
if (wumpus.start_game_timer)
mowgli_timer_destroy(base_eventloop, wumpus.start_game_timer);
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/