mirror of
https://github.com/tbamud/tbamud.git
synced 2026-03-18 18:26:33 +01:00
Consolidate rsave and msave into set.c
This commit is contained in:
parent
3f5c33fe66
commit
c1419f6fe7
15 changed files with 1223 additions and 1112 deletions
|
|
@ -8,19 +8,6 @@ A tall, burly human soldier stands here at the gate.
|
|||
has a thick, haggard looking brown beard that is flecked with dust and sand.
|
||||
His eyes have a bluish-tint to them with a small amount of green. One might
|
||||
call his stature bulky, as he has quite a bit of muscle.
|
||||
~
|
||||
B
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
~
|
||||
6218 0 0 0 0 0 0 0 0 E
|
||||
1 3d20+40
|
||||
|
|
@ -39,17 +26,17 @@ Skill 145 5
|
|||
Skill 146 5
|
||||
Skill 147 5
|
||||
E
|
||||
L 17 127 1
|
||||
L 16 117 1
|
||||
L 15 117 1
|
||||
L 11 111 1
|
||||
L 10 107 1
|
||||
L 9 124 1
|
||||
L 8 115 1
|
||||
L 7 108 1
|
||||
L 6 110 1
|
||||
L 5 131 1
|
||||
L 3 118 1
|
||||
L 5 131 1
|
||||
L 6 110 1
|
||||
L 7 108 1
|
||||
L 8 115 1
|
||||
L 9 124 1
|
||||
L 10 107 1
|
||||
L 11 111 1
|
||||
L 15 117 1
|
||||
L 16 117 1
|
||||
L 17 127 1
|
||||
#101
|
||||
Sally~
|
||||
slim lanky human soldier guard~
|
||||
|
|
@ -61,19 +48,6 @@ tightly. What muscle she does have is accentuated, though there is not much.
|
|||
Her arms and legs are lanky, seeming to be longer than normal. A ponytail of
|
||||
dark hair has been pulled back behind her head, intensifying the sharpness of
|
||||
her nose.
|
||||
~
|
||||
B
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
~
|
||||
6218 0 0 0 0 0 0 0 0 E
|
||||
1 3d20+40
|
||||
|
|
@ -116,26 +90,13 @@ has taken hold, yet he has a thick and wiry beard that covers most of his face.
|
|||
Pale blue eyes look bloodshot, and bluish-purple bags hang under them. His
|
||||
hands are marred with minor cuts and scars from years of working in service to
|
||||
others.
|
||||
~
|
||||
B
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
~
|
||||
10 0 0 0 0 0 0 0 0 E
|
||||
1 3d12+60
|
||||
8 8 1
|
||||
E
|
||||
L 9 112 1
|
||||
L 14 113 1
|
||||
L 9 112 1
|
||||
#103
|
||||
Lanky~
|
||||
woman lanky scarred~
|
||||
|
|
@ -148,19 +109,6 @@ dehydration. Tanned dark from the harsh rays of the sun, her upper torso is
|
|||
covered in scars from a lifetime of labor. Unkempt brown hair covers her head,
|
||||
descending to just above her shoulders. Her eyes are a light blue color and
|
||||
appear slightly bloodshot.
|
||||
~
|
||||
B
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
~
|
||||
10 0 0 0 0 0 0 0 0 E
|
||||
1 3d8+60
|
||||
|
|
@ -196,6 +144,10 @@ B
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
It's a rat.
|
||||
~
|
||||
8 0 0 0 0 0 0 0 0 E
|
||||
|
|
@ -220,17 +172,21 @@ A sandy brown kank is here, clacking its pincers.
|
|||
It looks unfinished.
|
||||
~
|
||||
B
|
||||
|
||||
|
||||
|
||||
|
||||
No background has been recorded.
|
||||
~
|
||||
1048584 0 0 0 0 0 0 0 0 E
|
||||
0 0d0+10
|
||||
1 0d0+10
|
||||
8 8 0
|
||||
Str: 18
|
||||
Dex: 5
|
||||
Int: 2
|
||||
Int: 3
|
||||
Wis: 7
|
||||
Con: 18
|
||||
Cha: 1
|
||||
Cha: 3
|
||||
Species: 21
|
||||
AtkT 4
|
||||
E
|
||||
|
|
|
|||
|
|
@ -43,59 +43,6 @@ V 5 0
|
|||
V 6 0
|
||||
V 7 0
|
||||
.
|
||||
#R 131 1759603279
|
||||
O 144 0 20 0 0
|
||||
X 0 0
|
||||
X 1 0
|
||||
X 2 0
|
||||
X 3 0
|
||||
W 0 0
|
||||
W 1 0
|
||||
W 2 0
|
||||
W 3 0
|
||||
V 0 100
|
||||
V 1 100
|
||||
V 2 0
|
||||
V 3 0
|
||||
V 4 0
|
||||
V 5 0
|
||||
V 6 0
|
||||
V 7 0
|
||||
O 145 0 40 0 0
|
||||
X 0 0
|
||||
X 1 0
|
||||
X 2 0
|
||||
X 3 0
|
||||
W 0 0
|
||||
W 1 0
|
||||
W 2 0
|
||||
W 3 0
|
||||
V 0 4
|
||||
V 1 0
|
||||
V 2 2
|
||||
V 3 0
|
||||
V 4 0
|
||||
V 5 0
|
||||
V 6 0
|
||||
V 7 0
|
||||
O 146 0 80 0 0
|
||||
X 0 0
|
||||
X 1 0
|
||||
X 2 0
|
||||
X 3 0
|
||||
W 0 0
|
||||
W 1 0
|
||||
W 2 0
|
||||
W 3 0
|
||||
V 0 6
|
||||
V 1 0
|
||||
V 2 2
|
||||
V 3 0
|
||||
V 4 0
|
||||
V 5 0
|
||||
V 6 0
|
||||
V 7 0
|
||||
.
|
||||
#R 136 1759603517
|
||||
O 147 0 100 0 0
|
||||
X 0 0
|
||||
|
|
@ -197,3 +144,58 @@ V 5 0
|
|||
V 6 0
|
||||
V 7 0
|
||||
.
|
||||
#R 131 1767296856
|
||||
O 144 0 20 0 0
|
||||
X 0 0
|
||||
X 1 0
|
||||
X 2 0
|
||||
X 3 0
|
||||
W 0 0
|
||||
W 1 0
|
||||
W 2 0
|
||||
W 3 0
|
||||
V 0 100
|
||||
V 1 100
|
||||
V 2 0
|
||||
V 3 0
|
||||
V 4 0
|
||||
V 5 0
|
||||
V 6 0
|
||||
V 7 0
|
||||
O 145 0 40 0 0
|
||||
X 0 0
|
||||
X 1 0
|
||||
X 2 0
|
||||
X 3 0
|
||||
W 0 0
|
||||
W 1 0
|
||||
W 2 0
|
||||
W 3 0
|
||||
V 0 4
|
||||
V 1 0
|
||||
V 2 2
|
||||
V 3 0
|
||||
V 4 0
|
||||
V 5 0
|
||||
V 6 0
|
||||
V 7 0
|
||||
O 146 0 80 0 0
|
||||
X 0 0
|
||||
X 1 0
|
||||
X 2 0
|
||||
X 3 0
|
||||
W 0 0
|
||||
W 1 0
|
||||
W 2 0
|
||||
W 3 0
|
||||
V 0 6
|
||||
V 1 0
|
||||
V 2 2
|
||||
V 3 0
|
||||
V 4 0
|
||||
V 5 0
|
||||
V 6 0
|
||||
V 7 0
|
||||
.
|
||||
#R 101 1767296996
|
||||
.
|
||||
|
|
|
|||
|
|
@ -726,7 +726,7 @@ map of the city can be seen upon one wall, giving the impression this would be a
|
|||
good place to learn more about where certain landmarks are.
|
||||
To the south lies Caravan Way, while to the east Wall Road can be seen.
|
||||
~
|
||||
1 131072 0 0 0 0
|
||||
1 131080 0 0 0 0
|
||||
D1
|
||||
~
|
||||
~
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@
|
|||
#include "shop.h"
|
||||
#include "quest.h"
|
||||
#include "modify.h"
|
||||
#include "roomsave.h"
|
||||
|
||||
/* Local defined utility functions */
|
||||
/* do_group utility functions */
|
||||
|
|
|
|||
196
src/act.wizard.c
196
src/act.wizard.c
|
|
@ -37,7 +37,6 @@
|
|||
#include "quest.h"
|
||||
#include "ban.h"
|
||||
#include "screen.h"
|
||||
#include "roomsave.h"
|
||||
|
||||
/* local utility functions with file scope */
|
||||
static int perform_set(struct char_data *ch, struct char_data *vict, int mode, char *val_arg);
|
||||
|
|
@ -6583,198 +6582,3 @@ ACMD(do_audit)
|
|||
|
||||
#undef APPEND_FMT
|
||||
}
|
||||
|
||||
/* ====== Builder snapshot: save a staged mob's gear as its prototype loadout ====== */
|
||||
/* Put these helpers near the top of act.wizard.c (or a shared .c) */
|
||||
struct inv_count { obj_vnum vnum; int qty; struct inv_count *next; };
|
||||
|
||||
static void inv_add(struct inv_count **head, obj_vnum v, int q) {
|
||||
struct inv_count *p;
|
||||
if (q < 1) q = 1;
|
||||
for (p = *head; p; p = p->next)
|
||||
if (p->vnum == v) { p->qty += q; return; }
|
||||
CREATE(p, struct inv_count, 1);
|
||||
p->vnum = v; p->qty = q; p->next = *head; *head = p;
|
||||
}
|
||||
|
||||
static void inv_free_all(struct inv_count **head) {
|
||||
struct inv_count *n, *p = *head;
|
||||
while (p) { n = p->next; free(p); p = n; }
|
||||
*head = NULL;
|
||||
}
|
||||
|
||||
/* Add ACMD prototype to interpreter.h: ACMD(do_msave); */
|
||||
|
||||
ACMD(do_msave)
|
||||
{
|
||||
char a1[MAX_INPUT_LENGTH], a2[MAX_INPUT_LENGTH];
|
||||
char target[MAX_INPUT_LENGTH] = {0}, flags[MAX_INPUT_LENGTH] = {0};
|
||||
struct char_data *vict = NULL, *tmp = NULL;
|
||||
mob_rnum rnum;
|
||||
int include_inv = 0; /* -all */
|
||||
int clear_first = 1; /* default replace; -append flips this to 0 */
|
||||
int equips_added = 0, inv_entries = 0;
|
||||
struct inv_count *inv = NULL;
|
||||
int pos;
|
||||
struct obj_data *o;
|
||||
|
||||
two_arguments(argument, a1, a2);
|
||||
if (*a1 && *a1 == '-') {
|
||||
/* user wrote: msave -flags <mob> */
|
||||
strcpy(flags, a1);
|
||||
strcpy(target, a2);
|
||||
} else {
|
||||
/* user wrote: msave <mob> [-flags] */
|
||||
strcpy(target, a1);
|
||||
strcpy(flags, a2);
|
||||
}
|
||||
|
||||
/* Parse flags (space-separated, any order) */
|
||||
if (*flags) {
|
||||
char buf[MAX_INPUT_LENGTH], *p = flags;
|
||||
while (*p) {
|
||||
p = one_argument(p, buf);
|
||||
if (!*buf) break;
|
||||
if (!str_cmp(buf, "-all")) include_inv = 1;
|
||||
else if (!str_cmp(buf, "-append")) clear_first = 0;
|
||||
else if (!str_cmp(buf, "-clear")) clear_first = 1;
|
||||
else {
|
||||
send_to_char(ch, "Unknown flag '%s'. Try -all, -append, or -clear.\r\n", buf);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Find target mob in the room */
|
||||
if (*target)
|
||||
vict = get_char_vis(ch, target, NULL, FIND_CHAR_ROOM);
|
||||
else {
|
||||
/* No name: pick the first NPC only if exactly one exists */
|
||||
for (tmp = world[IN_ROOM(ch)].people; tmp; tmp = tmp->next_in_room) {
|
||||
if (IS_NPC(tmp)) {
|
||||
if (vict) { vict = NULL; break; } /* more than one — force explicit name */
|
||||
vict = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!vict || !IS_NPC(vict)) {
|
||||
send_to_char(ch, "Target an NPC in this room: msave <mob> [-all] [-append|-clear]\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Resolve prototype and permission to edit its zone */
|
||||
rnum = GET_MOB_RNUM(vict);
|
||||
if (rnum < 0) {
|
||||
send_to_char(ch, "I can’t resolve that mob's prototype.\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CAN_EDIT_ZONE
|
||||
if (!can_edit_zone(ch, real_zone_by_thing(GET_MOB_VNUM(vict)))) {
|
||||
send_to_char(ch, "You don’t have permission to modify that mob’s zone.\r\n");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Build the new loadout into the PROTOTYPE */
|
||||
if (clear_first)
|
||||
loadout_free_list(&mob_proto[rnum].proto_loadout);
|
||||
|
||||
/* Capture equipment: one entry per worn slot */
|
||||
for (pos = 0; pos < NUM_WEARS; pos++) {
|
||||
o = GET_EQ(vict, pos);
|
||||
if (!o) continue;
|
||||
if (GET_OBJ_VNUM(o) <= 0) continue;
|
||||
loadout_add_entry(&mob_proto[rnum].proto_loadout, GET_OBJ_VNUM(o), (int)pos, 1);
|
||||
equips_added++;
|
||||
}
|
||||
|
||||
/* Capture inventory (compressed by vnum) if requested */
|
||||
if (include_inv) {
|
||||
for (o = vict->carrying; o; o = o->next_content) {
|
||||
if (GET_OBJ_VNUM(o) <= 0) continue;
|
||||
inv_add(&inv, GET_OBJ_VNUM(o), 1);
|
||||
}
|
||||
{
|
||||
struct inv_count *p;
|
||||
for (p = inv; p; p = p->next) {
|
||||
loadout_add_entry(&mob_proto[rnum].proto_loadout, p->vnum, -1, MAX(1, p->qty));
|
||||
inv_entries++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Persist to disk: save the zone owning this mob vnum */
|
||||
{
|
||||
zone_rnum zr = real_zone_by_thing(GET_MOB_VNUM(vict));
|
||||
if (zr == NOWHERE) {
|
||||
mudlog(CMP, MAX(LVL_GOD, GET_INVIS_LEV(ch)), TRUE,
|
||||
"msave: could not resolve zone for mob %d", GET_MOB_VNUM(vict));
|
||||
send_to_char(ch, "Saved in memory, but couldn’t resolve zone to write disk.\r\n");
|
||||
} else {
|
||||
save_mobiles(zr);
|
||||
send_to_char(ch,
|
||||
"Loadout saved for mob [%d]. Equipped: %d, Inventory lines: %d%s\r\n",
|
||||
GET_MOB_VNUM(vict), equips_added, inv_entries, include_inv ? "" : " (use -all to include inventory)");
|
||||
mudlog(CMP, MAX(LVL_GOD, GET_INVIS_LEV(ch)), TRUE,
|
||||
"msave: %s saved loadout for mob %d (eq=%d, inv=%d) in zone %d",
|
||||
GET_NAME(ch), GET_MOB_VNUM(vict), equips_added, inv_entries,
|
||||
zone_table[zr].number);
|
||||
}
|
||||
}
|
||||
|
||||
inv_free_all(&inv);
|
||||
}
|
||||
|
||||
ACMD(do_rsave)
|
||||
{
|
||||
room_rnum rnum;
|
||||
zone_rnum znum;
|
||||
int ok;
|
||||
|
||||
if (IS_NPC(ch)) {
|
||||
send_to_char(ch, "Mobiles can’t use this.\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* IN_ROOM(ch) is already a room_rnum (index into world[]). Do NOT pass it to real_room(). */
|
||||
rnum = IN_ROOM(ch);
|
||||
|
||||
if (rnum == NOWHERE || rnum < 0 || rnum > top_of_world) {
|
||||
send_to_char(ch, "You are not in a valid room.\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
znum = world[rnum].zone;
|
||||
if (znum < 0 || znum > top_of_zone_table) {
|
||||
send_to_char(ch, "This room is not attached to a valid zone.\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Optional: permission check */
|
||||
if (!can_edit_zone(ch, znum)) {
|
||||
send_to_char(ch, "You don’t have permission to modify zone %d.\r\n",
|
||||
zone_table[znum].number);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Save just this room into the correct zone’s .rsv file */
|
||||
ok = RoomSave_now(rnum);
|
||||
|
||||
if (!ok) {
|
||||
send_to_char(ch, "rsave: failed.\r\n");
|
||||
mudlog(BRF, GET_LEVEL(ch), TRUE,
|
||||
"RSAVE FAIL: %s room %d (rnum=%d) zone %d (znum=%d)",
|
||||
GET_NAME(ch), GET_ROOM_VNUM(rnum), rnum,
|
||||
zone_table[znum].number, znum);
|
||||
return;
|
||||
}
|
||||
|
||||
send_to_char(ch, "rsave: room %d saved to roomsave file for zone %d.\r\n",
|
||||
GET_ROOM_VNUM(rnum), zone_table[znum].number);
|
||||
|
||||
mudlog(CMP, GET_LEVEL(ch), TRUE,
|
||||
"RSAVE OK: %s room %d (rnum=%d) -> world/rsv/%d.rsv",
|
||||
GET_NAME(ch), GET_ROOM_VNUM(rnum), rnum, zone_table[znum].number);
|
||||
}
|
||||
|
|
|
|||
99
src/db.c
99
src/db.c
|
|
@ -40,7 +40,6 @@
|
|||
#include "mud_event.h"
|
||||
#include "msgedit.h"
|
||||
#include "screen.h"
|
||||
#include "roomsave.h"
|
||||
#include <sys/stat.h>
|
||||
|
||||
/* declarations of most of the 'global' variables */
|
||||
|
|
@ -1981,7 +1980,7 @@ void parse_mobile(FILE *mob_f, int nr)
|
|||
log("SYSERR: Bad 'L' line in mob #%d: '%s' (need <wear_pos> <obj_vnum> [qty]).", nr, line);
|
||||
} else {
|
||||
if (qty < 1) qty = 1;
|
||||
loadout_add_entry(&mob_proto[i].proto_loadout, vnum, (int)wpos, qty);
|
||||
loadout_append_entry(&mob_proto[i].proto_loadout, vnum, (int)wpos, qty);
|
||||
}
|
||||
/* look ahead to see if there is another 'L' */
|
||||
letter = fread_letter(mob_f);
|
||||
|
|
@ -2074,7 +2073,7 @@ void parse_mobile(FILE *mob_f, int nr)
|
|||
log("SYSERR: Bad post-trigger 'L' line in mob #%d: '%s' (need <wear_pos> <obj_vnum> [qty]).", nr, line);
|
||||
} else {
|
||||
if (qty < 1) qty = 1;
|
||||
loadout_add_entry(&mob_proto[i].proto_loadout, vnum, (int)wpos, qty);
|
||||
loadout_append_entry(&mob_proto[i].proto_loadout, vnum, (int)wpos, qty);
|
||||
}
|
||||
letter = fread_letter(mob_f);
|
||||
}
|
||||
|
|
@ -2646,6 +2645,11 @@ void equip_mob_from_loadout(struct char_data *mob)
|
|||
const struct mob_loadout *e = mob_proto[rnum].proto_loadout;
|
||||
if (!e) return;
|
||||
|
||||
struct obj_data *stack[16];
|
||||
int i;
|
||||
for (i = 0; i < (int)(sizeof(stack) / sizeof(stack[0])); i++)
|
||||
stack[i] = NULL;
|
||||
|
||||
for (; e; e = e->next) {
|
||||
int qty = (e->quantity > 0) ? e->quantity : 1;
|
||||
|
||||
|
|
@ -2657,46 +2661,73 @@ void equip_mob_from_loadout(struct char_data *mob)
|
|||
continue;
|
||||
}
|
||||
|
||||
/* Inventory-only request */
|
||||
if (e->wear_pos < 0) {
|
||||
obj_to_char(obj, mob);
|
||||
continue;
|
||||
}
|
||||
if (e->wear_pos >= 0) {
|
||||
for (i = 0; i < (int)(sizeof(stack) / sizeof(stack[0])); i++)
|
||||
stack[i] = NULL;
|
||||
|
||||
/* If the intended slot is free, place it there. We trust the saved slot. */
|
||||
if (e->wear_pos >= 0 && e->wear_pos < NUM_WEARS && GET_EQ(mob, e->wear_pos) == NULL) {
|
||||
/* If the intended slot is free, place it there. We trust the saved slot. */
|
||||
if (e->wear_pos < NUM_WEARS && GET_EQ(mob, e->wear_pos) == NULL) {
|
||||
|
||||
#ifdef STRICT_WEAR_CHECK
|
||||
/* Optional strict flag check (may be mismatched in customized codebases). */
|
||||
if (!invalid_align(mob, obj) /* example gate, add yours as needed */) {
|
||||
equip_char(mob, obj, e->wear_pos);
|
||||
continue;
|
||||
}
|
||||
/* If strict check fails, try alt or inventory below. */
|
||||
#else
|
||||
equip_char(mob, obj, e->wear_pos);
|
||||
continue;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Try the mirrored slot for finger/neck/wrist pairs if intended is occupied. */
|
||||
{
|
||||
int alt = find_alt_slot_same_family(mob, e->wear_pos);
|
||||
if (alt >= 0 && GET_EQ(mob, alt) == NULL) {
|
||||
#ifdef STRICT_WEAR_CHECK
|
||||
if (!invalid_align(mob, obj)) {
|
||||
equip_char(mob, obj, alt);
|
||||
continue;
|
||||
/* Optional strict flag check (may be mismatched in customized codebases). */
|
||||
if (!invalid_align(mob, obj) /* example gate, add yours as needed */) {
|
||||
equip_char(mob, obj, e->wear_pos);
|
||||
} else {
|
||||
obj_to_char(obj, mob);
|
||||
}
|
||||
#else
|
||||
equip_char(mob, obj, alt);
|
||||
continue;
|
||||
equip_char(mob, obj, e->wear_pos);
|
||||
#endif
|
||||
} else {
|
||||
/* Try the mirrored slot for finger/neck/wrist pairs if intended is occupied. */
|
||||
int alt = find_alt_slot_same_family(mob, e->wear_pos);
|
||||
if (alt >= 0 && GET_EQ(mob, alt) == NULL) {
|
||||
#ifdef STRICT_WEAR_CHECK
|
||||
if (!invalid_align(mob, obj)) {
|
||||
equip_char(mob, obj, alt);
|
||||
} else {
|
||||
obj_to_char(obj, mob);
|
||||
}
|
||||
#else
|
||||
equip_char(mob, obj, alt);
|
||||
#endif
|
||||
} else {
|
||||
/* Couldn’t place it — keep in inventory. */
|
||||
obj_to_char(obj, mob);
|
||||
}
|
||||
}
|
||||
|
||||
if (obj_is_storage(obj) || GET_OBJ_TYPE(obj) == ITEM_FURNITURE)
|
||||
stack[0] = obj;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Couldn’t place it — keep in inventory. */
|
||||
obj_to_char(obj, mob);
|
||||
/* Inventory-only request */
|
||||
if (e->wear_pos == -1) {
|
||||
for (i = 0; i < (int)(sizeof(stack) / sizeof(stack[0])); i++)
|
||||
stack[i] = NULL;
|
||||
obj_to_char(obj, mob);
|
||||
if (obj_is_storage(obj) || GET_OBJ_TYPE(obj) == ITEM_FURNITURE)
|
||||
stack[0] = obj;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Nested inventory: wear_pos = -2 (depth 1), -3 (depth 2), etc. */
|
||||
{
|
||||
int depth = -(e->wear_pos) - 1;
|
||||
if (depth <= 0 ||
|
||||
depth >= (int)(sizeof(stack) / sizeof(stack[0])) ||
|
||||
!stack[depth - 1]) {
|
||||
obj_to_char(obj, mob);
|
||||
continue;
|
||||
}
|
||||
obj_to_obj(obj, stack[depth - 1]);
|
||||
if (obj_is_storage(obj) || GET_OBJ_TYPE(obj) == ITEM_FURNITURE) {
|
||||
stack[depth] = obj;
|
||||
for (i = depth + 1; i < (int)(sizeof(stack) / sizeof(stack[0])); i++)
|
||||
stack[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
63
src/genmob.c
63
src/genmob.c
|
|
@ -408,33 +408,60 @@ int write_mobile_record(mob_vnum mvnum, struct char_data *mob, FILE *fd)
|
|||
char ddesc[MAX_STRING_LENGTH];
|
||||
char bdesc[MAX_STRING_LENGTH];
|
||||
char buf[MAX_STRING_LENGTH];
|
||||
int has_bdesc = 0;
|
||||
|
||||
ldesc[MAX_STRING_LENGTH - 1] = '\0';
|
||||
ddesc[MAX_STRING_LENGTH - 1] = '\0';
|
||||
bdesc[MAX_STRING_LENGTH - 1] = '\0';
|
||||
strip_cr(strncpy(ldesc, GET_LDESC(mob), MAX_STRING_LENGTH - 1));
|
||||
strip_cr(strncpy(ddesc, GET_DDESC(mob), MAX_STRING_LENGTH - 1));
|
||||
if (GET_BDESC(mob))
|
||||
if (GET_BDESC(mob)) {
|
||||
strip_cr(strncpy(bdesc, GET_BDESC(mob), MAX_STRING_LENGTH - 1));
|
||||
else
|
||||
{
|
||||
const char *p;
|
||||
for (p = bdesc; *p; p++) {
|
||||
if (*p != ' ' && *p != '\t' && *p != '\r' && *p != '\n') {
|
||||
has_bdesc = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else
|
||||
bdesc[0] = '\0';
|
||||
|
||||
int n = snprintf(buf, MAX_STRING_LENGTH,
|
||||
"#%d\n"
|
||||
"%s%c\n"
|
||||
"%s%c\n"
|
||||
"%s%c\n"
|
||||
"%s%c\n"
|
||||
"%s%c\n"
|
||||
"B\n"
|
||||
"%s%c\n",
|
||||
mvnum,
|
||||
GET_NAME(mob), STRING_TERMINATOR,
|
||||
GET_KEYWORDS(mob), STRING_TERMINATOR,
|
||||
GET_SDESC(mob), STRING_TERMINATOR,
|
||||
ldesc, STRING_TERMINATOR,
|
||||
ddesc, STRING_TERMINATOR,
|
||||
bdesc, STRING_TERMINATOR);
|
||||
int n;
|
||||
if (has_bdesc) {
|
||||
n = snprintf(buf, MAX_STRING_LENGTH,
|
||||
"#%d\n"
|
||||
"%s%c\n"
|
||||
"%s%c\n"
|
||||
"%s%c\n"
|
||||
"%s%c\n"
|
||||
"%s%c\n"
|
||||
"B\n"
|
||||
"%s%c\n",
|
||||
mvnum,
|
||||
GET_NAME(mob), STRING_TERMINATOR,
|
||||
GET_KEYWORDS(mob), STRING_TERMINATOR,
|
||||
GET_SDESC(mob), STRING_TERMINATOR,
|
||||
ldesc, STRING_TERMINATOR,
|
||||
ddesc, STRING_TERMINATOR,
|
||||
bdesc, STRING_TERMINATOR);
|
||||
} else {
|
||||
n = snprintf(buf, MAX_STRING_LENGTH,
|
||||
"#%d\n"
|
||||
"%s%c\n"
|
||||
"%s%c\n"
|
||||
"%s%c\n"
|
||||
"%s%c\n"
|
||||
"%s%c\n",
|
||||
mvnum,
|
||||
GET_NAME(mob), STRING_TERMINATOR,
|
||||
GET_KEYWORDS(mob), STRING_TERMINATOR,
|
||||
GET_SDESC(mob), STRING_TERMINATOR,
|
||||
ldesc, STRING_TERMINATOR,
|
||||
ddesc, STRING_TERMINATOR);
|
||||
}
|
||||
|
||||
if (n >= MAX_STRING_LENGTH) {
|
||||
mudlog(BRF, LVL_BUILDER, TRUE,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
#include "fight.h"
|
||||
#include "quest.h"
|
||||
#include "mud_event.h"
|
||||
#include "roomsave.h"
|
||||
|
||||
/* local file scope variables */
|
||||
static int extractions_pending = 0;
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
#include "fight.h"
|
||||
#include "screen.h"
|
||||
#include "mud_event.h"
|
||||
#include "roomsave.h"
|
||||
#include "set.h"
|
||||
#include <time.h>
|
||||
|
||||
/* local file scope function prototypes */
|
||||
|
|
@ -484,8 +484,8 @@ void point_update(void)
|
|||
}
|
||||
|
||||
/* ---- Room SAVE autosave (every 10 minutes; adjust the 600 as desired) ----
|
||||
* Requires: #include "roomsave.h" at the top of this file.
|
||||
* Saves all rooms flagged ROOM_SAVE via roomsave.c.
|
||||
* Requires: #include "set.h" at the top of this file.
|
||||
* Saves all rooms flagged ROOM_SAVE via set.c.
|
||||
*/
|
||||
if (++roomsave_pulse >= (PASSES_PER_SEC * 600)) {
|
||||
roomsave_pulse = 0;
|
||||
|
|
|
|||
708
src/roomsave.c
708
src/roomsave.c
|
|
@ -1,708 +0,0 @@
|
|||
/**
|
||||
* @file roomsave.c
|
||||
* Room file loading/saving and utility routines.
|
||||
*
|
||||
* This set of code was not originally part of the circlemud distribution.
|
||||
*/
|
||||
|
||||
#include "conf.h"
|
||||
#include "sysdep.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
#include "structs.h"
|
||||
#include "utils.h"
|
||||
#include "db.h"
|
||||
#include "handler.h"
|
||||
#include "comm.h"
|
||||
#include "constants.h"
|
||||
#include "roomsave.h"
|
||||
|
||||
/* Write saved rooms under lib/world/rsv/<vnum>.rsv (like wld/ zon/ obj/). */
|
||||
#ifndef ROOMSAVE_PREFIX
|
||||
#define ROOMSAVE_PREFIX LIB_WORLD "rsv/"
|
||||
#endif
|
||||
#ifndef ROOMSAVE_EXT
|
||||
#define ROOMSAVE_EXT ".rsv"
|
||||
#endif
|
||||
|
||||
static unsigned char *roomsave_dirty = NULL;
|
||||
|
||||
void RoomSave_init_dirty(void) {
|
||||
free(roomsave_dirty);
|
||||
roomsave_dirty = calloc((size_t)top_of_world + 1, 1);
|
||||
}
|
||||
|
||||
void RoomSave_mark_dirty_room(room_rnum rnum) {
|
||||
if (!roomsave_dirty) return;
|
||||
if (rnum != NOWHERE && rnum >= 0 && rnum <= top_of_world)
|
||||
roomsave_dirty[rnum] = 1;
|
||||
}
|
||||
|
||||
/* Where does an object “live” (topmost location -> room)? */
|
||||
room_rnum RoomSave_room_of_obj(struct obj_data *o) {
|
||||
if (!o) return NOWHERE;
|
||||
while (o->in_obj) o = o->in_obj;
|
||||
if (o->carried_by) return IN_ROOM(o->carried_by);
|
||||
if (o->worn_by) return IN_ROOM(o->worn_by);
|
||||
return o->in_room;
|
||||
}
|
||||
|
||||
/* --- helper: read a list of objects until '.' or 'E' and return the head --- */
|
||||
/* Context-aware implementation: stop_on_E = 1 for nested B..E, 0 for top-level. */
|
||||
static struct obj_data *roomsave_read_list_ctx(FILE *fl, int stop_on_E)
|
||||
{
|
||||
char line[256];
|
||||
struct obj_data *head = NULL, *tail = NULL;
|
||||
|
||||
while (fgets(line, sizeof(line), fl)) {
|
||||
if (line[0] == '.') {
|
||||
/* End of this list scope */
|
||||
break;
|
||||
}
|
||||
|
||||
if (stop_on_E && line[0] == 'E') {
|
||||
/* End of nested (B..E) scope */
|
||||
break;
|
||||
}
|
||||
|
||||
/* For top-level reads (stop_on_E==0), or any non-'O', push back
|
||||
so the outer #R reader can handle M/E/G/P or '.' */
|
||||
if (line[0] != 'O') {
|
||||
long back = -((long)strlen(line));
|
||||
fseek(fl, back, SEEK_CUR);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Parse object header: O vnum timer weight cost unused */
|
||||
int vnum, timer, weight, cost, unused_cost;
|
||||
if (sscanf(line, "O %d %d %d %d %d", &vnum, &timer, &weight, &cost, &unused_cost) != 5)
|
||||
continue;
|
||||
|
||||
/* IMPORTANT: read by VNUM (VIRTUAL), not real index */
|
||||
struct obj_data *obj = read_object((obj_vnum)vnum, VIRTUAL);
|
||||
if (!obj) {
|
||||
mudlog(NRM, LVL_IMMORT, TRUE, "RoomSave: read_object(vnum=%d) failed.", vnum);
|
||||
/* Skip to next object/header or end-of-scope */
|
||||
long backpos;
|
||||
while (fgets(line, sizeof(line), fl)) {
|
||||
if (line[0] == 'O' || line[0] == '.' || (stop_on_E && line[0] == 'E')) {
|
||||
backpos = -((long)strlen(line));
|
||||
fseek(fl, backpos, SEEK_CUR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Apply core scalars */
|
||||
GET_OBJ_TIMER(obj) = timer;
|
||||
GET_OBJ_WEIGHT(obj) = weight;
|
||||
GET_OBJ_COST(obj) = cost;
|
||||
GET_OBJ_COST_PER_DAY(obj) = 0;
|
||||
|
||||
/* Clear array flags so missing slots don't keep proto bits */
|
||||
#ifdef EF_ARRAY_MAX
|
||||
# ifdef GET_OBJ_EXTRA_AR
|
||||
for (int i = 0; i < EF_ARRAY_MAX; i++) GET_OBJ_EXTRA_AR(obj, i) = 0;
|
||||
# else
|
||||
for (int i = 0; i < EF_ARRAY_MAX; i++) GET_OBJ_EXTRA(obj)[i] = 0;
|
||||
# endif
|
||||
#endif
|
||||
#ifdef TW_ARRAY_MAX
|
||||
for (int i = 0; i < TW_ARRAY_MAX; i++) GET_OBJ_WEAR(obj)[i] = 0;
|
||||
#endif
|
||||
|
||||
/* Read per-object lines until next 'O' or '.' or 'E'(when nested) */
|
||||
long backpos;
|
||||
while (fgets(line, sizeof(line), fl)) {
|
||||
if (line[0] == 'V') {
|
||||
int idx, val;
|
||||
if (sscanf(line, "V %d %d", &idx, &val) == 2) {
|
||||
#ifdef NUM_OBJ_VAL_POSITIONS
|
||||
if (idx >= 0 && idx < NUM_OBJ_VAL_POSITIONS) GET_OBJ_VAL(obj, idx) = val;
|
||||
#else
|
||||
if (idx >= 0 && idx < 6) GET_OBJ_VAL(obj, idx) = val;
|
||||
#endif
|
||||
}
|
||||
continue;
|
||||
} else if (line[0] == 'X') { /* extra flags */
|
||||
int idx, val;
|
||||
if (sscanf(line, "X %d %d", &idx, &val) == 2) {
|
||||
#if defined(EF_ARRAY_MAX) && defined(GET_OBJ_EXTRA_AR)
|
||||
if (idx >= 0 && idx < EF_ARRAY_MAX) GET_OBJ_EXTRA_AR(obj, idx) = val;
|
||||
#elif defined(EF_ARRAY_MAX)
|
||||
if (idx >= 0 && idx < EF_ARRAY_MAX) GET_OBJ_EXTRA(obj)[idx] = val;
|
||||
#else
|
||||
if (idx == 0) GET_OBJ_EXTRA(obj) = val;
|
||||
#endif
|
||||
}
|
||||
continue;
|
||||
} else if (line[0] == 'W') { /* wear flags */
|
||||
int idx, val;
|
||||
if (sscanf(line, "W %d %d", &idx, &val) == 2) {
|
||||
#ifdef TW_ARRAY_MAX
|
||||
if (idx >= 0 && idx < TW_ARRAY_MAX) GET_OBJ_WEAR(obj)[idx] = val;
|
||||
#else
|
||||
if (idx == 0) GET_OBJ_WEAR(obj) = val;
|
||||
#endif
|
||||
}
|
||||
continue;
|
||||
} else if (line[0] == 'B') {
|
||||
/* Nested contents until matching 'E' */
|
||||
struct obj_data *child_head = roomsave_read_list_ctx(fl, 1 /* stop_on_E */);
|
||||
|
||||
/* Detach each node before obj_to_obj(), otherwise we lose siblings */
|
||||
for (struct obj_data *it = child_head, *next; it; it = next) {
|
||||
next = it->next_content; /* remember original sibling */
|
||||
it->next_content = NULL; /* detach from temp list */
|
||||
obj_to_obj(it, obj); /* push into container (LIFO) */
|
||||
}
|
||||
continue;
|
||||
} else if (line[0] == 'O' || line[0] == '.' || (stop_on_E && line[0] == 'E')) {
|
||||
/* Next object / end-of-scope: rewind one line for outer loop to see it */
|
||||
backpos = -((long)strlen(line));
|
||||
fseek(fl, backpos, SEEK_CUR);
|
||||
break;
|
||||
} else {
|
||||
/* ignore unknown lines */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Append to this scope's list */
|
||||
if (GET_OBJ_TYPE(obj) == ITEM_MONEY)
|
||||
update_money_obj(obj);
|
||||
|
||||
obj->next_content = NULL;
|
||||
if (!head) head = tail = obj;
|
||||
else { tail->next_content = obj; tail = obj; }
|
||||
}
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
/* Keep your existing one-arg API for callers: top-level semantics (stop_on_E = 0). */
|
||||
static struct obj_data *roomsave_read_list(FILE *fl)
|
||||
{
|
||||
return roomsave_read_list_ctx(fl, 0);
|
||||
}
|
||||
|
||||
/* ---------- Minimal line format ----------
|
||||
#R <vnum> <unix_time>
|
||||
O <vnum> <timer> <extra_flags> <wear_flags> <weight> <cost> <unused>
|
||||
V <i> <val[i]> ; repeated for all value slots present on this obj
|
||||
B ; begin contents of this object (container)
|
||||
E ; end contents of this object
|
||||
. ; end of room
|
||||
------------------------------------------ */
|
||||
|
||||
static void ensure_dir_exists(const char *path) {
|
||||
if (mkdir(path, 0775) == -1 && errno != EEXIST) {
|
||||
mudlog(CMP, LVL_IMMORT, TRUE, "SYSERR: roomsave mkdir(%s): %s", path, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
/* zone vnum for a given room rnum (e.g., 134 -> zone 1) */
|
||||
static int roomsave_zone_for_rnum(room_rnum rnum) {
|
||||
if (rnum == NOWHERE || rnum < 0 || rnum > top_of_world) return 0;
|
||||
zone_rnum znum = world[rnum].zone;
|
||||
if (znum < 0 || znum > top_of_zone_table) return 0;
|
||||
return zone_table[znum].number; /* e.g., 1 for rooms 100–199, 2 for 200–299, etc. */
|
||||
}
|
||||
|
||||
/* lib/world/rsv/<zone>.rsv */
|
||||
static void roomsave_zone_filename(int zone_vnum, char *out, size_t outsz) {
|
||||
snprintf(out, outsz, "%s%d%s", ROOMSAVE_PREFIX, zone_vnum, ROOMSAVE_EXT);
|
||||
}
|
||||
|
||||
/* Write one object (and its recursive contents) */
|
||||
static void write_one_object(FILE *fl, struct obj_data *obj) {
|
||||
int i;
|
||||
|
||||
/* Core scalars (flags printed separately per-slot) */
|
||||
fprintf(fl, "O %d %d %d %d %d\n",
|
||||
GET_OBJ_VNUM(obj),
|
||||
GET_OBJ_TIMER(obj),
|
||||
GET_OBJ_WEIGHT(obj),
|
||||
GET_OBJ_COST(obj),
|
||||
GET_OBJ_COST_PER_DAY(obj));
|
||||
|
||||
/* Extra flags array */
|
||||
#if defined(EF_ARRAY_MAX) && defined(GET_OBJ_EXTRA_AR)
|
||||
for (i = 0; i < EF_ARRAY_MAX; i++)
|
||||
fprintf(fl, "X %d %d\n", i, GET_OBJ_EXTRA_AR(obj, i));
|
||||
#elif defined(EF_ARRAY_MAX)
|
||||
for (i = 0; i < EF_ARRAY_MAX; i++)
|
||||
fprintf(fl, "X %d %d\n", i, GET_OBJ_EXTRA(obj)[i]);
|
||||
#else
|
||||
fprintf(fl, "X %d %d\n", 0, GET_OBJ_EXTRA(obj));
|
||||
#endif
|
||||
|
||||
/* Wear flags array */
|
||||
#ifdef TW_ARRAY_MAX
|
||||
for (i = 0; i < TW_ARRAY_MAX; i++)
|
||||
fprintf(fl, "W %d %d\n", i, GET_OBJ_WEAR(obj)[i]);
|
||||
#else
|
||||
fprintf(fl, "W %d %d\n", 0, GET_OBJ_WEAR(obj));
|
||||
#endif
|
||||
|
||||
/* Values[] (durability, liquids, charges, etc.) */
|
||||
#ifdef NUM_OBJ_VAL_POSITIONS
|
||||
for (i = 0; i < NUM_OBJ_VAL_POSITIONS; i++)
|
||||
fprintf(fl, "V %d %d\n", i, GET_OBJ_VAL(obj, i));
|
||||
#else
|
||||
for (i = 0; i < 6; i++)
|
||||
fprintf(fl, "V %d %d\n", i, GET_OBJ_VAL(obj, i));
|
||||
#endif
|
||||
|
||||
/* Contents (recursive) */
|
||||
if (obj->contains) {
|
||||
struct obj_data *cont;
|
||||
fprintf(fl, "B\n");
|
||||
for (cont = obj->contains; cont; cont = cont->next_content)
|
||||
write_one_object(fl, cont);
|
||||
fprintf(fl, "E\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Forward declaration for RoomSave_now*/
|
||||
static void RS_write_room_mobs(FILE *out, room_rnum rnum);
|
||||
|
||||
/* Public: write the entire room’s contents */
|
||||
int RoomSave_now(room_rnum rnum) {
|
||||
char path[PATH_MAX], tmp[PATH_MAX], line[512];
|
||||
FILE *in = NULL, *out = NULL;
|
||||
room_vnum rvnum;
|
||||
int zvnum;
|
||||
|
||||
if (rnum == NOWHERE)
|
||||
return 0;
|
||||
|
||||
rvnum = world[rnum].number;
|
||||
zvnum = roomsave_zone_for_rnum(rnum);
|
||||
if (zvnum < 0)
|
||||
return 0;
|
||||
|
||||
ensure_dir_exists(ROOMSAVE_PREFIX);
|
||||
roomsave_zone_filename(zvnum, path, sizeof(path));
|
||||
|
||||
{
|
||||
int n = snprintf(tmp, sizeof(tmp), "%s.tmp", path);
|
||||
if (n < 0 || n >= (int)sizeof(tmp)) {
|
||||
mudlog(NRM, LVL_IMMORT, TRUE,
|
||||
"SYSERR: RoomSave: temp path too long for %s", path);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(out = fopen(tmp, "w"))) {
|
||||
mudlog(NRM, LVL_IMMORT, TRUE,
|
||||
"SYSERR: RoomSave: fopen(%s) failed: %s",
|
||||
tmp, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((in = fopen(path, "r")) != NULL) {
|
||||
while (fgets(line, sizeof(line), in)) {
|
||||
if (strncmp(line, "#R ", 3) == 0) {
|
||||
int file_rvnum;
|
||||
long ts;
|
||||
if (sscanf(line, "#R %d %ld", &file_rvnum, &ts) == 2) {
|
||||
if (file_rvnum == (int)rvnum) {
|
||||
/* Skip old block completely until and including '.' line */
|
||||
while (fgets(line, sizeof(line), in)) {
|
||||
if (line[0] == '.') {
|
||||
/* consume it and break */
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue; /* do NOT write skipped lines */
|
||||
}
|
||||
}
|
||||
}
|
||||
fputs(line, out); /* keep unrelated lines */
|
||||
}
|
||||
fclose(in);
|
||||
}
|
||||
|
||||
/* Append new block */
|
||||
fprintf(out, "#R %d %ld\n", rvnum, (long)time(0));
|
||||
|
||||
RS_write_room_mobs(out, rnum);
|
||||
|
||||
for (struct obj_data *obj = world[rnum].contents; obj; obj = obj->next_content)
|
||||
write_one_object(out, obj);
|
||||
|
||||
/* Always terminate block */
|
||||
fprintf(out, ".\n");
|
||||
|
||||
if (fclose(out) != 0) {
|
||||
mudlog(NRM, LVL_IMMORT, TRUE,
|
||||
"SYSERR: RoomSave: fclose(%s) failed: %s",
|
||||
tmp, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
if (rename(tmp, path) != 0) {
|
||||
mudlog(NRM, LVL_IMMORT, TRUE,
|
||||
"SYSERR: RoomSave: rename(%s -> %s) failed: %s",
|
||||
tmp, path, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* --- M/E/G/P load helpers (mob restore) -------------------------------- */
|
||||
|
||||
struct rs_load_ctx {
|
||||
room_rnum rnum;
|
||||
struct char_data *cur_mob; /* last mob spawned by 'M' */
|
||||
struct obj_data *stack[16]; /* container stack by depth for 'P' */
|
||||
};
|
||||
|
||||
static struct obj_data *RS_create_obj_by_vnum(obj_vnum ov) {
|
||||
obj_rnum ornum;
|
||||
if (ov <= 0) return NULL;
|
||||
ornum = real_object(ov);
|
||||
if (ornum == NOTHING) return NULL;
|
||||
return read_object(ornum, REAL);
|
||||
}
|
||||
|
||||
static struct char_data *RS_create_mob_by_vnum(mob_vnum mv) {
|
||||
mob_rnum mrnum;
|
||||
if (mv <= 0) return NULL;
|
||||
mrnum = real_mobile(mv);
|
||||
if (mrnum == NOBODY) return NULL;
|
||||
return read_mobile(mrnum, REAL);
|
||||
}
|
||||
|
||||
|
||||
/* Reset the loader context before reading a new #R block */
|
||||
static void RS_ctx_clear(struct rs_load_ctx *ctx) {
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
/* DO NOT reset ctx->rnum — each #R block sets this explicitly
|
||||
* before parsing mobs or objects. Resetting it causes cross-room
|
||||
* bleed (e.g., mobs from one room spawning in another).
|
||||
*/
|
||||
|
||||
ctx->cur_mob = NULL;
|
||||
|
||||
/* Clear all container stack pointers */
|
||||
for (int i = 0; i < 16; i++)
|
||||
ctx->stack[i] = NULL;
|
||||
}
|
||||
|
||||
/* Optional autosave hook (invoked by limits.c:point_update). */
|
||||
void RoomSave_autosave_tick(void) {
|
||||
/* Iterate all rooms; only save flagged ones. */
|
||||
for (room_rnum rnum = 0; rnum <= top_of_world; ++rnum) {
|
||||
if (ROOM_FLAGGED(rnum, ROOM_SAVE))
|
||||
RoomSave_now(rnum);
|
||||
}
|
||||
}
|
||||
|
||||
/* Forward decl so RS_parse_mob_line can use it without implicit declaration */
|
||||
static void RS_stack_clear(struct rs_load_ctx *ctx);
|
||||
|
||||
/* Handle one line inside a #R block. Returns 1 if handled here. */
|
||||
static int RS_parse_mob_line(struct rs_load_ctx *ctx, char *line)
|
||||
{
|
||||
if (!line) return 0;
|
||||
while (*line == ' ' || *line == '\t') ++line;
|
||||
if (!*line) return 0;
|
||||
|
||||
switch (line[0]) {
|
||||
case 'M': {
|
||||
mob_vnum mv;
|
||||
if (sscanf(line+1, " %d", (int *)&mv) != 1) return 0;
|
||||
|
||||
ctx->cur_mob = RS_create_mob_by_vnum(mv);
|
||||
if (!ctx->cur_mob) return 1;
|
||||
|
||||
/* Place in the block's room */
|
||||
char_to_room(ctx->cur_mob, ctx->rnum);
|
||||
|
||||
/* Safety: if anything put it elsewhere, force it back */
|
||||
if (IN_ROOM(ctx->cur_mob) != ctx->rnum)
|
||||
char_to_room(ctx->cur_mob, ctx->rnum);
|
||||
|
||||
RS_stack_clear(ctx); /* clear only container stack */
|
||||
return 1;
|
||||
}
|
||||
|
||||
case 'E': { /* E <wear_pos> <obj_vnum> */
|
||||
int pos; obj_vnum ov; struct obj_data *obj;
|
||||
if (!ctx->cur_mob) return 1; /* orphan -> ignore */
|
||||
if (sscanf(line+1, " %d %d", &pos, (int *)&ov) != 2) return 0;
|
||||
obj = RS_create_obj_by_vnum(ov);
|
||||
if (!obj) return 1;
|
||||
|
||||
if (pos < 0 || pos >= NUM_WEARS) pos = WEAR_HOLD; /* clamp */
|
||||
equip_char(ctx->cur_mob, obj, pos);
|
||||
|
||||
/* Reset ONLY container stack for following P-lines; keep cur_mob */
|
||||
RS_stack_clear(ctx);
|
||||
ctx->stack[0] = obj;
|
||||
return 1;
|
||||
}
|
||||
|
||||
case 'G': { /* G <obj_vnum> */
|
||||
obj_vnum ov; struct obj_data *obj;
|
||||
if (!ctx->cur_mob) return 1; /* orphan -> ignore */
|
||||
if (sscanf(line+1, " %d", (int *)&ov) != 1) return 0;
|
||||
obj = RS_create_obj_by_vnum(ov);
|
||||
if (!obj) return 1;
|
||||
|
||||
obj_to_char(obj, ctx->cur_mob);
|
||||
|
||||
RS_stack_clear(ctx);
|
||||
ctx->stack[0] = obj;
|
||||
return 1;
|
||||
}
|
||||
|
||||
case 'P': { /* P <depth> <obj_vnum> : put into last obj at (depth-1) */
|
||||
int depth; obj_vnum ov; struct obj_data *parent, *obj;
|
||||
if (sscanf(line+1, " %d %d", &depth, (int *)&ov) != 2) return 0;
|
||||
if (depth <= 0 || depth >= (int)(sizeof(ctx->stack)/sizeof(ctx->stack[0])))
|
||||
return 1;
|
||||
parent = ctx->stack[depth-1];
|
||||
if (!parent) return 1;
|
||||
|
||||
obj = RS_create_obj_by_vnum(ov);
|
||||
if (!obj) return 1;
|
||||
obj_to_obj(obj, parent);
|
||||
|
||||
ctx->stack[depth] = obj;
|
||||
{ int d; for (d = depth+1; d < (int)(sizeof(ctx->stack)/sizeof(ctx->stack[0])); ++d) ctx->stack[d] = NULL; }
|
||||
return 1;
|
||||
}
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Forward decls for mob restore helpers */
|
||||
static void RS_stack_clear(struct rs_load_ctx *ctx);
|
||||
|
||||
void RoomSave_boot(void)
|
||||
{
|
||||
DIR *dirp;
|
||||
struct dirent *dp;
|
||||
|
||||
ensure_dir_exists(ROOMSAVE_PREFIX);
|
||||
|
||||
dirp = opendir(ROOMSAVE_PREFIX);
|
||||
if (!dirp) {
|
||||
mudlog(NRM, LVL_IMMORT, TRUE,
|
||||
"SYSERR: RoomSave_boot: cannot open %s", ROOMSAVE_PREFIX);
|
||||
return;
|
||||
}
|
||||
|
||||
log("RoomSave: scanning %s for *.rsv", ROOMSAVE_PREFIX);
|
||||
|
||||
while ((dp = readdir(dirp))) {
|
||||
size_t n = strlen(dp->d_name);
|
||||
if (n < 5) continue; /* skip . and .. */
|
||||
if (strcmp(dp->d_name + n - 4, ROOMSAVE_EXT) != 0) continue;
|
||||
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
int wn = snprintf(path, sizeof(path), "%s%s", ROOMSAVE_PREFIX, dp->d_name);
|
||||
if (wn < 0 || wn >= (int)sizeof(path)) {
|
||||
mudlog(NRM, LVL_IMMORT, TRUE,
|
||||
"SYSERR: RoomSave_boot: path too long: %s%s",
|
||||
ROOMSAVE_PREFIX, dp->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
FILE *fl = fopen(path, "r");
|
||||
if (!fl) {
|
||||
mudlog(NRM, LVL_IMMORT, TRUE,
|
||||
"SYSERR: RoomSave_boot: fopen(%s) failed: %s",
|
||||
path, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
log("RoomSave: reading %s", path);
|
||||
|
||||
int blocks = 0;
|
||||
int restored_objs_total = 0;
|
||||
int restored_mobs_total = 0;
|
||||
|
||||
/* Outer loop: read every #R block in this .rsv file */
|
||||
char line[512];
|
||||
while (fgets(line, sizeof(line), fl)) {
|
||||
|
||||
/* Skip until a valid #R header */
|
||||
if (strncmp(line, "#R ", 3) != 0)
|
||||
continue;
|
||||
|
||||
/* Parse header line */
|
||||
int rvnum; long ts;
|
||||
if (sscanf(line, "#R %d %ld", &rvnum, &ts) != 2) {
|
||||
mudlog(NRM, LVL_IMMORT, TRUE,
|
||||
"RoomSave: malformed #R header in %s: %s", path, line);
|
||||
/* Skip malformed block */
|
||||
while (fgets(line, sizeof(line), fl))
|
||||
if (line[0] == '.') break;
|
||||
continue;
|
||||
}
|
||||
|
||||
blocks++;
|
||||
|
||||
/* Resolve the room for this block */
|
||||
room_rnum rnum = real_room((room_vnum)rvnum);
|
||||
if (rnum == NOWHERE) {
|
||||
mudlog(NRM, LVL_IMMORT, FALSE,
|
||||
"RoomSave: unknown room vnum %d in %s (skipping)",
|
||||
rvnum, path);
|
||||
/* Skip to next block */
|
||||
while (fgets(line, sizeof(line), fl))
|
||||
if (line[0] == '.') break;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Clear this room's ground contents before restoring */
|
||||
while (world[rnum].contents)
|
||||
extract_obj(world[rnum].contents);
|
||||
|
||||
/* Clear and set mob context for this block */
|
||||
struct rs_load_ctx mctx;
|
||||
RS_ctx_clear(&mctx);
|
||||
mctx.rnum = rnum;
|
||||
|
||||
/* Per-block counts */
|
||||
int count_objs = 0, count_mobs = 0;
|
||||
char inner[512];
|
||||
|
||||
/* Inner loop: read this #R block until '.' */
|
||||
while (fgets(inner, sizeof(inner), fl)) {
|
||||
|
||||
/* Trim spaces */
|
||||
while (inner[0] == ' ' || inner[0] == '\t')
|
||||
memmove(inner, inner + 1, strlen(inner));
|
||||
|
||||
/* Stop at end of block */
|
||||
if (inner[0] == '.')
|
||||
break;
|
||||
|
||||
/* Defensive: stop if another #R starts (malformed file) */
|
||||
if (!strncmp(inner, "#R ", 3)) {
|
||||
fseek(fl, -((long)strlen(inner)), SEEK_CUR);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Handle object blocks */
|
||||
if (inner[0] == 'O') {
|
||||
long pos = ftell(fl);
|
||||
fseek(fl, pos - strlen(inner), SEEK_SET);
|
||||
struct obj_data *list = roomsave_read_list(fl);
|
||||
for (struct obj_data *it = list, *next; it; it = next) {
|
||||
next = it->next_content;
|
||||
it->next_content = NULL;
|
||||
obj_to_room(it, rnum);
|
||||
count_objs++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Handle mob & equipment/inventory */
|
||||
if (RS_parse_mob_line(&mctx, inner)) {
|
||||
if (inner[0] == 'M')
|
||||
count_mobs++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Unknown token: ignore gracefully */
|
||||
}
|
||||
|
||||
restored_objs_total += count_objs;
|
||||
restored_mobs_total += count_mobs;
|
||||
|
||||
if (count_mobs > 0)
|
||||
log("RoomSave: room %d <- %d object(s) and %d mob(s)",
|
||||
rvnum, count_objs, count_mobs);
|
||||
else
|
||||
log("RoomSave: room %d <- %d object(s)", rvnum, count_objs);
|
||||
}
|
||||
|
||||
log("RoomSave: finished %s (blocks=%d, objects=%d, mobs=%d)",
|
||||
path, blocks, restored_objs_total, restored_mobs_total);
|
||||
|
||||
fclose(fl);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dirp);
|
||||
}
|
||||
|
||||
/* ======== MOB SAVE: write NPCs and their equipment/inventory ========== */
|
||||
|
||||
/* Depth-aware writer for container contents under a parent object.
|
||||
* Writes: P <depth> <obj_vnum>
|
||||
* depth starts at 1 for direct children. */
|
||||
static void RS_write_P_chain(FILE *fp, struct obj_data *parent, int depth) {
|
||||
struct obj_data *c;
|
||||
for (c = parent->contains; c; c = c->next_content) {
|
||||
obj_vnum cv = GET_OBJ_VNUM(c);
|
||||
if (cv <= 0) continue; /* skip non-proto / invalid */
|
||||
fprintf(fp, "P %d %d\n", depth, (int)cv);
|
||||
if (c->contains)
|
||||
RS_write_P_chain(fp, c, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Writes: E <wear_pos> <obj_vnum> (then P-chain) */
|
||||
static void RS_write_mob_equipment(FILE *fp, struct char_data *mob) {
|
||||
int w;
|
||||
for (w = 0; w < NUM_WEARS; ++w) {
|
||||
struct obj_data *eq = GET_EQ(mob, w);
|
||||
if (!eq) continue;
|
||||
if (GET_OBJ_VNUM(eq) <= 0) continue;
|
||||
fprintf(fp, "E %d %d\n", w, (int)GET_OBJ_VNUM(eq));
|
||||
if (eq->contains) RS_write_P_chain(fp, eq, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Writes: G <obj_vnum> for inventory items (then P-chain) */
|
||||
static void RS_write_mob_inventory(FILE *fp, struct char_data *mob) {
|
||||
struct obj_data *o;
|
||||
for (o = mob->carrying; o; o = o->next_content) {
|
||||
if (GET_OBJ_VNUM(o) <= 0) continue;
|
||||
fprintf(fp, "G %d\n", (int)GET_OBJ_VNUM(o));
|
||||
if (o->contains) RS_write_P_chain(fp, o, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Top-level writer: for each NPC in room, emit:
|
||||
* M <mob_vnum>
|
||||
* [E ...]*
|
||||
* [G ...]*
|
||||
* (Players are ignored.) */
|
||||
static void RS_write_room_mobs(FILE *out, room_rnum rnum) {
|
||||
struct char_data *mob;
|
||||
for (mob = world[rnum].people; mob; mob = mob->next_in_room) {
|
||||
if (!IS_NPC(mob)) continue;
|
||||
if (GET_MOB_VNUM(mob) <= 0) continue;
|
||||
fprintf(out, "M %d\n", (int)GET_MOB_VNUM(mob));
|
||||
RS_write_mob_equipment(out, mob);
|
||||
RS_write_mob_inventory(out, mob);
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear only the container stack, NOT cur_mob */
|
||||
static void RS_stack_clear(struct rs_load_ctx *ctx) {
|
||||
int i;
|
||||
for (i = 0; i < (int)(sizeof(ctx->stack)/sizeof(ctx->stack[0])); ++i)
|
||||
ctx->stack[i] = NULL;
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
/**
|
||||
* @file roomsave.h
|
||||
* Room file loading/saving and utility headers.
|
||||
*
|
||||
* This set of code was not originally part of the circlemud distribution.
|
||||
*/
|
||||
|
||||
#ifndef ROOMSAVE_H_
|
||||
#define ROOMSAVE_H_
|
||||
|
||||
#include "conf.h"
|
||||
#include "sysdep.h"
|
||||
#include "structs.h"
|
||||
|
||||
/* Boot-time loader: scans ROOMSAVE_DIR and restores contents into rooms. */
|
||||
void RoomSave_boot(void);
|
||||
|
||||
/* Immediate save for a single room (room_rnum). Safe to call anytime. */
|
||||
/* saves ground objs AND NPCs (+their E/G/P trees) in rsv. */
|
||||
int RoomSave_now(room_rnum rnum);
|
||||
|
||||
/* Autosave pass for all rooms flagged ROOM_SAVE. */
|
||||
void RoomSave_autosave_tick(void);
|
||||
|
||||
/* Only save rooms when modified */
|
||||
void RoomSave_init_dirty(void);
|
||||
void RoomSave_mark_dirty_room(room_rnum rnum);
|
||||
/* For container edits: find the room an object ultimately lives in */
|
||||
room_rnum RoomSave_room_of_obj(struct obj_data *obj);
|
||||
|
||||
#endif /* ROOMSAVE_H_ */
|
||||
20
src/set.h
20
src/set.h
|
|
@ -8,10 +8,30 @@
|
|||
#ifndef SET_H
|
||||
#define SET_H
|
||||
|
||||
#include "structs.h"
|
||||
|
||||
ACMD(do_rset);
|
||||
ACMD(do_rcreate);
|
||||
ACMD(do_ocreate);
|
||||
ACMD(do_oset);
|
||||
ACMD(do_osave);
|
||||
ACMD(do_msave);
|
||||
ACMD(do_rsave);
|
||||
|
||||
/* Boot-time loader: scans ROOMSAVE_DIR and restores contents into rooms. */
|
||||
void RoomSave_boot(void);
|
||||
|
||||
/* Immediate save for a single room (room_rnum). Safe to call anytime. */
|
||||
/* saves ground objs AND NPCs (+their E/G/P trees) in rsv. */
|
||||
int RoomSave_now(room_rnum rnum);
|
||||
|
||||
/* Autosave pass for all rooms flagged ROOM_SAVE. */
|
||||
void RoomSave_autosave_tick(void);
|
||||
|
||||
/* Only save rooms when modified */
|
||||
void RoomSave_init_dirty(void);
|
||||
void RoomSave_mark_dirty_room(room_rnum rnum);
|
||||
/* For container edits: find the room an object ultimately lives in */
|
||||
room_rnum RoomSave_room_of_obj(struct obj_data *obj);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1386,6 +1386,7 @@ struct recent_player
|
|||
/* NPC loadout helpers */
|
||||
void loadout_free_list(struct mob_loadout **head);
|
||||
void loadout_add_entry(struct mob_loadout **head, obj_vnum vnum, sh_int wear_pos, int qty);
|
||||
void loadout_append_entry(struct mob_loadout **head, obj_vnum vnum, sh_int wear_pos, int qty);
|
||||
struct mob_loadout *loadout_deep_copy(const struct mob_loadout *src);
|
||||
|
||||
/* Furniture defines for object values */
|
||||
|
|
|
|||
18
src/utils.c
18
src/utils.c
|
|
@ -2151,6 +2151,24 @@ void loadout_add_entry(struct mob_loadout **head, obj_vnum vnum, sh_int wear_pos
|
|||
*head = e;
|
||||
}
|
||||
|
||||
void loadout_append_entry(struct mob_loadout **head, obj_vnum vnum, sh_int wear_pos, int qty) {
|
||||
struct mob_loadout *e = NULL;
|
||||
struct mob_loadout *tail;
|
||||
if (qty < 1) qty = 1;
|
||||
CREATE(e, struct mob_loadout, 1);
|
||||
e->vnum = vnum;
|
||||
e->wear_pos = wear_pos;
|
||||
e->quantity = qty;
|
||||
e->next = NULL;
|
||||
if (!*head) {
|
||||
*head = e;
|
||||
return;
|
||||
}
|
||||
for (tail = *head; tail->next; tail = tail->next)
|
||||
;
|
||||
tail->next = e;
|
||||
}
|
||||
|
||||
struct mob_loadout *loadout_deep_copy(const struct mob_loadout *src) {
|
||||
struct mob_loadout *head = NULL, *tail = NULL;
|
||||
for (const struct mob_loadout *p = src; p; p = p->next) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue