mirror of
https://github.com/tbamud/tbamud.git
synced 2026-03-20 11:16:33 +01:00
Add mob equipment save function
This commit is contained in:
parent
fd58fc5f13
commit
542b01d71d
10 changed files with 386 additions and 53 deletions
|
|
@ -16,6 +16,17 @@ Str: 16
|
|||
Dex: 14
|
||||
Con: 14
|
||||
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
|
||||
#101
|
||||
slim lanky human soldier guard~
|
||||
a slim, lanky human soldier~
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ padded armor~
|
|||
some padded armor~
|
||||
Some padded armor has been discarded here.~
|
||||
~
|
||||
9 0 0 0 0 ad 0 0 0 0 0 0 0
|
||||
9 0 0 0 0 ae 0 0 0 0 0 0 0
|
||||
1 1 0 0
|
||||
10 4 0 0 0
|
||||
#101
|
||||
|
|
@ -11,7 +11,7 @@ erdlu leather armor~
|
|||
some erdlu leather armor~
|
||||
An unfinished object is lying here.~
|
||||
~
|
||||
9 0 0 0 0 ad 0 0 0 0 0 0 0
|
||||
9 0 0 0 0 ae 0 0 0 0 0 0 0
|
||||
1 1 0 0
|
||||
5 5 0 0 0
|
||||
#102
|
||||
|
|
@ -19,7 +19,7 @@ studded leather jacket~
|
|||
a studded leather jacket~
|
||||
A jacket made from studded leather lies here.~
|
||||
~
|
||||
9 0 0 0 0 ad 0 0 0 0 0 0 0
|
||||
9 0 0 0 0 ae 0 0 0 0 0 0 0
|
||||
2 1 0 0
|
||||
13 10 0 0 0
|
||||
#103
|
||||
|
|
@ -27,7 +27,7 @@ bone chitin armor~
|
|||
some bone and chitin armor~
|
||||
A piece of armor made from bone and chitin lies here.~
|
||||
~
|
||||
9 0 0 0 0 ad 0 0 0 0 0 0 0
|
||||
9 0 0 0 0 ae 0 0 0 0 0 0 0
|
||||
1 1 0 0
|
||||
20 12 0 0 0
|
||||
#104
|
||||
|
|
@ -35,7 +35,7 @@ pair padded sleeves~
|
|||
a pair of padded sleeves~
|
||||
A pair of padded cloth sleeves lie here abandoned.~
|
||||
~
|
||||
9 0 0 0 0 ai 0 0 0 0 0 0 0
|
||||
9 0 0 0 0 aj 0 0 0 0 0 0 0
|
||||
1 1 0 0
|
||||
5 10 0 0 0
|
||||
#105
|
||||
|
|
@ -43,7 +43,7 @@ pair padded leggings~
|
|||
a pair of padded leggings~
|
||||
A pair of padded leggings lie here in the dust.~
|
||||
~
|
||||
9 0 0 0 0 af 0 0 0 0 0 0 0
|
||||
9 0 0 0 0 ag 0 0 0 0 0 0 0
|
||||
1 1 0 0
|
||||
5 10 0 0 0
|
||||
#106
|
||||
|
|
@ -51,7 +51,7 @@ pair cloth gloves~
|
|||
a pair of cloth gloves~
|
||||
A pair of yellowed cloth gloves lie here.~
|
||||
~
|
||||
11 0 0 0 0 ah 0 0 0 0 0 0 0
|
||||
11 0 0 0 0 ai 0 0 0 0 0 0 0
|
||||
0 0 0 0
|
||||
2 10 0 0 0
|
||||
#107
|
||||
|
|
@ -59,7 +59,7 @@ pair leather gloves~
|
|||
a pair of leather gloves~
|
||||
A pair of thick leather gloves have been left here.~
|
||||
~
|
||||
9 0 0 0 0 ah 0 0 0 0 0 0 0
|
||||
9 0 0 0 0 ai 0 0 0 0 0 0 0
|
||||
1 1 0 0
|
||||
6 10 0 0 0
|
||||
#108
|
||||
|
|
@ -67,7 +67,7 @@ bone helmet~
|
|||
a bone helmet~
|
||||
A helmet made of bone has been discarded here.~
|
||||
~
|
||||
9 0 0 0 0 ae 0 0 0 0 0 0 0
|
||||
9 0 0 0 0 af 0 0 0 0 0 0 0
|
||||
1 1 0 0
|
||||
12 18 0 0 0
|
||||
#109
|
||||
|
|
@ -75,7 +75,7 @@ padded cloth helmet~
|
|||
a padded cloth helmet~
|
||||
A padded cloth helmet has been left here.~
|
||||
~
|
||||
9 0 0 0 0 ae 0 0 0 0 0 0 0
|
||||
9 0 0 0 0 af 0 0 0 0 0 0 0
|
||||
1 1 0 0
|
||||
4 11 0 0 0
|
||||
#110
|
||||
|
|
@ -83,7 +83,7 @@ braxat hide jacket~
|
|||
a braxat hide jacket~
|
||||
A thick jacket made of braxat hide has been abandoned here.~
|
||||
~
|
||||
9 0 0 0 0 ad 0 0 0 0 0 0 0
|
||||
9 0 0 0 0 ae 0 0 0 0 0 0 0
|
||||
3 3 0 1
|
||||
30 250 0 0 0
|
||||
#111
|
||||
|
|
@ -91,7 +91,7 @@ pair thick leather sleeves~
|
|||
a pair of thick leather sleeves~
|
||||
Cured and stitched tight, a pair of thick leather sleeves are here.~
|
||||
~
|
||||
9 0 0 0 0 ai 0 0 0 0 0 0 0
|
||||
9 0 0 0 0 aj 0 0 0 0 0 0 0
|
||||
1 1 0 0
|
||||
14 30 0 0 0
|
||||
#112
|
||||
|
|
@ -99,7 +99,7 @@ pair sandals~
|
|||
a pair of sandals~
|
||||
Some cheap looking sandals have been left in the dust here.~
|
||||
~
|
||||
11 0 0 0 0 ag 0 0 0 0 0 0 0
|
||||
11 0 0 0 0 ah 0 0 0 0 0 0 0
|
||||
0 0 0 0
|
||||
1 8 0 0 0
|
||||
#113
|
||||
|
|
@ -107,7 +107,7 @@ loincloth~
|
|||
a loincloth~
|
||||
A scrap of cloth with a string attached has been left here.~
|
||||
~
|
||||
11 0 0 0 0 al 0 0 0 0 0 0 0
|
||||
11 0 0 0 0 am 0 0 0 0 0 0 0
|
||||
0 0 0 0
|
||||
1 1 0 0 0
|
||||
#114
|
||||
|
|
@ -115,7 +115,7 @@ erdlu scale shield~
|
|||
an erdlu scale shield~
|
||||
A shield made from erdlu scales lies here, collecting dust.~
|
||||
~
|
||||
9 0 0 0 0 aj 0 0 0 0 0 0 0
|
||||
9 0 0 0 0 ak 0 0 0 0 0 0 0
|
||||
0 0 0 0
|
||||
5 50 0 0 0
|
||||
#115
|
||||
|
|
@ -123,7 +123,7 @@ pair thick leather leggings~
|
|||
a pair of thick leather leggings~
|
||||
A pair of leggings made from thick, dark leather are here.~
|
||||
~
|
||||
9 0 0 0 0 af 0 0 0 0 0 0 0
|
||||
9 0 0 0 0 ag 0 0 0 0 0 0 0
|
||||
1 1 0 0
|
||||
12 50 0 0 0
|
||||
#116
|
||||
|
|
@ -131,7 +131,7 @@ hide wrist wrap wrist-wrap~
|
|||
a hide wrist-wrap~
|
||||
A simple hide wrist-wrap gathers dust and sand here.~
|
||||
~
|
||||
9 0 0 0 0 am 0 0 0 0 0 0 0
|
||||
9 0 0 0 0 an 0 0 0 0 0 0 0
|
||||
1 1 0 0
|
||||
2 25 0 0 0
|
||||
#117
|
||||
|
|
@ -139,7 +139,7 @@ studded hide wrist wrap wrist-wrap~
|
|||
a studded hide wrist-wrap~
|
||||
A hide wrist-wrap with studded bone bits has been left here.~
|
||||
~
|
||||
9 0 0 0 0 am 0 0 0 0 0 0 0
|
||||
9 0 0 0 0 an 0 0 0 0 0 0 0
|
||||
1 1 0 0
|
||||
8 40 0 0 0
|
||||
#118
|
||||
|
|
@ -155,7 +155,7 @@ pair padded shoes~
|
|||
a pair of padded shoes~
|
||||
An assuming pair of padded cloth shoes are here.~
|
||||
~
|
||||
9 0 0 0 0 ag 0 0 0 0 0 0 0
|
||||
9 0 0 0 0 ah 0 0 0 0 0 0 0
|
||||
1 1 0 0
|
||||
8 25 0 0 0
|
||||
#120
|
||||
|
|
@ -163,7 +163,7 @@ black belt~
|
|||
a black belt~
|
||||
A belt made of black cloth lies here.~
|
||||
~
|
||||
15 0 0 0 0 al 0 0 0 0 0 0 0
|
||||
15 0 0 0 0 am 0 0 0 0 0 0 0
|
||||
10 0 0 0
|
||||
1 25 0 0 0
|
||||
#121
|
||||
|
|
@ -187,7 +187,7 @@ pair boots~
|
|||
a pair of boots~
|
||||
A pair of simple leather boots are here.~
|
||||
~
|
||||
9 0 0 0 0 ag 0 0 0 0 0 0 0
|
||||
9 0 0 0 0 ah 0 0 0 0 0 0 0
|
||||
1 1 0 0
|
||||
1 25 0 0 0
|
||||
#124
|
||||
|
|
@ -195,7 +195,7 @@ pair thick leather boots~
|
|||
a pair of thick leather boots~
|
||||
Boots made of thick leather have been left here.~
|
||||
~
|
||||
9 0 0 0 0 ag 0 0 0 0 0 0 0
|
||||
9 0 0 0 0 ah 0 0 0 0 0 0 0
|
||||
2 2 0 0
|
||||
5 50 0 0 0
|
||||
#125
|
||||
|
|
@ -203,7 +203,7 @@ bone shortsword sword~
|
|||
a bone shortsword~
|
||||
Made of bone, a shortsword has been left here.~
|
||||
~
|
||||
5 0 0 0 0 ano 0 0 0 0 0 0 0
|
||||
5 0 0 0 0 aop 0 0 0 0 0 0 0
|
||||
1 6 11 0
|
||||
5 50 0 0 0
|
||||
#126
|
||||
|
|
@ -211,7 +211,7 @@ bone dagger~
|
|||
a bone dagger~
|
||||
A dagger made of bone lies here abandoned.~
|
||||
~
|
||||
5 0 0 0 0 ano 0 0 0 0 0 0 0
|
||||
5 0 0 0 0 aop 0 0 0 0 0 0 0
|
||||
1 4 11 0
|
||||
2 25 0 0 0
|
||||
#127
|
||||
|
|
@ -219,7 +219,7 @@ bone longsword sword~
|
|||
a bone longsword~
|
||||
Long and slender, a sword lies here.~
|
||||
~
|
||||
5 0 0 0 0 an 0 0 0 0 0 0 0
|
||||
5 0 0 0 0 ao 0 0 0 0 0 0 0
|
||||
1 8 3 0
|
||||
8 100 0 0 0
|
||||
#128
|
||||
|
|
@ -227,7 +227,7 @@ bone club~
|
|||
a bone club~
|
||||
Made of bone, a club is lying here.~
|
||||
~
|
||||
5 0 0 0 0 ano 0 0 0 0 0 0 0
|
||||
5 0 0 0 0 aop 0 0 0 0 0 0 0
|
||||
1 4 5 0
|
||||
5 50 0 0 0
|
||||
#129
|
||||
|
|
@ -235,7 +235,7 @@ bone javelin~
|
|||
a bone javelin~
|
||||
A long, bone javelin has been left here.~
|
||||
~
|
||||
5 0 0 0 0 an 0 0 0 0 0 0 0
|
||||
5 0 0 0 0 ao 0 0 0 0 0 0 0
|
||||
1 6 11 0
|
||||
5 50 0 0 0
|
||||
#130
|
||||
|
|
@ -243,7 +243,7 @@ bone spear~
|
|||
a bone spear~
|
||||
A long piece of bones with a sharpened edge has been left here.~
|
||||
~
|
||||
5 0 0 0 0 an 0 0 0 0 0 0 0
|
||||
5 0 0 0 0 ao 0 0 0 0 0 0 0
|
||||
1 6 11 0
|
||||
3 50 0 0 0
|
||||
#131
|
||||
|
|
|
|||
|
|
@ -3,15 +3,5 @@ None.~
|
|||
City of Tyr~
|
||||
100 199 30 2
|
||||
M 0 100 1 100 (the tall, burly human soldier)
|
||||
E 1 102 1 6 (a studded leather jacket)
|
||||
E 1 115 1 8 (a pair of thick leather leggings)
|
||||
E 1 111 1 11 (a pair of thick leather sleeves)
|
||||
E 1 108 1 7 (a bone helmet)
|
||||
E 1 117 2 16 (a studded hide wrist-wrap)
|
||||
E 1 118 1 3 (a padded neckguard)
|
||||
E 1 117 2 15 (a studded hide wrist-wrap)
|
||||
E 1 107 1 10 (a pair of leather gloves)
|
||||
E 1 124 1 9 (a pair of thick leather boots)
|
||||
E 1 127 1 17 (a bone longsword)
|
||||
S
|
||||
$
|
||||
|
|
|
|||
|
|
@ -318,6 +318,7 @@ ACMD(do_goto);
|
|||
ACMD(do_invis);
|
||||
ACMD(do_links);
|
||||
ACMD(do_load);
|
||||
ACMD(do_msave);
|
||||
ACMD(do_oset);
|
||||
ACMD(do_peace);
|
||||
ACMD(do_plist);
|
||||
|
|
|
|||
142
src/act.wizard.c
142
src/act.wizard.c
|
|
@ -26,6 +26,7 @@
|
|||
#include "act.h"
|
||||
#include "genzon.h" /* for real_zone_by_thing */
|
||||
#include "class.h"
|
||||
#include "genmob.h"
|
||||
#include "genolc.h"
|
||||
#include "genobj.h"
|
||||
#include "fight.h"
|
||||
|
|
@ -5733,4 +5734,145 @@ ACMD(do_acaudit)
|
|||
#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), (sh_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);
|
||||
}
|
||||
131
src/db.c
131
src/db.c
|
|
@ -1862,14 +1862,53 @@ void parse_mobile(FILE *mob_f, int nr)
|
|||
exit(1);
|
||||
}
|
||||
|
||||
/* DG triggers -- script info follows mob S/E section */
|
||||
letter = fread_letter(mob_f);
|
||||
while (letter == 'L') {
|
||||
int wpos = -1, vnum = -1, qty = 1;
|
||||
/* read rest of the line AFTER the leading 'L' */
|
||||
if (!get_line(mob_f, line)) {
|
||||
log("SYSERR: Unexpected EOF while reading 'L' line in mob #%d.", nr);
|
||||
break;
|
||||
}
|
||||
/* parse "<wear_pos> <obj_vnum> [qty]" from the line buffer */
|
||||
int nread = sscanf(line, "%d %d %d", &wpos, &vnum, &qty);
|
||||
if (nread < 2) {
|
||||
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, (sh_int)wpos, qty);
|
||||
}
|
||||
/* look ahead to see if there is another 'L' */
|
||||
letter = fread_letter(mob_f);
|
||||
}
|
||||
ungetc(letter, mob_f);
|
||||
while (letter=='T') {
|
||||
|
||||
/* ---- DG triggers: script info follows mob S/E section ---- */
|
||||
letter = fread_letter(mob_f);
|
||||
while (letter == 'T') {
|
||||
dg_read_trigger(mob_f, &mob_proto[i], MOB_TRIGGER);
|
||||
letter = fread_letter(mob_f);
|
||||
ungetc(letter, mob_f);
|
||||
}
|
||||
ungetc(letter, mob_f);
|
||||
|
||||
/* ---- And allow loadout lines AFTER triggers, too ---- */
|
||||
letter = fread_letter(mob_f);
|
||||
while (letter == 'L') {
|
||||
int wpos = -1, vnum = -1, qty = 1;
|
||||
if (!get_line(mob_f, line)) {
|
||||
log("SYSERR: Unexpected EOF while reading post-trigger 'L' line in mob #%d.", nr);
|
||||
break;
|
||||
}
|
||||
int nread = sscanf(line, "%d %d %d", &wpos, &vnum, &qty);
|
||||
if (nread < 2) {
|
||||
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, (sh_int)wpos, qty);
|
||||
}
|
||||
letter = fread_letter(mob_f);
|
||||
}
|
||||
ungetc(letter, mob_f);
|
||||
|
||||
mob_proto[i].aff_abils = mob_proto[i].real_abils;
|
||||
|
||||
|
|
@ -2419,6 +2458,88 @@ void new_mobile_data(struct char_data *ch)
|
|||
ch->group = NULL;
|
||||
}
|
||||
|
||||
/* ========== Mob Loadout Auto-Equip ========== */
|
||||
static int find_alt_slot_same_family(struct char_data *ch, int intended_pos);
|
||||
|
||||
/* Equip items the prototype says to wear, in those exact slots when possible. */
|
||||
void equip_mob_from_loadout(struct char_data *mob)
|
||||
{
|
||||
if (!mob || !IS_NPC(mob)) return;
|
||||
|
||||
mob_rnum rnum = GET_MOB_RNUM(mob);
|
||||
if (rnum < 0) return;
|
||||
|
||||
const struct mob_loadout *e = mob_proto[rnum].proto_loadout;
|
||||
if (!e) return;
|
||||
|
||||
for (; e; e = e->next) {
|
||||
int qty = (e->quantity > 0) ? e->quantity : 1;
|
||||
|
||||
for (int n = 0; n < qty; n++) {
|
||||
struct obj_data *obj = read_object(e->vnum, VIRTUAL);
|
||||
if (!obj) {
|
||||
log("SYSERR: equip_mob_from_loadout: bad obj vnum %d on mob %d",
|
||||
e->vnum, GET_MOB_VNUM(mob));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Inventory-only request */
|
||||
if (e->wear_pos < 0) {
|
||||
obj_to_char(obj, mob);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
|
||||
#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;
|
||||
}
|
||||
#else
|
||||
equip_char(mob, obj, alt);
|
||||
continue;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Couldn’t place it — keep in inventory. */
|
||||
obj_to_char(obj, mob);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Minimal “same family” alternates for symmetrical pairs. Extend if you have more. */
|
||||
static int find_alt_slot_same_family(struct char_data *ch, int intended_pos)
|
||||
{
|
||||
switch (intended_pos) {
|
||||
case WEAR_FINGER_R: return WEAR_FINGER_L;
|
||||
case WEAR_FINGER_L: return WEAR_FINGER_R;
|
||||
case WEAR_NECK_1: return WEAR_NECK_2;
|
||||
case WEAR_NECK_2: return WEAR_NECK_1;
|
||||
case WEAR_WRIST_R: return WEAR_WRIST_L;
|
||||
case WEAR_WRIST_L: return WEAR_WRIST_R;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* create a new mobile from a prototype */
|
||||
struct char_data *read_mobile(mob_vnum nr, int type) /* and mob_rnum */
|
||||
|
|
@ -2438,6 +2559,7 @@ struct char_data *read_mobile(mob_vnum nr, int type) /* and mob_rnum */
|
|||
clear_char(mob);
|
||||
|
||||
*mob = mob_proto[i];
|
||||
mob->proto_loadout = NULL; /* Instances should not directly point at prototype’s loadout list */
|
||||
mob->next = character_list;
|
||||
character_list = mob;
|
||||
|
||||
|
|
@ -2461,6 +2583,9 @@ struct char_data *read_mobile(mob_vnum nr, int type) /* and mob_rnum */
|
|||
|
||||
mob->script_id = 0; // this is set later by char_script_id
|
||||
|
||||
/* Equip/load items from prototype loadout before scripts fire */
|
||||
equip_mob_from_loadout(mob);
|
||||
|
||||
copy_proto_script(&mob_proto[i], mob, MOB_TRIGGER);
|
||||
assign_triggers(mob, MOB_TRIGGER);
|
||||
|
||||
|
|
|
|||
33
src/genmob.c
33
src/genmob.c
|
|
@ -380,9 +380,9 @@ int write_mobile_record(mob_vnum mvnum, struct char_data *mob, FILE *fd)
|
|||
ddesc, STRING_TERMINATOR
|
||||
);
|
||||
|
||||
if(n < MAX_STRING_LENGTH) {
|
||||
if (n < MAX_STRING_LENGTH) {
|
||||
fprintf(fd, "%s", convert_from_tabs(buf));
|
||||
|
||||
|
||||
fprintf(fd, "%d %d %d %d %d %d %d %d %d E\n"
|
||||
"%d %d %d %dd%d+%d %dd%d+%d\n",
|
||||
MOB_FLAGS(mob)[0], MOB_FLAGS(mob)[1],
|
||||
|
|
@ -393,29 +393,38 @@ int write_mobile_record(mob_vnum mvnum, struct char_data *mob, FILE *fd)
|
|||
GET_LEVEL(mob), 20 - GET_HITROLL(mob), GET_AC(mob) / 10, GET_HIT(mob),
|
||||
GET_MANA(mob), GET_MOVE(mob), GET_NDD(mob), GET_SDD(mob),
|
||||
GET_DAMROLL(mob));
|
||||
|
||||
fprintf(fd, "%d %d\n"
|
||||
|
||||
fprintf(fd, "%d %d\n"
|
||||
"%d %d %d\n",
|
||||
GET_GOLD(mob), GET_EXP(mob),
|
||||
GET_POS(mob), GET_DEFAULT_POS(mob), GET_SEX(mob)
|
||||
);
|
||||
|
||||
|
||||
/* Write any E-specs */
|
||||
if (write_mobile_espec(mvnum, mob, fd) < 0)
|
||||
log("SYSERR: GenOLC: Error writing E-specs for mobile #%d.", mvnum);
|
||||
|
||||
|
||||
/* --- Persist prototype loadout lines (one per entry) --- */
|
||||
for (struct mob_loadout *e = mob->proto_loadout; e; e = e->next) {
|
||||
fprintf(fd, "L %d %d %d\n",
|
||||
(int)e->wear_pos,
|
||||
(int)e->vnum,
|
||||
MAX(1, e->quantity));
|
||||
}
|
||||
|
||||
/* DG scripts after loadout lines */
|
||||
script_save_to_disk(fd, mob, MOB_TRIGGER);
|
||||
|
||||
|
||||
#if CONFIG_GENOLC_MOBPROG
|
||||
|
||||
#if CONFIG_GENOLC_MOBPROG
|
||||
if (write_mobile_mobprog(mvnum, mob, fd) < 0)
|
||||
log("SYSERR: GenOLC: Error writing MobProgs for mobile #%d.", mvnum);
|
||||
#endif
|
||||
#endif
|
||||
} else {
|
||||
mudlog(BRF,LVL_BUILDER,TRUE,
|
||||
mudlog(BRF, LVL_BUILDER, TRUE,
|
||||
"SYSERR: Could not save mobile #%d due to size (%d > maximum of %d)",
|
||||
mvnum, n, MAX_STRING_LENGTH);
|
||||
}
|
||||
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -200,6 +200,7 @@ cpp_extern const struct command_info cmd_info[] = {
|
|||
{ "medit" , "med" , POS_DEAD , do_oasis_medit, LVL_BUILDER, 0 },
|
||||
{ "mlist" , "mlist" , POS_DEAD , do_oasis_list, LVL_BUILDER, SCMD_OASIS_MLIST },
|
||||
{ "mcopy" , "mcopy" , POS_DEAD , do_oasis_copy, LVL_GOD, CON_MEDIT },
|
||||
{ "msave" , "msav" , POS_DEAD , do_msave, LVL_BUILDER, 0 },
|
||||
{ "msgedit" , "msgedit" , POS_DEAD , do_msgedit, LVL_GOD, 0 },
|
||||
{ "mute" , "mute" , POS_DEAD , do_wizutil , LVL_GOD, SCMD_MUTE },
|
||||
|
||||
|
|
|
|||
|
|
@ -1025,6 +1025,14 @@ struct follow_type
|
|||
struct follow_type *next; /**< Next character following. */
|
||||
};
|
||||
|
||||
/* Handles items that NPC's are loaded with ahead of time */
|
||||
struct mob_loadout {
|
||||
obj_vnum vnum; /* item to clone */
|
||||
sh_int wear_pos; /* WEAR_* slot (or -1 for inventory) */
|
||||
int quantity; /* default 1; >1 for stackables or multiple clones */
|
||||
struct mob_loadout *next;
|
||||
};
|
||||
|
||||
/** Master structure for PCs and NPCs. */
|
||||
struct char_data
|
||||
{
|
||||
|
|
@ -1041,6 +1049,7 @@ struct char_data
|
|||
struct char_special_data char_specials; /**< PC/NPC specials */
|
||||
struct player_special_data *player_specials; /**< PC specials */
|
||||
struct mob_special_data mob_specials; /**< NPC specials */
|
||||
struct mob_loadout *proto_loadout; /* NPC objects equipped before loading NULL if none */
|
||||
|
||||
struct affected_type *affected; /**< affected by what spells */
|
||||
struct obj_data *equipment[NUM_WEARS]; /**< Equipment array */
|
||||
|
|
@ -1312,6 +1321,16 @@ struct recent_player
|
|||
#define GET_STEALTH_CHECK(ch) ((ch)->char_specials.stealth_check)
|
||||
#define SET_STEALTH_CHECK(ch,v) ((ch)->char_specials.stealth_check = (v))
|
||||
|
||||
/* NPC loadout macros */
|
||||
#define MOB_PROTO(ch) (&mob_proto[GET_MOB_RNUM(ch)]) /* Resolve proto from instance */
|
||||
#define MOB_LOADOUT_PROTO(m) ((m)->proto_loadout) /* Accessors, only prototypes should have a non-NULL list */
|
||||
#define MOB_HAS_LOADOUT(ch) (IS_NPC(ch) && (MOB_LOADOUT_PROTO(MOB_PROTO(ch)) != NULL)) /* */
|
||||
|
||||
/* 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);
|
||||
struct mob_loadout *loadout_deep_copy(const struct mob_loadout *src);
|
||||
|
||||
/* Config structs */
|
||||
|
||||
/** The game configuration structure used for configurating the game play
|
||||
|
|
|
|||
35
src/utils.c
35
src/utils.c
|
|
@ -1754,3 +1754,38 @@ int GET_SITUATIONAL_AC(struct char_data *ch)
|
|||
int compute_armor_class_asc(struct char_data *ch) {
|
||||
return compute_ascending_ac(ch);
|
||||
}
|
||||
|
||||
/* NPC loadout helpers */
|
||||
void loadout_free_list(struct mob_loadout **head) {
|
||||
struct mob_loadout *n, *p = *head;
|
||||
while (p) { n = p->next; free(p); p = n; }
|
||||
*head = NULL;
|
||||
}
|
||||
|
||||
void loadout_add_entry(struct mob_loadout **head, obj_vnum vnum, sh_int wear_pos, int qty) {
|
||||
struct mob_loadout *e = NULL;
|
||||
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;
|
||||
/* push-front for O(1); order doesn’t matter yet */
|
||||
e->next = *head;
|
||||
*head = 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) {
|
||||
struct mob_loadout *n;
|
||||
CREATE(n, struct mob_loadout, 1);
|
||||
n->vnum = p->vnum;
|
||||
n->wear_pos = p->wear_pos;
|
||||
n->quantity = p->quantity;
|
||||
n->next = NULL;
|
||||
if (!head) head = tail = n;
|
||||
else { tail->next = n; tail = n; }
|
||||
}
|
||||
return head;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue