/* group.c */
#include "config.h"
/*
* This file is part of TeenyMUD II.
* Copyright(C) 1995 by Jason Downs. All rights reserved.
*
* TeenyMUD II 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 2 of the License, or
* (at your option) any later version.
*
* TeenyMUD II 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 (see the file 'COPYING'); if not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*
*/
#include <stdio.h>
#include <sys/types.h>
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif /* HAVE_STRING_H */
#include "conf.h"
#include "teeny.h"
#include "commands.h"
#include "externs.h"
/* User-level support for ``groups''. */
/*
* ``Groups'' are maintained seperate from the database, in memory, so
* they don't last between boots or logins.
*/
struct glist {
int player; /* Current player, or leader. */
int flags;
#define GRP_FOLLOW 0x01 /* Follow the leader. */
#define GRP_SILENT 0x02 /* Don't hear group messages. */
#define GRP_CLOSE 0x04 /* No new members. */
/* If top of the list, use these two: */
int count; /* Total number of members. */
char *str; /* Name of group. */
struct glist *next; /* Next in list. NULL if bottom. */
struct glist *prev; /* Previous. NULL if top. */
};
#ifndef MAXPGROUPS
#define MAXPGROUPS 128 /* Should be more than enough. */
#endif /* MAXPGROUPS */
static struct glist *garray[MAXPGROUPS];
static int gtop = 0;
static struct glist *glist_alloc _ANSI_ARGS_((void));
static void glist_free _ANSI_ARGS_((struct glist *));
static int group_find _ANSI_ARGS_((int, struct glist **, struct glist **));
static void group_zap _ANSI_ARGS_((struct glist *, int));
static void group_notify _ANSI_ARGS_((struct glist *, int, char *, int));
static void group_dump _ANSI_ARGS_((int, int, int));
static void group_sdump _ANSI_ARGS_((int, int, struct glist *));
#define can_follow(_x) ((Typeof(_x) == TYP_PLAYER) || \
(Typeof(_x) == TYP_THING))
/*
* glist_alloc()
*
* Allocate a glist struct.
*/
static struct glist *glist_alloc()
{
register struct glist *ret;
ret = (struct glist *)ty_malloc(sizeof(struct glist), "glist_alloc.ret");
bzero((VOID *)ret, sizeof(struct glist));
return(ret);
}
/*
* glist_free()
*
* Destroy a glist struct.
*/
static void glist_free(ptr)
register struct glist *ptr;
{
if(ptr != (struct glist *)NULL) {
if(ptr->str != (char *)NULL) {
/* free this, too. */
ty_free(ptr->str);
}
ty_free(ptr);
}
}
/*
* group_find()
*
* Return the appropiate glist pointers.
*/
static int group_find(player, actual, leader)
register int player;
struct glist **actual, **leader;
{
register struct glist *gcur;
register int acur;
for(acur = 0; acur < gtop; acur++) {
if(garray[acur] != (struct glist *)NULL) {
for(gcur = garray[acur]; gcur != (struct glist *)NULL;
gcur = gcur->next) {
if(gcur->player == player) {
if(actual != (struct glist **)NULL)
*actual = gcur;
if(leader != (struct glist **)NULL)
*leader = garray[acur];
return(1);
}
}
}
}
return(0);
}
/*
* group_ismem()
*
* Return true if player is a member (or leader) of a group.
*/
int group_ismem(player)
int player;
{
return(group_find(player, (struct glist **)NULL, (struct glist **)NULL));
}
/*
* group_isleader()
*
* Return true if player is a leader of a group.
*/
int group_isleader(player)
int player;
{
struct glist *leader;
return(group_find(player, (struct glist **)NULL, &leader)
&& (leader->player == player));
}
/*
* group_remove()
*
* Remove a player from a group. If the player is a leader, disolve the
* group.
*/
void group_remove(player, cause)
int player, cause;
{
struct glist *actual, *leader;
char *name, buffer[MEDBUFFSIZ];
if(group_find(player, &actual, &leader)) {
if(actual == leader)
group_zap(leader, cause);
else {
/* prev should never be NULL. */
(actual->prev)->next = actual->next;
if(actual->next != (struct glist *)NULL)
(actual->next)->prev = actual->prev;
glist_free(actual);
/* Decrement leader's count. */
leader->count--;
/* Notify everyone. */
if(get_str_elt(player, NAME, &name) == -1)
name = "???";
snprintf(buffer, sizeof(buffer), "**] %s has left the group.", name);
group_notify(leader, cause, buffer, 0);
}
}
}
/*
* group_create()
*
* Create a new group and make player the leader.
*/
void group_create(player, cause, switches, str)
int player, cause, switches;
char *str;
{
if(group_ismem(player)) {
if(!(switches & CMD_QUIET))
notify_player(player, cause, player, "You're already in a group!",
NOT_QUIET);
} else {
if(gtop < MAXPGROUPS) {
struct glist *newgrp;
newgrp = glist_alloc();
garray[gtop++] = newgrp;
newgrp->player = player;
/* Set the desc. */
if(str != (char *)NULL)
newgrp->str = ty_strdup(str, "group_create.str");
if(!(switches & CMD_QUIET))
notify_player(player, cause, player, "New group created.", NOT_QUIET);
} else {
if(!(switches & CMD_QUIET))
notify_player(player, cause, player,
"Sorry, there are too many groups.", NOT_QUIET);
}
}
}
/*
* group_join()
*
* Add player to the other's group.
*/
void group_join(player, cause, switches, friend)
int player, cause, switches, friend;
{
struct glist *actual, *leader;
if(group_ismem(player)) {
if(!(switches & CMD_QUIET))
notify_player(player, cause, player,
"You can only be in one group at a time.", NOT_QUIET);
return;
}
if(!group_find(friend, &actual, &leader)) {
if(!(switches & CMD_QUIET))
notify_player(player, cause, player, "But they aren't in a group!",
NOT_QUIET);
} else {
struct glist *newmem;
char *name, buf[MEDBUFFSIZ];
if(leader->flags & GRP_CLOSE) {
if(!(switches & CMD_QUIET))
notify_player(player, cause, player, "Sorry, that group is closed.",
NOT_QUIET);
return;
}
newmem = glist_alloc();
newmem->player = player;
/* Insert them forward of leader. */
newmem->prev = leader;
if(leader->next != (struct glist *)NULL)
(leader->next)->prev = newmem;
newmem->next = leader->next;
leader->next = newmem;
/* Increment leader's count. */
leader->count++;
/* Notify everyone. */
if(get_str_elt(player, NAME, &name) == -1)
name = "???";
snprintf(buf, sizeof(buf), "**] %s has joined the group.", name);
group_notify(leader, cause, buf, 0);
}
}
/*
* group_zap()
*
* Lower-level group killer.
*/
static void group_zap(grptr, cause)
struct glist *grptr;
int cause;
{
register struct glist *curr, *next;
register int idx;
/* Inline notify_group(). */
curr = grptr;
while(curr != (struct glist *)NULL) {
next = curr->next; /* Save next. */
if(!(curr->flags & GRP_SILENT))
notify_player(curr->player, cause, curr->player,
"**] Your group has been dissolved.", NOT_QUIET);
glist_free(curr);
curr = next;
}
/* grptr is no longer valid, but we need it to fix garray. */
for(idx = 0; idx < gtop; idx++) {
if(garray[idx] == grptr) {
/* Found it. Sure hope your bcopy() likes to overlap! */
bcopy((VOID *)&garray[idx+1], (VOID *)&garray[idx], gtop - (idx + 1));
gtop--; /* Important! */
break;
}
}
}
/*
* group_notify()
*
* Send a message to everyone in a group. Low level.
*/
static void group_notify(grptr, cause, str, flags)
struct glist *grptr;
int cause;
char *str;
int flags;
{
register struct glist *curr;
for(curr = grptr; curr != (struct glist *)NULL; curr = curr->next) {
if(!(curr->flags & GRP_SILENT))
notify_player(curr->player, cause, curr->player, str, flags);
}
}
/*
* notify_group()
*
* Send a message to everyone in a group. High level.
*/
void notify_group(player, cause, sender, str, flags)
int player, cause, sender;
char *str;
int flags;
{
struct glist *actual, *leader;
register struct glist *curr;
if(group_find(player, &actual, &leader)) {
for(curr = leader; curr != (struct glist *)NULL; curr = curr->next) {
notify_player(curr->player, cause, sender, str, flags);
}
}
}
/*
* group_follow()
*
* For everyone in player's group (if they lead) who is following, and are
* in ``oldloc'', teleport to ``newloc''. This is different from standard
* teleport: it doesn't trigger teleport actions.
*/
void group_follow(leader, cause, oldloc, newloc)
int leader, cause, oldloc, newloc;
{
struct glist *actual, *rleader;
register struct glist *curr;
if(group_find(leader, &actual, &rleader) && (actual == rleader)) {
for(curr = rleader->next; curr != (struct glist *)NULL; curr = curr->next) {
if((curr->flags & GRP_FOLLOW) && can_follow(curr->player)) {
int loc;
if(get_int_elt(curr->player, LOC, &loc) == -1) {
logfile(LOG_ERROR, "group_follow: couldn't get location of #%d\n",
curr->player);
continue;
}
if((loc != oldloc) || !legal_thingloc_check(curr->player, newloc)) {
/* They aren't with the group. */
continue;
}
move_player_leave(curr->player, cause, oldloc, 0,
"You feel a wrenching sensation...");
move_player_arrive(curr->player, cause, newloc, 0);
look_location(curr->player, cause, 0);
if(has_html(curr->player))
html_anchor_location(curr->player, cause);
}
}
}
}
/*
* group_dump()
*
* Produce a pretty-formatted group listing. High level.
*/
static void group_dump(player, cause, switches)
int player, cause, switches;
{
struct glist *actual, *leader;
register int idx;
if(switches & GROUP_ALL) {
for(idx = 0; idx < gtop; idx++) {
if(garray[idx] != (struct glist *)NULL)
group_sdump(player, cause, garray[idx]);
}
notify_player(player, cause, player, "***End of list***", 0);
} else {
if(group_find(player, &actual, &leader)) {
group_sdump(player, cause, leader);
notify_player(player, cause, player, "***End of list***", 0);
} else {
if(!(switches & CMD_QUIET))
notify_player(player, cause, player, "But you're not in a group!",
NOT_QUIET);
}
}
}
/*
* group_sdump()
*
* Produce a pretty-formatted group listing. Low level.
*/
static void group_sdump(player, cause, grptr)
int player, cause;
struct glist *grptr;
{
char buf[MEDBUFFSIZ], *name;
register struct glist *curr;
snprintf(buf, sizeof(buf), "Group: %s",
((grptr->str != (char *)NULL) ? grptr->str : "*NO NAME*"));
notify_player(player, cause, player, buf, 0);
strcpy(buf, "Members:");
for(curr = grptr; curr != (struct glist *)NULL; curr = curr->next) {
if(get_str_elt(curr->player, NAME, &name) == -1)
name = "???";
if((sizeof(buf) - strlen(buf)) < strlen(name)) { /* Overflow */
notify_player(player, cause, player, buf, 0);
sprintf(buf, "Members: %s", name);
} else
sprintf(&buf[strlen(buf)], " %s", name);
}
notify_player(player, cause, player, buf, 0);
}
/* User commands. */
VOID do_group(player, cause, switches, argone)
int player, cause, switches;
char *argone;
{
if(mudconf.enable_groups) {
if(switches & GROUP_DISOLVE) {
struct glist *actual, *leader;
if(!group_find(player, &actual, &leader)) {
if(!(switches & CMD_QUIET))
notify_player(player, cause, player, "You're not even in a group!",
NOT_QUIET);
return;
}
if((leader->player != player) && !isWIZARD(player)) {
if(!(switches & CMD_QUIET))
notify_player(player, cause, player, "Permission denied.", NOT_QUIET);
return;
}
group_zap(leader, cause);
} else if((switches & GROUP_CLOSE) || (switches & GROUP_OPEN)
|| (switches & GROUP_SILENCE) || (switches & GROUP_NOISY)) {
struct glist *actual, *leader;
char *msg;
if(!group_find(player, &actual, &leader)) {
if(!(switches & CMD_QUIET))
notify_player(player, cause, player, "You're not even in a group!",
NOT_QUIET);
return;
}
if((leader->player != player) && !isWIZARD(player)) {
if(!(switches & CMD_QUIET))
notify_player(player, cause, player, "Permission denied.", NOT_QUIET);
return;
}
if(switches & GROUP_CLOSE) {
leader->flags |= GRP_CLOSE;
msg = "Group closed.";
} else if(switches & GROUP_OPEN) {
leader->flags &= ~GRP_CLOSE;
msg = "Group opened.";
} else if(switches & GROUP_SILENCE) {
actual->flags |= GRP_SILENT;
msg = "Group silenced.";
} else {
actual->flags &= ~GRP_SILENT;
msg = "Group unsilenced.";
}
if(!(switches & CMD_QUIET))
notify_player(player, cause, player, msg, NOT_QUIET);
} else if(switches & GROUP_BOOT) {
struct glist *actual, *leader;
int victim;
if((argone == (char *)NULL) || (argone[0] == '\0')) {
if(!(switches & CMD_QUIET))
notify_player(player, cause, player, "You must specify a player.",
NOT_QUIET);
return;
}
victim = resolve_player(player, cause, argone,
(!(switches & CMD_QUIET) ? RSLV_NOISY|RSLV_ALLOK
: RSLV_ALLOK));
if(victim == -1)
return;
if(!group_find(victim, &actual, &leader)) {
if(!(switches & CMD_QUIET))
notify_player(player, cause, player,
"They're not even in a group!", NOT_QUIET);
return;
}
if((leader->player != player) && !isWIZARD(player)) {
if(!(switches & CMD_QUIET))
notify_player(player, cause, player, "Permission denied.", NOT_QUIET);
return;
}
group_remove(victim, cause);
} else if((switches & GROUP_SAY) || (switches & GROUP_EMOTE)) {
char *name, buf[MEDBUFFSIZ];
if(get_str_elt(player, NAME, &name) == -1) {
notify_bad(player);
return;
}
if(switches & GROUP_SAY) {
snprintf(buf, sizeof(buf), "**] %s says, \"%s\"", name,
((argone != (char *)NULL) ? argone : ""));
} else {
if((argone != (char *)NULL) &&
((argone[0] == ',') || (argone[0] == '\''))) {
snprintf(buf, sizeof(buf), "**] %s%s", name, argone);
} else
snprintf(buf, sizeof(buf), "**] %s %s", name,
((argone != (char *)NULL) ? argone : ""));
}
notify_group(player, cause, player, buf, 0);
} else if(switches & GROUP_DISPLAY) {
group_dump(player, cause, switches);
} else if(switches & GROUP_JOIN) {
int friend;
if((argone == (char *)NULL) || (argone[0] == '\0')) {
if(!(switches & CMD_QUIET))
notify_player(player, cause, player,
"You must specify a player to join.", NOT_QUIET);
return;
}
friend = resolve_player(player, cause, argone,
(!(switches & CMD_QUIET) ? RSLV_NOISY|RSLV_ALLOK
: RSLV_ALLOK));
if(friend == -1)
return;
group_join(player, cause, switches, friend);
} else if(switches & GROUP_FOLLOW) {
struct glist *actual, *leader;
if(!group_find(player, &actual, &leader)) {
if(!(switches & CMD_QUIET))
notify_player(player, cause, player,
"You're not a member of a group!", NOT_QUIET);
} else {
if(actual == leader) {
if(!(switches & CMD_QUIET))
notify_player(player, cause, player,
"You wish to follow yourself?", NOT_QUIET);
return;
}
actual->flags |= GRP_FOLLOW;
if(!(switches & CMD_QUIET))
notify_player(player, cause, player, "Following activated.",
NOT_QUIET);
if(!(leader->flags & GRP_SILENT)) {
char *name, buf[MEDBUFFSIZ];
if(get_str_elt(player, NAME, &name) == -1)
name = "???";
snprintf(buf, sizeof(buf), "%s is now following you.", name);
notify_player(leader->player, cause, player, buf, 0);
}
}
} else {
if(isGUEST(player)) { /* XXX */
if(!(switches & CMD_QUIET))
notify_player(player, cause, player, "Permission denied.", NOT_QUIET);
return;
}
group_create(player, cause, switches, argone);
}
} else {
if(!(switches & CMD_QUIET))
notify_player(player, cause, player, "Sorry, groups are not enabled.",
NOT_QUIET);
}
}