/************************************************************************** * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, * * Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. * * * * Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael * * Chastain, Michael Quan, and Mitchell Tse. * * * * In order to use any part of this Merc Diku Mud, you must comply with * * both the original Diku license in 'license.doc' as well the Merc * * license in 'license.txt'. In particular, you may not remove either of * * these copyright notices. * * * * Much time and thought has gone into this software and you are * * benefiting. We hope that you share your changes too. What goes * * around, comes around. * *************************************************************************** * ROM 2.4 is copyright 1993-1998 Russ Taylor * * ROM has been brought to you by the ROM consortium * * Russ Taylor (rtaylor@hypercube.org) * * Gabrielle Taylor (gtaylor@hypercube.org) * * Brian Moore (zump@rom.org) * * By using this code, you have agreed to follow the terms of the * * ROM license, in the file Rom24/doc/rom.license * *************************************************************************** * 1stMUD ROM Derivative (c) 2001-2003 by Ryan Jennings * * http://1stmud.dlmud.com/ <r-jenn@shaw.ca> * ***************************************************************************/ #include "merc.h" #define IN_CHANNELS_C #include "gcn.h" #undef IN_CHANNELS_C #include "interp.h" #include "globals.h" #include "recycle.h" #include "tables.h" #include "lookup.h" #include "olc.h" const struct gsn_type gcn_table[] = { #undef GCN_H #define MAKE_GCN_TABLE #include "gcn.h" #undef MAKE_GCN_TABLE {NULL, NULL} }; int *gcn_lookup(const char *word) { int i; for (i = 0; gcn_table[i].name != NULL; i++) { if (!str_cmp(word, gcn_table[i].name)) return gcn_table[i].pgsn; } return NULL; } const char *gcn_name(int *pgcn) { int i; for (i = 0; gcn_table[i].pgsn != NULL; i++) { if (pgcn == gcn_table[i].pgsn) return gcn_table[i].name; } return NULL; } const char *get_chan_colour(CHANNEL_DATA * chan) { if (chan->custom_colour != -1) return FORMATF(CTAG(%d), chan->custom_colour); else return chan->colour; } const char *format_channel(CHANNEL_DATA * chan, CHAR_DATA * ch) { static char buf[MSL]; strcpy(buf, chan->format); switch (chan->spec_flag) { case spec_clan_flag: sprintf(buf, chan->format, ch->clan->name, ch->clan->rank[ch->rank].rankname); break; default: break; } strcat(buf, get_chan_colour(chan)); return (buf); } bool display_channel(CHAR_DATA * ch, CHAR_DATA * victim, CHANNEL_DATA * chan, chanarg_t type, bool fShow) { if (!ch || !victim) return FALSE; if (IS_SET(victim->wiznet, WIZ_CHANSNOOP)) return TRUE; if (!fShow) { if (chan->bit > 0 && IS_SET(victim->comm, chan->bit)) return FALSE; if ((type == CHANNEL_EMOTE || type == CHANNEL_SOCIAL) && IS_SET(victim->comm, COMM_NOGOCIAL)) return FALSE; if (IS_SET(victim->comm, COMM_QUIET)) return FALSE; if (is_ignoring(victim, ch->name, IGNORE_CHANNELS)) return FALSE; } switch (chan->spec_flag) { case spec_clan_flag: if (!is_same_clan(ch, victim)) return FALSE; break; case spec_imm_flag: if (!IS_IMMORTAL(victim)) return FALSE; break; case spec_buddy_flag: if (victim != ch && (check_buddy(ch, victim) == -1 || check_buddy(victim, ch) == -1)) return FALSE; break; case spec_public_flag: return TRUE; break; case spec_none: return FALSE; } return TRUE; } #if !defined(NO_WEB) void init_www_history(void) { int x; for (x = 0; x < 20; x++) { www_history[x] = &str_empty[0]; } } #endif void init_channel_history(PC_DATA * pcdata) { int i, x; alloc_mem(pcdata->history, const char **, maxChannel); alloc_mem(pcdata->history_index, int, maxChannel); for (i = 0; i < maxChannel; i++) { pcdata->history_index[i] = 0; if (channel_table[i].page_length <= 0) continue; alloc_mem(pcdata->history[i], const char *, channel_table[i].page_length); for (x = 0; x < channel_table[i].page_length; x++) pcdata->history[i][x] = &str_empty[0]; } } void realloc_channel_history(PC_DATA * pcdata, int gcn, int val, int oval) { int i, x; if (gcn >= 0 && gcn < maxChannel) { if (oval <= 0) { if (val > 0) { alloc_mem(pcdata->history[gcn], const char *, val); } } else { if (val <= 0) { free_mem(pcdata->history[gcn]); } else { realloc_mem(pcdata->history[gcn], const char *, val); for (x = oval; x < val; x++) pcdata->history[gcn][x] = &str_empty[0]; } } } else { realloc_mem(pcdata->history, const char **, val); realloc_mem(pcdata->history_index, int, val); for (i = oval; i < val; i++) { pcdata->history_index[i] = 0; if (channel_table[i].page_length <= 0) continue; alloc_mem(pcdata->history[i], const char *, channel_table[i].page_length); for (x = 0; x < channel_table[i].page_length; x++) pcdata->history[i][x] = &str_empty[0]; } } } void free_channel_history(PC_DATA * pcdata) { int i, x; for (i = 0; i < maxChannel; i++) { if (channel_table[i].page_length <= 0) continue; for (x = 0; x < channel_table[i].page_length; x++) free_string(pcdata->history[i][x]); free_mem(pcdata->history[i]); } free_mem(pcdata->history); free_mem(pcdata->history_index); } #if !defined(NO_WEB) const char *PERS_WWW(CHAR_DATA * ch) { if (!ch) return "@@@"; if (IS_IMMORTAL(ch) && (ch->invis_level != 0 || ch->incog_level != 0)) return "an Immortal"; else if (IS_AFFECTED(ch, AFF_INVISIBLE)) return "someone"; return IS_NPC(ch) ? ch->short_descr : ch->name; } #endif void update_last_data(CHAR_DATA * sender, CHAR_DATA * viewer, CHANNEL_DATA * channel, const char *str, chanarg_t type) { char *time; int gcn, i; char buf[MSL]; const char *chan; if (IS_NPC(viewer) || !channel || channel->page_length <= 0) return; gcn = *channel->index; chan = format_channel(channel, sender); ++viewer->pcdata->history_index[gcn]; viewer->pcdata->history_index[gcn] %= channel->page_length; i = viewer->pcdata->history_index[gcn]; time = str_time(current_time, GET_TZONE(viewer), "%I:%M:%S %p"); switch (type) { case CHANNEL_NORMAL: sprintf(buf, "[%s] %s{x %s %s '%s'{x", time, chan, PERS(sender, viewer), sender == viewer ? "say" : "says", str); break; case CHANNEL_SOCIAL: sprintf(buf, "[%s] %s{x %s{x", time, chan, str); break; case CHANNEL_EMOTE: sprintf(buf, "[%s] %s{x %s %s{x", time, chan, PERS(sender, viewer), str); break; case CHANNEL_THINK: sprintf(buf, "[%s] %s %s . o O ( %s ){x", time, chan, PERS(sender, viewer), str); break; default: bugf("bad channel type [%d]", type); buf[0] = '\0'; break; } replace_string(viewer->pcdata->history[gcn][i], buf); #if !defined(NO_WEB) if (channel->spec_flag != spec_public_flag || sender != viewer) return; ++www_index; www_index %= 20; time = str_time(current_time, -1, "%I:%M:%S %p"); switch (type) { case CHANNEL_NORMAL: sprintf(buf, "[%s] %s %s says '%s'", time, chan, PERS_WWW(sender), str); break; case CHANNEL_SOCIAL: sprintf(buf, "[%s] %s %s", time, chan, str); break; case CHANNEL_EMOTE: sprintf(buf, "[%s] %s %s %s", time, chan, PERS_WWW(sender), str); break; case CHANNEL_THINK: sprintf(buf, "[%s] %s %s . o O ( %s )", time, chan, PERS_WWW(sender), str); break; default: bugf("bad channel type [%d]", type); buf[0] = '\0'; break; } replace_string(www_history[www_index], buf); #endif } void view_last_data(CHAR_DATA * ch, CHANNEL_DATA * chan) { int i, gcn; bool found = FALSE; BUFFER *output; if (!chan || chan->page_length <= 0) return; output = new_buf(); gcn = *chan->index; for (i = (ch->pcdata->history_index[gcn] + 1) % chan->page_length; i != ch->pcdata->history_index[gcn]; i = (i + 1) % chan->page_length) { if (!IS_NULLSTR(ch->pcdata->history[gcn][i])) { found = TRUE; bprintln(output, ch->pcdata->history[gcn][i]); } } if (!IS_NULLSTR(ch->pcdata->history[gcn][ch->pcdata->history_index[gcn]])) { bprintln(output, ch->pcdata->history[gcn][ch->pcdata->history_index[gcn]]); found = TRUE; } if (!found) bprintln(output, "None."); else { bprintlnf(output, "\n\rCurrent time: %s Your Login Time: %s", str_time(current_time, GET_TZONE(ch), "%I:%M:%S %p"), str_time(ch->logon, GET_TZONE(ch), "%I:%M:%S %p")); } sendpage(ch, buf_string(output)); free_buf(output); } void channel_social(CHAR_DATA * ch, CHAR_DATA * victim, OBJ_DATA * obj, const char *string, CHANNEL_DATA * chan) { DESCRIPTOR_DATA *d; const char *type = format_channel(chan, ch); for (d = descriptor_first; d; d = d->next) { CHAR_DATA *vch = CH(d); if (vch && (vch != ch) && (vch != victim) && display_channel(ch, vch, chan, CHANNEL_SOCIAL, FALSE)) { if (d->connected == CON_PLAYING) { char buf[MSL]; sprintf(buf, "%s %s{x", type, string); perform_act(buf, ch, obj, victim, FALSE, vch); } update_last_data(ch, vch, chan, perform_act_string(string, ch, obj, victim, FALSE), CHANNEL_SOCIAL); } } update_last_data(ch, ch, chan, perform_act_string(string, ch, obj, victim, FALSE), CHANNEL_SOCIAL); } void public_ch(CHAR_DATA * ch, const char *argument, int gcn) { char command[MIL + 100]; DESCRIPTOR_DATA *d; chanarg_t chan_type = CHANNEL_NORMAL; char arg_left[MSL]; CHANNEL_DATA *chan = &channel_table[gcn]; const char *type; if (chan == NULL) { chprintln(ch, "Channel is currently unavailable."); return; } type = format_channel(chan, ch); if (IS_NULLSTR(argument)) { if (chan->bit <= 0) chprintln(ch, "What do you want to say?"); else { set_on_off(ch, &ch->comm, chan->bit, FORMATF("%s channel is now OFF.{x", type), FORMATF("%s channel is now ON.{x", type)); } } else { if (IS_SET(ch->comm, COMM_QUIET)) { chprintln(ch, "You must turn off quiet mode first."); return; } if (chan->bit > 0) REMOVE_BIT(ch->comm, (chan->bit)); strcpy(arg_left, argument); argument = one_argument(argument, command); if (command[0] == '+') { CHAR_DATA *victim; char buf[MIL + 200]; SOCIAL_DATA *soc; char argx[MIL]; argument = one_argument(argument, command); if (IS_NULLSTR(command)) { chprintln (ch, "{W+ <social> is used for channel based socials.{x"); return; } if (!(soc = find_social(command))) { chprintln(ch, "{WWhat kind of social is that?!?!{x"); return; } one_argument(argument, argx); victim = NULL; if (IS_NULLSTR(argx)) { sprintf(buf, "%s %s{x", type, soc->char_no_arg); act_new(buf, ch, NULL, NULL, TO_CHAR, POS_DEAD); channel_social(ch, NULL, NULL, soc->others_no_arg, chan); } else if ((victim = get_char_world(ch, argx)) == NULL) { chprintln(ch, "They aren't here."); return; } else { if (is_ignoring(victim, ch->name, IGNORE_SOCIALS)) { act("$N is ignoring socials from you.", ch, NULL, victim, TO_CHAR); return; } if (!display_channel(ch, victim, chan, CHANNEL_SOCIAL, FALSE)) { chprintln(ch, "They can't use that channel."); return; } if (victim == ch) { sprintf(buf, "%s %s{x", type, soc->char_auto); act_new(buf, ch, NULL, NULL, TO_CHAR, POS_DEAD); channel_social(ch, victim, NULL, soc->others_auto, chan); } else { sprintf(buf, "%s %s{x", type, soc->char_found); act_new(buf, ch, NULL, victim, TO_CHAR, POS_DEAD); if (display_channel (ch, victim, chan, CHANNEL_SOCIAL, FALSE)) { sprintf(buf, "%s %s{x", type, soc->vict_found); act_new(buf, ch, NULL, victim, TO_VICT, POS_DEAD); } channel_social(ch, victim, NULL, soc->others_found, chan); } } return; } else if (command[0] == '!') { if (IS_NULLSTR(argument)) { chprintln(ch, "Syntax: ! <argument>"); return; } chan_type = CHANNEL_EMOTE; chprintlnf(ch, "%s %s %s{x", type, IS_NPC(ch) ? ch->short_descr : ch->name, argument); update_last_data(ch, ch, chan, argument, CHANNEL_EMOTE); } else if (command[0] == '@') { if (IS_NULLSTR(argument)) { chprintln(ch, "Syntax: @ <argument>"); return; } chan_type = CHANNEL_THINK; chprintlnf(ch, "%s %s . o O ( %s ){x", type, IS_NPC(ch) ? ch->short_descr : ch->name, argument); update_last_data(ch, ch, chan, argument, CHANNEL_THINK); } else if (is_exact_name(command, "wholist -who -w") && IS_NULLSTR(argument)) { chan_type = CHANNEL_WHO; chprintlnf(ch, "{WPlayers on %s{x", type); chprintln(ch, "{C-------------------{x"); } else if (is_name(command, "/last -hist") && IS_NULLSTR(argument)) { if (IS_NPC(ch) || chan->page_length <= 0) { chprintln(ch, "Channel history unavailable."); return; } chprintlnf(ch, "{WLast %d messages on %s{x", chan->page_length, type); chprintln(ch, "{C------------------------------{x"); view_last_data(ch, chan); return; } else if (command[0] == '?' && IS_NULLSTR(argument)) { chprintln(ch, "Syntax: <message> - send a message"); if (chan->bit > 0) chprintln(ch, " : - toggle channel on/off"); chprintln(ch, " : -hist - display channel history"); chprintln(ch, " : -who - display who is on channel"); chprintln(ch, " : ! <emote> - send an emote over channel"); chprintln(ch, " : + <social> [args] - do a social over channel"); chprintln(ch, " : @ <message> - enclose a message in 'thought bubbles'"); chprintln(ch, " : ? - this message"); return; } else { chan_type = CHANNEL_NORMAL; chprintlnf(ch, "%s You say '%s'{x", type, arg_left); update_last_data(ch, ch, chan, arg_left, CHANNEL_NORMAL); } for (d = descriptor_first; d != NULL; d = d->next) { CHAR_DATA *victim; if ((victim = d->character) == NULL) continue; if (victim == ch) continue; if (!display_channel(ch, victim, chan, chan_type, FALSE)) continue; switch (chan_type) { default: case CHANNEL_NORMAL: if (d->connected == CON_PLAYING) chprintlnf(victim, "%s %s says '%s'{x", type, smash_colour(PERS(ch, victim)), arg_left); update_last_data(ch, victim, chan, arg_left, CHANNEL_NORMAL); break; case CHANNEL_EMOTE: case CHANNEL_THINK: if (d->connected == CON_PLAYING) { if (chan_type == CHANNEL_THINK) chprintlnf(victim, "%s %s . o O ( %s ){x", type, smash_colour(PERS(ch, victim)), argument); else chprintlnf(victim, "%s %s %s{x", type, smash_colour(PERS(ch, victim)), argument); } update_last_data(ch, victim, chan, argument, chan_type); break; case CHANNEL_WHO: if (can_see(ch, victim)) chprintlnf(ch, "{W%s{x", PERS(victim, ch)); break; } } } } CH_CMD(do_gossip) { public_ch(ch, argument, gcn_gossip); } CH_CMD(do_grats) { public_ch(ch, argument, gcn_grats); } CH_CMD(do_quote) { public_ch(ch, argument, gcn_quote); } CH_CMD(do_question) { public_ch(ch, argument, gcn_qa); } CH_CMD(do_answer) { public_ch(ch, argument, gcn_qa); } CH_CMD(do_music) { public_ch(ch, argument, gcn_music); } CH_CMD(do_ooc) { public_ch(ch, argument, gcn_ooc); } CH_CMD(do_immtalk) { public_ch(ch, argument, gcn_immtalk); } /* RT code to display channel status */ CH_CMD(do_channels) { int i; /* lists all channels and their status */ chprintlnf(ch, " %-9s %-6s{w %s", "Command", "Status", "Description"); chprintln(ch, draw_line(ch, NULL, 0)); for (i = 0; i < maxChannel; i++) { if (!display_channel(ch, ch, &channel_table[i], CHANNEL_NORMAL, TRUE)) continue; print_on_off(ch, !IS_SET(ch->comm, channel_table[i].bit), channel_table[i].name, channel_table[i].description); } print_on_off(ch, IS_SET(ch->comm, COMM_SHOUTSOFF), "shouts", "A global channel that transmits with a delay as if there is an echo."); print_on_off(ch, IS_SET(ch->comm, COMM_DEAF), "deaf", "Prevents you from hearing any tells."); print_on_off(ch, IS_SET(ch->comm, COMM_QUIET), "quiet", "Toggles whether you receive any channels at all."); print_on_off(ch, IS_SET(ch->comm, COMM_AFK), "afk", "Sets you Away From Keyboard."); print_on_off(ch, IS_SET(ch->comm, COMM_NOGOCIAL), "nogocial", "Toggles socials/emotes over public channels."); chprintln(ch, draw_line(ch, NULL, 0)); if (IS_SET(ch->comm, COMM_SNOOP_PROOF)) chprintln(ch, "You are immune to snooping."); if (ch->prompt != NULL) { chprintlnf(ch, "Your current prompt is: %s", ch->prompt); } if (IS_SET(ch->comm, COMM_NOSHOUT)) chprintln(ch, "You cannot shout."); if (IS_SET(ch->comm, COMM_NOTELL)) chprintln(ch, "You cannot use tell."); if (IS_SET(ch->comm, COMM_NOCHANNELS)) chprintln(ch, "You cannot use channels."); if (IS_SET(ch->comm, COMM_NOEMOTE)) chprintln(ch, "You cannot show emotions."); } OLCED(chanedit_show) { CHANNEL_DATA *pChan; EDIT_CHAN(ch, pChan); chprintlnf(ch, "Index: %d (%s)", *pChan->index, gcn_name(pChan->index)); chprintlnf(ch, "Name: %s", pChan->name); chprintlnf(ch, "Description: %s", pChan->description); chprintlnf(ch, "Type: %s", flag_string(chan_types, pChan->spec_flag)); chprintlnf(ch, "Bit: %s", flag_string(comm_flags, pChan->bit)); chprintlnf(ch, "Hist Length: %d", pChan->page_length); chprintlnf(ch, "Format: %s{x", pChan->format); chprintlnf(ch, "Colour: %s(looks like this){x", get_chan_colour(pChan)); return TRUE; } OLCED(chanedit_create) { int j, i = maxChannel; CHANNEL_DATA *pChan; struct channel_type *new_table; CHAR_DATA *pch; maxChannel++; alloc_mem(new_table, struct channel_type, maxChannel); if (!new_table) { chprintln(ch, "Memory Allocation Failed!!! Unable to create channel."); return FALSE; } for (j = 0; j < i; j++) new_table[j] = channel_table[j]; free_mem(channel_table); channel_table = new_table; channel_table[i].index = &gcn_null; channel_table[i].bit = 0; channel_table[i].spec_flag = spec_none; channel_table[i].page_length = 20; channel_table[i].format = &str_empty[0]; channel_table[i].colour = &str_empty[0]; channel_table[i].name = str_dup(argument); channel_table[i].description = &str_empty[0]; channel_table[i].custom_colour = -1; for (pch = player_first; pch; pch = pch->next_player) realloc_channel_history(pch->pcdata, -1, maxChannel, i); pChan = &channel_table[i]; edit_start(ch, pChan, ED_CHAN); chprintln(ch, "Channel created."); return TRUE; } OLCED(chanedit_gcn) { CHANNEL_DATA *pChan; int i; EDIT_CHAN(ch, pChan); if (IS_NULLSTR(argument)) { chprintln(ch, "Syntax: gcn <gcn-name>"); return FALSE; } if (is_name(argument, "clear reset none")) { pChan->index = &gcn_null; chprintln(ch, "GCN Entry cleared."); return TRUE; } for (i = 0; gcn_table[i].name != NULL; i++) { if (!str_cmp(argument, gcn_table[i].name)) { pChan->index = gcn_table[i].pgsn; chprintlnf(ch, "Channel now uses global channel pointer '%s'.", gcn_table[i].name); return TRUE; } } chprintln(ch, "That GCN hasn't been coded in yet."); return FALSE; } OLCED(chanedit_colour) { CHANNEL_DATA *pChan; int slot; int cslot_lookup(const char *); EDIT_CHAN(ch, pChan); if (IS_NULLSTR(argument)) { chprintln(ch, "Syntax: colour <colour code>"); chprintln(ch, " : colour <custom colour name>"); return FALSE; } if ((slot = cslot_lookup(argument)) != -1) { pChan->custom_colour = slot; replace_string(pChan->colour, ""); } else { replace_string(pChan->colour, argument); pChan->custom_colour = -1; } chprintln(ch, "Channel colour changed."); return TRUE; } OLCED(chanedit_delete) { CHANNEL_DATA *pChan; EDIT_CHAN(ch, pChan); if (str_cmp(argument, "confirm")) { chprintln (ch, "Typing 'delete confirm' will permanetely remove this channel!\n\r" "-or- 'delete' with any argument will cancel the deletion."); return FALSE; } else { int i, j = 0, c, old = maxChannel; struct channel_type *new_table; int channel_lookup(const char *); CHAR_DATA *pch; maxChannel--; alloc_mem(new_table, struct channel_type, maxChannel); if (!new_table) { chprintln(ch, "Memory Allocation error!!! Unable to delete channel."); return FALSE; } c = channel_lookup(pChan->name); if (c == -1 && pChan->index != NULL) c = *pChan->index; for (i = 0; i < old; i++) if (i != c) new_table[j++] = channel_table[i]; free_mem(channel_table); channel_table = new_table; for (pch = player_first; pch != NULL; pch = pch->next_player) realloc_channel_history(pch->pcdata, -1, maxChannel, old); pChan = &channel_table[0]; ch->desc->pEdit = (void *) pChan; chprintln(ch, "Channel deleted."); } return TRUE; } OLCED(chanedit_list) { int i; for (i = 0; i < maxChannel; i++) chprintlnf(ch, "%2d) %s{x", i, format_channel(&channel_table[i], ch)); return TRUE; }