#include "os.h" #include "interpret.h" #include "object.h" #include "lnode.h" #include "sent.h" #include "config.h" /* main.c */ extern int d_flag; extern void *xalloc(int size); extern char *string_copy(char *str); extern void debug_message(char *a, ...); extern char *escape_path (char *str); /* comm1.c */ extern void add_message(char *fmt, ...); /* simulate.c */ extern struct value *find_value(struct lnode_variable *p); extern void fatal(char *fmt, ...); extern void error(char *fmt, ...); extern void free_sentence(struct sentence *p); extern struct lnode_var_def *find_status(char *str, int must_find); /* interpret.c */ extern int count_value_ref(struct object *ob); extern struct value *apply(char *fun, struct object *ob, struct value *arg); /* swap.c */ extern void remove_swap_file(struct object *ob); int tot_alloc_object; /* object.c */ void save_object(struct object *ob, char *file); int restore_object(struct object *ob, char *file); struct object *find_living_object(char *name); struct object *find_player(char *name); void tell_npc(struct object *ob, char *str); void tell_object(struct object *ob, char *str); void free_object(struct object *ob, char *from); void add_ref(struct object *ob, char *from); struct object *get_empty_object(void); void dump_all_objects(void); static void verify(char *str, char *where); static void replace_newline (char *str); static void restore_newline (char *str); /* * Replace newlines in a string with a carriage return, to make the string * writeable on one line. */ static void replace_newline (char *str) { for (; *str; str++) { if (str[0] == '\n') str[0] = '\r'; } } /* * Replace carriage return in a string with newlines. */ static void restore_newline (char *str) { for (; *str; str++) { if (str[0] == '\r') str[0] = '\n'; } } void save_object (struct object *ob, char *file) { char *name; int len; FILE *f; struct lnode_var_def *p; if (strchr (file, '.') || file[0] == '/' || file[0] == '\\' || file[0] == ' ') error ("Illegal file name to save_object(%s).\n", file); if (strncmp (current_object->name, "obj/", 4) != 0 && strncmp (current_object->name, "room/", 5) != 0) error ("Illegal use of save_object()\n"); len = strlen (file); name = xalloc (len + 3); (void) strcpy (name, file); (void) strcat (name, ".o"); escape_path (name); f = fopen (name, "w"); if (f == 0) { error ("Could not open %s for a save.\n", name); #ifdef DO_ABORT WIN32CLEANUP abort (); #else return; #endif } for (p = ob->status; p; p = p->next) { struct value *v = &ob->variables[p->num_var]; char *new_string; if (v->type == T_NUMBER) { (void) fprintf (f, "%s %d\n", p->name, v->u.number); } else if (v->type == T_STRING) { new_string = string_copy (v->u.string); replace_newline (new_string); (void) fprintf (f, "%s \"%s\"\n", p->name, new_string); free (new_string); } } free (name); (void) fclose (f); } int restore_object (struct object *ob, char *file) { char *name, var[100], *val, *buff, *space; int len; FILE *f; struct lnode_var_def *p; struct object *save = current_object; struct stat st; if (strchr (file, '.') || file[0] == '/' || file[0] == '\\' || file[0] == ' ') error ("Illegal file name to restore_object(%s).\n", file); len = strlen (file); name = xalloc (len + 3); (void) strcpy (name, file); if (name[len - 2] == '.' && name[len - 1] == 'c') name[len - 1] = 'o'; else (void) strcat (name, ".o"); escape_path (name); f = fopen (name, "r"); if (f == 0) return 0; if (fstat (fileno (f), &st) == -1) { perror (name); return 0; } if (st.st_size == 0) return 0; val = xalloc (st.st_size + 1); buff = xalloc (st.st_size + 1); current_object = ob; while (1) { struct value *v; if (fgets (buff, st.st_size, f) == 0) break; /* Remember that we have a newline at end of buff ! */ space = strchr (buff, ' '); if (space == 0) fatal ("Illegal format when restore %s.\n", name); (void) strncpy (var, buff, space - buff); var[space - buff] = '\0'; (void) strcpy (val, space + 1); p = find_status (var, 0); if (p == 0) continue; v = &ob->variables[p->num_var]; if (val[0] == '"') { v->type = T_STRING; v->u.string = xalloc (strlen (val) - 2); (void) strncpy (v->u.string, val + 1, strlen (val) - 3); v->u.string[strlen (val) - 3] = '\0'; restore_newline (v->u.string); continue; } v->type = T_NUMBER; v->u.number = atoi (val); } current_object = save; if (d_flag) debug_message ("Object %s restored from %s.\n", ob->name, name); free (name); free (buff); free (val); (void) fclose (f); return 1; } /* * Search for a living object which answers to the id NAME. * If there are many living objects, this algorithm can become quite * heavy. It should be hashed in the future ! */ struct object *find_living_object (char *name) { struct object *ob; struct value *ret; struct value thing; for (ob = obj_list; ob; ob = ob->next_all) { if (!ob->enable_commands || ob->super == 0) continue; thing.type = T_STRING, thing.u.string = name; ret = apply ("id", ob, &thing); if (ret == 0 || (ret->type == T_NUMBER && ret->u.number == 0)) continue; return ob; } return 0; } /* * Search for a player object which answers to the id NAME. * Like find_living_object(), this should really be hashed. */ struct object *find_player (char *name) { struct object *ob; struct value *ret; struct value thing; for (ob = obj_list; ob; ob = ob->next_all) { if (!ob->enable_commands || ob->super == 0) continue; thing.type = T_STRING, thing.u.string = name; /* Is this a player? */ if (apply ("is_player", ob, &const0)) /* Yep... but is it the right one? */ ret = apply ("id", ob, &thing); else /* Nope... try again. */ continue; if (ret == 0 || (ret->type == T_NUMBER && ret->u.number == 0)) continue; return ob; } return 0; } void tell_npc (struct object *ob, char *str) { struct value thing; thing.type = T_STRING; thing.u.string = str; (void) apply ("catch_tell", ob, &thing); } /* * Send a message to an object. * If it is an interactive object, it will go to his * screen. Otherwise, it will go to a local function * catch_tell() in that object. This enables communications * between players and NPC's, and between other NPC's. */ void tell_object (struct object *ob, char *str) { struct object *save_command_giver; if (ob->interactive) { save_command_giver = command_giver; command_giver = ob; add_message ("%s", str); command_giver = save_command_giver; return; } tell_npc (ob, str); } void free_object (struct object *ob, char *from) { struct sentence *s; ob->ref--; if (d_flag) debug_message ("Subtr ref to ob %s: %d (%s)\n", ob->name, ob->ref, from); if (ob->ref > 0) return; if (!ob->destructed) { /* * This should never happen, but it does. I had to do this * kludge, or the game would crash. * When this bug is found, fatal() should be called if this would * happen again. */ debug_message ("Remove a not destructed object.\n"); fprintf (stderr, "Remove a not destructed object.\n"); ob->ref++; return; } ob->reset = 0; if (ob->interactive) fatal ("Tried to free an interactive object.\n"); /* * If the program is freed, then we can also free the variable * deklarations. */ if (ob->prog) { if (free_prog ((struct lnode_def *) (ob->prog))) { free_sub_part ((struct lnode *) ob->status, 1); ob->status = 0; } } ob->prog = 0; if (ob->swap_num) remove_swap_file (ob); for (s = ob->sent; s;) { struct sentence *next; next = s->next; free_sentence (s); s = next; } if (ob->name) { if (d_flag) debug_message ("Free object %s\n", ob->name); free (ob->name); ob->name = 0; } if (ob->variables) { free (ob->variables); ob->variables = 0; } tot_alloc_object--; free (ob); } void add_ref (struct object *ob, char *from) { ob->ref++; if (d_flag) debug_message ("Add reference to object %s: %d (%s)\n", ob->name, ob->ref, from); } struct object *get_empty_object (void) { struct object *ob; tot_alloc_object++; ob = (struct object *) xalloc (sizeof (struct object)); ob->ref = 1; ob->swapped = 0; ob->not_touched = 0; ob->swap_num = 0; ob->ed_buffer = 0; ob->next_inv = 0; ob->contains = 0; ob->reset = 0; ob->interactive = 0; ob->enable_commands = 0; ob->total_light = 0; ob->sent = 0; ob->cloned = 0; ob->variables = 0; ob->super = 0; ob->enable_heart_beat = 0; ob->wl = 0; ob->destructed = 0; return ob; } void dump_all_objects (void) { struct object *ob; FILE *d; d = fopen ("OBJ_DUMP", "w"); if (!d) { add_message ("Couldn't open dump file.\n"); return; } add_message ("Dumping data to 'OBJ_DUMP'... "); for (ob = obj_list; ob; ob = ob->next_all) { if (!ob->name) fprintf (d, "<NULL> (0x%x) ", (unsigned int) ob); else fprintf (d, "%s (0x%x) ", ob->name, (unsigned int) ob); if (ob->super) { if (ob->super->name) fprintf (d, "Super %s (0x%x)\n", ob->super->name, (unsigned int) ob->super); else fprintf (d, "Super <NULL> (0x%x)\n", (unsigned int) ob->super); } else fprintf (d, "\n"); fprintf (d, "\tRef %2d Rst %d Enbl_cmd %d Clnd %d Hrt_bt %d lang ref %d swp %d\n", ob->ref, ob->reset, ob->enable_commands, ob->cloned, // ob->enable_heart_beat, (struct lnode_def *) (ob->prog)->num_ref, ob->enable_heart_beat, ob->prog->num_ref, ob->swapped); } fclose (d); add_message ("DONE\n"); } /* * This one is used for extreme emergency. */ static void verify (char *str, char *where) { static struct object *harry; struct object *ob; int num_ref, i; if (!harry) harry = find_living_object (str); if (!harry) return; if (harry->ref == 0) { debug_message ("%20s verify %s ref 0\n", where, str); harry = 0; } num_ref = 0; for (ob = obj_list; ob; ob = ob->next_all) { if (ob->num_variables == 0) continue; for (i = 0; i < ob->num_variables; i++) { if (ob->variables[i].type == T_OBJECT && ob->variables[i].u.ob == harry) { num_ref++; debug_message ("var in '%s'\n", ob->name); } } } i = count_value_ref (harry); debug_message ("%20s: num ref %2d : %2d+%2d = %2d\n", where, harry->ref, num_ref, i, i + num_ref); }