/*- * Copyright (c) 2005 Zsuzsu <little_zsuzsu@hotmail.com> * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: waffects.c 1019 2007-02-15 00:52:41Z zsuzsu $ */ #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <string.h> #include <time.h> #include "merc.h" #include "const.h" #include "db/db.h" #include "waffects.h" static void show_waffects (CHAR_DATA *ch, BUFFER *output, AREA_DATA *area, bool show_all); static bool can_see_waffect (CHAR_DATA *ch, WORLD_AFFECT_DATA *waff); void affect_notify_area (WORLD_AFFECT_DATA *paf); void affect_notify_world (WORLD_AFFECT_DATA *paf); void fwrite_waff (FILE *fp, WORLD_AFFECT_DATA *waff); WORLD_AFFECT_DATA * load_waff(FILE *fp); /* * global */ WORLD_AFFECT_DATA * world_affect_list; /* functions */ bool is_affected_area (int sn, AREA_DATA *area) { WORLD_AFFECT_DATA *paf; for (paf = world_affect_list; paf; paf = paf->next) if (paf->type == sn && paf->active && area == paf->area) return TRUE; return FALSE; } bool is_affected_world (int sn) { return is_affected_area(sn, NULL); } bool is_waffected (CHAR_DATA *ch, int sn) { return ch_waffected(ch, sn) != NULL; } /* * return the affect on this character * returns NULL if there is no affect * * notice, affects do not compound and area affects override * world affects. */ WORLD_AFFECT_DATA * ch_waffected (CHAR_DATA *ch, int sn) { WORLD_AFFECT_DATA *waf; if (!ch || !ch->in_room || !ch->in_room->area) return NULL; for (waf = world_affect_list; waf; waf = waf->next) { if (waf->type == sn && waf->active && ch->in_room->area == waf->area && ch->level >= waf->min_level && ch->level <= waf->max_level) break; } if (!waf) for (waf = world_affect_list; waf; waf = waf->next) { if (waf->type == sn && waf->active && ch->level >= waf->min_level && ch->level <= waf->max_level) break; } return waf; } WORLD_AFFECT_DATA * affect_find_area (int sn, AREA_DATA *area) { WORLD_AFFECT_DATA *waf; for (waf = world_affect_list; waf; waf = waf->next) if (waf->type == sn && area == waf->area) return waf; return NULL; } WORLD_AFFECT_DATA * affect_find_world (int sn) { return affect_find_area(sn, NULL); } void affect_to_area (WORLD_AFFECT_DATA *paf) { affect_to_world(paf); } void affect_to_world (WORLD_AFFECT_DATA *paf) { WORLD_AFFECT_DATA *waf_new; waf_new = waff_new(); *waf_new = *paf; waf_new->next = world_affect_list; world_affect_list = waf_new; if (paf->active == TRUE) affect_notify_world(paf); } void affect_notify_area (WORLD_AFFECT_DATA *paf) { affect_notify_world(paf); } void affect_notify_world (WORLD_AFFECT_DATA *paf) { DESCRIPTOR_DATA *d; if (IS_SET(paf->notify, WAFF_NOTIFY_OFF)) { for (d = descriptor_list; d; d = d->next) { if (d->connected == CON_PLAYING && d->character && d->character->in_room && (!paf->area || d->character->in_room->area == paf->area) && ((d->character->level >= paf->min_level && d->character->level <= paf->max_level) || IS_IMMORTAL(d->character))) { act_puts(flag_string( (paf->active) ? waff_mortal_on : waff_mortal_off, paf->type), NULL, NULL, d->character, TO_VICT, POS_DEAD); } } } } void affect_remove_area (WORLD_AFFECT_DATA *paf) { affect_remove_world(paf); } void affect_remove_world (WORLD_AFFECT_DATA *paf) { WORLD_AFFECT_DATA *prev; if (world_affect_list == NULL) { BUG("affect_remove_world: tried to remove affect" " from NULL affected world"); waff_free(paf); return; } if (paf == world_affect_list) { world_affect_list = paf->next; } else { for (prev = world_affect_list; prev; prev = prev->next) { if (prev->next == paf) { prev->next = paf->next; break; } } if (prev == NULL) { BUG("affect_remove_world: tried to remove affect" " from world but couldn't find affect."); waff_free(paf); return; } } paf->active = FALSE; affect_notify_world(paf); waff_free(paf); } void affect_strip_world (int sn) { WORLD_AFFECT_DATA *paf; WORLD_AFFECT_DATA *paf_next; for (paf = world_affect_list; paf; paf = paf_next) { paf_next = paf->next; if (paf->type == sn) affect_remove_world(paf); } } void affect_strip_area (int sn, AREA_DATA *area) { WORLD_AFFECT_DATA *paf; WORLD_AFFECT_DATA *paf_next; for (paf = world_affect_list; paf; paf = paf_next) { paf_next = paf->next; if (paf->type == sn && area == paf->area) affect_remove_world(paf); } } /* * add a new affect, or augment an old one. */ void affect_join_world (WORLD_AFFECT_DATA *paf) { WORLD_AFFECT_DATA *paf_old; for (paf_old = world_affect_list; paf_old; paf_old = paf_old->next) { if (paf_old->type == paf->type && paf_old->area == paf->area) { paf->modifier += paf_old->modifier; paf->duration += paf_old->duration; affect_remove_world(paf_old); break; } } affect_to_world(paf); } /* * World Set - to apply waffects * * wset add <affect> duration <n> modifier <n> chance <n> area <n> notify * wset del <affect> <notify> */ DO_FUN(do_wset) { char arg1[MAX_INPUT_LENGTH]; char arg2[MAX_INPUT_LENGTH]; char helpname[] = "'WIZ WSET'"; WORLD_AFFECT_DATA waff; AREA_DATA *area = NULL; bool is_add = FALSE; int type = 0, modifier = 0, duration = -1, chance = 100, min_level = 0, max_level = LEVEL_HERO, visible_level = LEVEL_IMMORTAL, level = ch->level, count = 0, repeat = WAFF_REPEAT_NONE, interval = 0, start_hour = 0; bool notify = FALSE, active = TRUE; argument = one_argument(argument, arg1, sizeof(arg1)); argument = one_argument(argument, arg2, sizeof(arg2)); if (arg1[0] == '\0' || arg2[0] == '\0') { do_help(ch, helpname); return; } if (!str_prefix(arg1, "add")) is_add = TRUE; else if (!str_prefix(arg1, "remove") || !str_prefix(arg1, "delete")) is_add = FALSE; else { do_wset(ch, str_empty); return; } if ((type = flag_value(waff_types, arg2)) <= 0) { char_printf(ch, "choose an affect:\n"); show_flags(ch, waff_types); return; } argument = one_argument(argument, arg1, sizeof(arg1)); argument = one_argument(argument, arg2, sizeof(arg2)); while (arg1[0] != '\0') { if (arg2[0] == '\0') argument = one_argument(argument, arg2, sizeof(arg2)); if (!str_prefix(arg1, "modifier") || !str_prefix(arg1, "modify")) { if (arg2[0] == '\0') { do_help(ch, helpname); return; } if (!str_cmp(arg2, "on")) modifier = 1; else if (!str_cmp(arg2, "off")) modifier = -1; else modifier = atoi(arg2); } else if (!str_prefix(arg1, "duration")) { if (arg2[0] == '\0') { do_help(ch, helpname); return; } if (!str_prefix(arg2, "permanent")) duration = -1; else duration = atoi(arg2); } else if (!str_prefix(arg1, "chance")) { if (arg2[0] == '\0') { do_help(ch, helpname); return; } chance = atoi(arg2); } else if (!str_prefix(arg1, "visible_level")) { if (arg2[0] == '\0') { do_help(ch, helpname); return; } visible_level = atoi(arg2); } else if (!str_prefix(arg1, "min_level")) { if (arg2[0] == '\0') { do_help(ch, helpname); return; } min_level = atoi(arg2); } else if (!str_prefix(arg1, "max_level")) { if (arg2[0] == '\0') { do_help(ch, helpname); return; } max_level = atoi(arg2); } else if (!str_prefix(arg1, "level")) { if (arg2[0] == '\0') { do_help(ch, helpname); return; } level = atoi(arg2); } else if (!str_prefix(arg1, "area")) { if (arg2[0] == '\0') { do_help(ch, helpname); return; } if (arg2[0] == '\0' && (!is_number(arg2) || (area = area_lookup(atoi(arg2))) == NULL)) { area = ch->in_room->area; strnzcpy(arg1, sizeof(arg1), arg2); arg2[0] = '\0'; continue; } } else if (!str_prefix(arg1, "notify")) { if (arg2[0] == '\0' || !str_prefix(arg2, "both") || !str_cmp(arg2, "all") || !str_cmp(arg2, "true")) { notify = WAFF_NOTIFY_ON | WAFF_NOTIFY_OFF; } else if (!str_cmp(arg2, "on")) { notify = WAFF_NOTIFY_ON; } else if (!str_cmp(arg2, "off")) { notify = WAFF_NOTIFY_OFF; } else if (!str_prefix(arg2, "none")) { notify = WAFF_NOTIFY_OFF; } } else if (!str_prefix(arg1, "starthour")) { if (arg2[0] == '\0') { do_help(ch, helpname); return; } start_hour = atoi(arg2); active = FALSE; if (start_hour < 0 || start_hour > 23) { char_printf(ch, "starthour must be between 0 and 23.\n"); return; } } else if (!str_prefix(arg1, "repeat")) { if (is_number(arg2)) { repeat = atoi(arg2); } else { repeat = WAFF_REPEAT_PERM; strnzcpy(arg1, sizeof(arg1), arg2); arg2[0] = '\0'; continue; } } else if (!str_prefix(arg1, "interval")) { if (arg2[0] == '\0' || !is_number(arg2)) { do_help(ch, helpname); return; } interval = atoi(arg2); } arg2[0] = '\0'; argument = one_argument(argument, arg1, sizeof(arg1)); } if (!is_add) { WORLD_AFFECT_DATA *paff; while (!count && (paff = affect_find_area(type, area)) != NULL) { if (paff->level <= ch->level || ch->level == ML || !str_cmp(ch->name, paff->player_name)) { count++; affect_remove_world(paff); } else { char_puts("Sorry, you're not high enough" " level to do that.\n", ch); break; } } if (count > 0) { wiznet("$N has removed $t mode from the world.", ch, flag_string(waff_types, type), WIZ_ANNOUNCE, 0, 0); char_printf(ch, "Removed %s.", flag_string(waff_types, type)); } else { char_puts("No affects removed.", ch); } } else { if (level > ch->level) { char_printf(ch, "The level on this world affect" " is too high. Your max is %d.", ch->level); return; } waff.player = ch; waff.player_name = ch->name; waff.level = level; waff.type = type; waff.interval = interval; waff.duration = duration; waff.modifier = modifier; waff.chance = chance; waff.min_level = min_level; waff.max_level = max_level; waff.visible_level = visible_level; waff.notify = notify; waff.area = area; waff.active = active; waff.start_hour = start_hour; waff.repeat = repeat; /* if there's a start hour don't start immediately */ if (active) waff.timer = duration; else waff.timer = -1; affect_to_world(&waff); char_printf(ch, "Added %s.", flag_string(waff_types, type)); wiznet("$N has added $t mode to the world.", ch, flag_string(waff_types, type), WIZ_ANNOUNCE, 0, 0); } } /* * list world affects */ void do_waff(CHAR_DATA *ch, const char *argument) { BUFFER *output; char arg[MAX_INPUT_LENGTH]; argument = first_arg(argument, arg, sizeof(arg), FALSE); output = buf_new(ch->lang); if (arg[0] == '\0') show_waffects(ch, output, NULL, TRUE); else if (!str_prefix(arg, "world")) show_waffects(ch, output, NULL, FALSE); else if (!str_prefix(arg, "area")) { if (!ch->in_room) BUG("do_waff: character requested area, but not in room."); else show_waffects(ch, output, ch->in_room->area, FALSE); } page_to_char(buf_string(output), ch); buf_free(output); } static void show_waffects (CHAR_DATA *ch, BUFFER *output, AREA_DATA *area, bool show_all) { WORLD_AFFECT_DATA *waf; buf_printf(output, "%s is affected by the following:\n", (!area) ? "The World" : area->name); for (waf = world_affect_list; waf; waf = waf->next) if (show_all || (!show_all && area == waf->area)) { if (can_see_waffect(ch, waf)) { if (IS_IMMORTAL(ch)) buf_printf(output, "{%c '{c%s{z'" " modified by {c%d{z" " timer {c%d{z" " chance {c%d{z%%" " levels {c%d{z-{c%d{z" "\n " " duration {c%d{z" " interval {c%d{z" " repeat {c%d{z" " hour {c%d{z" "\n " " notify {c%s{z" " visby {c%d{z" " immlevel {c%d{z" " by {W%s{x\n" "%s%s%s", (waf->active) ? 'x' : 'D', flag_string(waff_types, waf->type), waf->modifier, waf->timer, waf->chance, waf->min_level, waf->max_level, waf->duration, waf->interval, waf->repeat, waf->start_hour, IS_SET(waf->notify, WAFF_NOTIFY_ON) && IS_SET(waf->notify, WAFF_NOTIFY_OFF) ? "on/off" : IS_SET(waf->notify, WAFF_NOTIFY_ON) ? "on" : IS_SET(waf->notify, WAFF_NOTIFY_OFF) ? "off" : "none", waf->visible_level, waf->level, waf->player_name, (waf->area) ? " area {c" : "", (waf->area) ? waf->area->name : "", (waf->area) ? "{x\n" : ""); else buf_printf(output, "[%s] %s\n", (!waf->area) ? "{gworld{x" : "{yarea{x", flag_string(waff_mortal_on, waf->type)); } } } static bool can_see_waffect (CHAR_DATA *ch, WORLD_AFFECT_DATA *waff) { if (!ch || !ch->in_room || !waff) return FALSE; if (ch->level < waff->visible_level) return FALSE; /* mortals only see area affects if they're in the area*/ if (!IS_IMMORTAL(ch) && waff->active && waff->area && ch->in_room->area != waff->area) return FALSE; if (!IS_IMMORTAL(ch) && waff->active && (waff->min_level > ch->level || waff->max_level < ch->level)) return FALSE; return TRUE; } /* * world affect update, called by update.c */ void world_affect_update (void) { WORLD_AFFECT_DATA *waf; WORLD_AFFECT_DATA *waf_next; struct tm *ct = NULL; int hour = 0; ct = localtime(¤t_time); hour = ct->tm_hour; for (waf = world_affect_list; waf; waf = waf_next) { waf_next = waf->next; if (waf->active) { if (waf->duration < 0) continue; if (waf->timer == 0) { waf->active = FALSE; if (waf->repeat < 1) { affect_remove_world(waf); } else { affect_notify_world(waf); waf->repeat--; waf->timer = (waf->interval) ? waf->interval : -1; } } else waf->timer--; } else { /* if the timer is off, start it in the hour */ if (waf->timer < 0 && waf->start_hour && waf->start_hour == hour) { if (waf->interval) waf->timer = waf->interval; else { waf->active = TRUE; waf->timer = waf->duration; affect_notify_world(waf); } } else if (waf->timer == 0) { waf->active = TRUE; waf->timer = waf->duration; affect_notify_world(waf); } else waf->timer--; } } } /* * world affect constructors and deconstructors. */ WORLD_AFFECT_DATA *waff_new(void) { WORLD_AFFECT_DATA *waff = NULL; waff = calloc(1, sizeof(WORLD_AFFECT_DATA)); waff->start_hour = -1; return waff; } WORLD_AFFECT_DATA *waff_dup(const WORLD_AFFECT_DATA *paf) { WORLD_AFFECT_DATA *naf = waff_new(); naf->player = paf->player; return naf; } void waff_free(WORLD_AFFECT_DATA *paf) { paf->next = NULL; free(paf); } /* * tables */ flag_t waff_types[] = { { "", TABLE_INTVAL, }, { "none", WAFF_NONE, FALSE }, { "arena", WAFF_ARENA, TRUE }, { "auction", WAFF_AUCTION, TRUE }, { "peace", WAFF_PEACE, TRUE }, { "exp", WAFF_EXP, TRUE }, { "qp", WAFF_QP, TRUE }, { "gold", WAFF_GOLD, TRUE }, { "pvp_damage", WAFF_PVP_DAMAGE, TRUE }, { "pvm_damage", WAFF_PVM_DAMAGE, TRUE }, { "inflation", WAFF_INFLATION, TRUE }, { "ffa", WAFF_FFA, TRUE }, { "pk_range", WAFF_PK_RANGE, TRUE }, { NULL } }; flag_t waff_mortal_on[] = { { "", TABLE_INTVAL, }, { "none", WAFF_NONE, FALSE }, { "{WYou feel your death, at the hands of a mortal, might have no meaning.{x", WAFF_ARENA, TRUE }, { "{WThe auctioneer is available, and ready for your business.{x", WAFF_AUCTION, TRUE }, { "{WPeace and tranquility fill the realm bringing an end to violence.{x", WAFF_PEACE, TRUE }, { "{WYou seem blessed with greater insight into your combat experiences.{x", WAFF_EXP, TRUE }, { "{WRumors have it that Questus is desperate for help.{x", WAFF_QP, TRUE }, { "{WYour palm itches.{x", WAFF_GOLD, TRUE }, { "{WYou overhear a shopkeeper grumbling about missing shipments.{x", WAFF_INFLATION, TRUE }, { "{WYour soul feels resilient to mortal attacks.{x", WAFF_PVP_DAMAGE, TRUE }, { "{WLesser souls fear your wrath.{x", WAFF_PVM_DAMAGE, TRUE }, { "The world is rife with {rmurderous intent{x.", WAFF_FFA, TRUE }, { "The world seems very {rdangerous{x all of a sudden!", WAFF_PK_RANGE, TRUE }, { NULL } }; flag_t waff_mortal_off[] = { { "", TABLE_INTVAL, }, { "none", WAFF_NONE, FALSE }, { "{WThe gods will no longer spare you the effects of mortal death.{x", WAFF_ARENA, TRUE }, { "{WThe auctioneer is no longer doing business in the realm.{x", WAFF_AUCTION, TRUE }, { "{WTurmoil and strife return to the world.{x", WAFF_PEACE, TRUE }, { "{WYour enhanced insight into combat experience fades.{x", WAFF_EXP, TRUE }, { "{WRumor is that Questus is no longer hard-up for help.{x", WAFF_QP, TRUE }, { "{WThe itch in your palm subsides.{x", WAFF_GOLD, TRUE }, { "{WYou notice shops seem to have fuller shelves again.{x", WAFF_INFLATION, TRUE }, { "{WYour soul feels more vulnerable to mortal attacks.{x", WAFF_PVP_DAMAGE, TRUE }, { "{WLesser souls steel themselves against your wrath.{x", WAFF_PVM_DAMAGE, TRUE }, { "The world is no longer rife with {rmurderous intent{x.", WAFF_FFA, TRUE }, { "The world seems less {rdangerous{x.", WAFF_PK_RANGE, TRUE }, { NULL } }; #define END_TAG "#END\n" void do_save_waffs (CHAR_DATA *ch, const char *argument) { if (!save_waffs()) char_printf(ch, "World Affects not saved, for some reason.\n"); else char_printf(ch, "World Affects saved.\n"); } bool save_waffs () { WORLD_AFFECT_DATA *waff = world_affect_list; FILE *fp = NULL; int size; if ((fp = dfopen(ETC_PATH, TMP_FILE, "w")) == NULL) return FALSE; fprintf(fp, "#WorldAffectsFor %s\n", strtime(current_time)); fprintf(fp, "WorldEpoc %ld\n\n", current_time); while (waff) { fwrite_waff(fp, waff); waff = waff->next; } size = fprintf(fp, END_TAG); fclose(fp); if (size != strlen(END_TAG)) return FALSE; d2rename(ETC_PATH, TMP_FILE, ETC_PATH, WAFF_STATE); return TRUE; } void fwrite_waff (FILE *fp, WORLD_AFFECT_DATA *waff) { fprintf(fp, "#WORLD_AFFECT\n"); fwrite_string(fp, "Player", waff->player_name); fprintf(fp, "Level %d\n", waff->level); fprintf(fp, "Type %s~\n", flag_string(waff_types, waff->type)); fprintf(fp, "Modifier %d\n", waff->modifier); fprintf(fp, "Duration %d\n", waff->duration); fprintf(fp, "Interval %d\n", waff->interval); fprintf(fp, "Timer %d\n", waff->timer); fprintf(fp, "Chance %d\n", waff->chance); fprintf(fp, "MinLevel %d\n", waff->min_level); fprintf(fp, "MaxLevel %d\n", waff->max_level); fprintf(fp, "VisLevel %d\n", waff->visible_level); fprintf(fp, "StartHour %d\n", waff->start_hour); fprintf(fp, "Repeat %d\n", waff->repeat); fprintf(fp, "Active %d\n", waff->active); fprintf(fp, "Notify %s\n", format_flags(waff->notify)); if (waff->area) { fwrite_string(fp, "AreaName", waff->area->name); fprintf(fp, "AreaMinVnum %d\n", waff->area->min_vnum); } fprintf(fp, "End\n\n"); } int load_waffs () { WORLD_AFFECT_DATA *waff = world_affect_list; FILE *fp = NULL; char *word = "#END"; bool fMatch = FALSE; long old_time = 0L; int count = 0; Line_Number = 0; if (!dfexist(ETC_PATH, WAFF_STATE) || (fp = dfopen(ETC_PATH, WAFF_STATE, "r")) == NULL) return FALSE; while (TRUE) { word = feof(fp) ? "#END" : fread_word(fp); fMatch = FALSE; switch (UPPER(word[0])) { case '#': if (!str_cmp(word, "#WORLD_AFFECT")) { waff = load_waff(fp); if (waff) { if (world_affect_list) { waff->next = world_affect_list; world_affect_list = waff; count++; } else { world_affect_list = waff; count++; } } fMatch = TRUE; } if (!str_cmp(word, "#END")) { return count; } else { fMatch = TRUE; fread_to_eol(fp); } break; case 'W': KEY("WorldEpoc", old_time, fread_number(fp)); break; } if (!fMatch) { LOG("load_waffs: '%s' no match (%dth byte?) line %d", word, ftell(fp), Line_Number); fread_to_eol(fp); } } fclose(fp); /* TODO - update all durations with current_time vs old_time */ } WORLD_AFFECT_DATA * load_waff(FILE *fp) { char *word = "End"; bool fMatch = FALSE; int sanity = 0; WORLD_AFFECT_DATA *waff = NULL; waff = waff_new(); while (sanity++ < 100) { word = feof(fp) ? "End" : fread_word(fp); fMatch = FALSE; switch (UPPER(word[0])) { case '#': fMatch = TRUE; fread_to_eol(fp); break; case 'A': KEY("Active", waff->active, fread_number(fp)); break; case 'C': KEY("Chance", waff->chance, fread_number(fp)); break; case 'D': KEY("Duration", waff->duration, fread_number(fp)); break; case 'E': if (!str_cmp(word, "End")) return waff; break; case 'I': KEY("Interval", waff->interval, fread_number(fp)); break; case 'L': KEY("Level", waff->level, fread_number(fp)); break; case 'M': KEY("MaxLevel", waff->max_level, fread_number(fp)); KEY("MinLevel", waff->min_level, fread_number(fp)); KEY("Modifier", waff->modifier, fread_number(fp)); break; case 'N': KEY("Notify", waff->notify, fread_flags(fp)); break; case 'P': KEY("Player", waff->player_name, fread_string(fp)); break; case 'R': KEY("Repeat", waff->repeat, fread_number(fp)); break; case 'S': KEY("StartHour", waff->start_hour, fread_number(fp)); break; case 'T': KEY("Timer", waff->timer, fread_number(fp)); KEY("Type", waff->type, fread_fstring(waff_types, fp)); break; case 'V': KEY("VisLevel", waff->visible_level, fread_number(fp)); break; } if (!fMatch) { LOG("load_waff: %s: no match (%dth byte?)", word, ftell(fp)); fread_to_eol(fp); } } LOG("load_waff: couldn't find End -- so freeing waff"); waff_free(waff); waff = NULL; return NULL; }