From 3d96f7b8363009941898f411ff000bb149f23309 Mon Sep 17 00:00:00 2001 From: ahumbert Date: Mon, 17 Nov 2025 11:51:50 -0600 Subject: [PATCH 001/195] Re-Enable Shaping of Room Affections (#265) --- src/shaperom.cpp | 148 ++++++++++++++++++++--------------------------- 1 file changed, 62 insertions(+), 86 deletions(-) diff --git a/src/shaperom.cpp b/src/shaperom.cpp index e8e5a2ea..3f4d63cf 100644 --- a/src/shaperom.cpp +++ b/src/shaperom.cpp @@ -422,9 +422,10 @@ void shape_center_room(struct char_data* ch, char* arg) { char str[1000], tmpstr[1000]; - int tmp, choice, tmp1; + int tmp, choice, tmp1, tmp2, tmp3; struct room_data* mob; struct extra_descr_data* tmpdescr; + struct affected_type* tmpaf; // char key; // char * tmppt; @@ -901,100 +902,75 @@ void shape_center_room(struct char_data* ch, char* arg) break; case 18: - send_to_char("Not available at the moment\n\r", ch); - SHAPE_ROOM(ch) - ->editflag - = 0; + if(!mob->affected){ + send_to_char("No room affections found.\n\r",ch); + SHAPE_ROOM(ch) + ->editflag + = 0; + break; + } + if(!IS_SET(SHAPE_ROOM(ch)->flags,SHAPE_DIGIT_ACTIVE)){ + send_to_char("Describe room affection (four numbers, without commas):\n\r",ch); + SET_BIT(SHAPE_ROOM(ch)->flags,SHAPE_DIGIT_ACTIVE); + SHAPE_ROOM(ch)->position=shape_standup(ch,POSITION_SHAPING); + ch->specials.prompt_number=3; + return; + } + else{ + if(4!=sscanf(arg,"%d %d %d %d", + &tmp,&tmp1,&tmp2,&tmp3)){ + send_to_char("four numbers required. dropped\n\r",ch); + REMOVE_BIT(SHAPE_PROTO(ch)->flags,SHAPE_DIGIT_ACTIVE); + shape_standup(ch,SHAPE_ROOM(ch)->position); + ch->specials.prompt_number=4; + SHAPE_ROOM(ch)->editflag=0; + return; + } + } + mob->affected->type = tmp; + mob->affected->location = tmp1; + mob->affected->duration = -1; + mob->affected->modifier = tmp2; + mob->affected->bitvector = tmp3; + REMOVE_BIT(SHAPE_ROOM(ch)->flags,SHAPE_DIGIT_ACTIVE); + shape_standup(ch,SHAPE_ROOM(ch)->position); + ch->specials.prompt_number=4; + SHAPE_ROOM(ch)->editflag=0; + if(IS_SET(SHAPE_ROOM(ch)->flags,SHAPE_CHAIN)) { + SHAPE_ROOM(ch)->editflag=room_chain[18]; + } break; - /* Code below removed as it is unstable. fingolfin, december 2001 - - if(!mob->affected){ - send_to_char("No room affections found.\n\r",ch); - break; - } - if(!IS_SET(SHAPE_ROOM(ch)->flags,SHAPE_DIGIT_ACTIVE)){ - send_to_char("Describe room affection (four numbers, without commas):\n\r",ch); - SET_BIT(SHAPE_ROOM(ch)->flags,SHAPE_DIGIT_ACTIVE); - SHAPE_ROOM(ch)->position=shape_standup(ch,POSITION_SHAPING); - ch->specials.prompt_number=3; - return; - } - else{ - if(4!=sscanf(arg,"%d %d %d %d", - &tmp,&tmp1,&tmp2,&tmp3)){ - send_to_char("four numbers required. dropped\n\r",ch); - REMOVE_BIT(SHAPE_PROTO(ch)->flags,SHAPE_DIGIT_ACTIVE); - shape_standup(ch,SHAPE_ROOM(ch)->position); - ch->specials.prompt_number=4; - SHAPE_ROOM(ch)->editflag=0; - return; - } - } - mob->affected->type = tmp; - mob->affected->location = tmp1; - mob->affected->duration = -1; - mob->affected->modifier = tmp2; - mob->affected->bitvector = tmp3; - - REMOVE_BIT(SHAPE_ROOM(ch)->flags,SHAPE_DIGIT_ACTIVE); - shape_standup(ch,SHAPE_ROOM(ch)->position); - ch->specials.prompt_number=4; - SHAPE_ROOM(ch)->editflag=0; - if(IS_SET(SHAPE_ROOM(ch)->flags,SHAPE_CHAIN)) - SHAPE_ROOM(ch)->editflag=room_chain[18]; - - break; */ - case 19: /* adding new affection */ + tmpaf = get_from_affected_type_pool(); + tmpaf->next = SHAPE_ROOM(ch)->room->affected; + SHAPE_ROOM(ch)->room->affected = tmpaf; + send_to_char("A new affection added.\n\r",ch); - send_to_char("Not available at the moment\n\r", ch); - SHAPE_ROOM(ch) - ->editflag - = 0; + SHAPE_ROOM(ch)->editflag=0; + SET_BIT(SHAPE_ROOM(ch)->flags, SHAPE_CHAIN); + if(IS_SET(SHAPE_ROOM(ch)->flags, SHAPE_CHAIN)) { + SHAPE_ROOM(ch)->editflag=room_chain[19]; + } break; - /* Code below removed as it is unstable. fingolfin, december 2001 - - tmpaf = get_from_affected_type_pool(); - tmpaf->next = SHAPE_ROOM(ch)->room->affected; - SHAPE_ROOM(ch)->room->affected = tmpaf; - send_to_char("A new affection added.\n\r",ch); - - SHAPE_ROOM(ch)->editflag=0; - SET_BIT(SHAPE_ROOM(ch)->flags, SHAPE_CHAIN); - if(IS_SET(SHAPE_ROOM(ch)->flags, SHAPE_CHAIN)) - SHAPE_ROOM(ch)->editflag=room_chain[19]; - - break; */ - case 20: /*removing an affection */ - - send_to_char("Not available at the moment\n\r", ch); - SHAPE_ROOM(ch) - ->editflag - = 0; + if(!SHAPE_ROOM(ch)->room->affected){ + send_to_char("No affections exist on this room.\n\r",ch); + } + else{ + tmpaf = SHAPE_ROOM(ch)->room->affected->next; + put_to_affected_type_pool(SHAPE_ROOM(ch)->room->affected); + SHAPE_ROOM(ch)->room->affected = tmpaf; + send_to_char("Affection removed.\n\r",ch); + } + SHAPE_ROOM(ch)->editflag=0; + SET_BIT(SHAPE_ROOM(ch)->flags, SHAPE_CHAIN); + if(IS_SET(SHAPE_ROOM(ch)->flags, SHAPE_CHAIN)) { + SHAPE_ROOM(ch)->editflag=room_chain[20]; + } break; - /* Code below removed as it is unstable. fingolfin, december 2001 - - if(!SHAPE_ROOM(ch)->room->affected){ - send_to_char("No affections exist on this room.\n\r",ch); - } - else{ - tmpaf = SHAPE_ROOM(ch)->room->affected->next; - put_to_affected_type_pool(SHAPE_ROOM(ch)->room->affected); - SHAPE_ROOM(ch)->room->affected = tmpaf; - - send_to_char("Affection removed.\n\r",ch); - } - SHAPE_ROOM(ch)->editflag=0; - SET_BIT(SHAPE_ROOM(ch)->flags, SHAPE_CHAIN); - if(IS_SET(SHAPE_ROOM(ch)->flags, SHAPE_CHAIN)) - SHAPE_ROOM(ch)->editflag=room_chain[20]; - - break; */ - #undef DESCRCHANGE #undef LINECHANGE #undef DIGITCHANGE From 27cecd1ca96596d3d9a26c724ab54492053388b4 Mon Sep 17 00:00:00 2001 From: ahumbert Date: Thu, 19 Mar 2026 15:44:47 -0500 Subject: [PATCH 002/195] RangerProg: Add option for Riposte (#264) * RangerProg: Add option for Riposte * Add missing eof line --- src/fight.cpp | 27 ++++++++++++++++++++------- src/utility.cpp | 10 +++++++++- src/utils.h | 1 + 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/fight.cpp b/src/fight.cpp index 2998fdbf..8d8ba4f4 100644 --- a/src/fight.cpp +++ b/src/fight.cpp @@ -2107,14 +2107,28 @@ void check_grip(struct char_data* ch, struct obj_data* wielded) int check_riposte(struct char_data* ch, struct char_data* victim) { int dam; - int prob; + int prob = 0; + int can_riposte = 0; struct obj_data* wielded; - wielded = victim->equipment[WIELD]; - if (GET_SKILL(victim, SKILL_RIPOSTE) && !IS_NPC(victim) && wielded && GET_POS(victim) == POSITION_FIGHTING && !IS_SET(victim->specials.affected_by, AFF_BASH)) + if (!wielded || GET_POS(victim) != POSITION_FIGHTING || IS_SET(victim->specials.affected_by, AFF_BASH)) { + return 0; + } + + const int ranger_prog = 32; + if (IS_NPC(victim)) { + if (has_alias(victim, "riposter") && has_program(victim, ranger_prog)) { + can_riposte = 1; + prob = 75; + } + } else { + can_riposte = 1; + prob = GET_SKILL(victim, SKILL_RIPOSTE); + } + + if (can_riposte && prob) if (wielded->obj_flags.value[2] <= 3) { - prob = GET_SKILL(victim, SKILL_RIPOSTE); prob += GET_SKILL(victim, SKILL_STEALTH); prob += GET_DEX(victim) * 5; prob *= GET_PROF_LEVEL(PROF_RANGER, victim); @@ -2124,12 +2138,11 @@ int check_riposte(struct char_data* ch, struct char_data* victim) do_riposte(victim, ch); dam = get_weapon_damage(wielded) * std::min(static_cast(GET_DEX(victim)), 20) / number(50, 100); - if (damage(victim, ch, dam, - weapon_hit_type(wielded->obj_flags.value[3]), 1)) + if (damage(victim, ch, dam, weapon_hit_type(wielded->obj_flags.value[3]), 1)) { return 1; + } } } - return 0; } diff --git a/src/utility.cpp b/src/utility.cpp index 303a718d..b204722c 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -2208,4 +2208,12 @@ int has_alias(char_data* host, char *keyword) { } else { return 0; } -} \ No newline at end of file +} + +int has_program(char_data* host, int num) { + if((int)host->specials.store_prog_number == num) { + return 1; + } else { + return 0; + } +} diff --git a/src/utils.h b/src/utils.h index 80692140..c19653f9 100644 --- a/src/utils.h +++ b/src/utils.h @@ -94,6 +94,7 @@ void track_specialized_mage(char_data* mage); void untrack_specialized_mage(char_data* mage); int has_alias(char_data* host, char *keyword); +int has_program(char_data* host, int num); /* defines for fseek */ #ifndef SEEK_SET From a827e1c2d86f53fac02f703085f420d2a0ee0713 Mon Sep 17 00:00:00 2001 From: Noobinabox Date: Thu, 19 Mar 2026 15:48:44 -0500 Subject: [PATCH 003/195] Docs (#266) * Added docs. * Added documentation. * Updated docs. --- docs/Running the Game.md | 82 +++++++ docs/shape_mob.md | 402 ++++++++++++++++++++++++++++++++++ docs/shape_mudlle.md | 187 ++++++++++++++++ docs/shape_object.md | 411 +++++++++++++++++++++++++++++++++++ docs/shape_room.md | 295 +++++++++++++++++++++++++ docs/shape_script.md | 456 +++++++++++++++++++++++++++++++++++++++ docs/shape_zone.md | 291 +++++++++++++++++++++++++ docs/wizset.md | 166 ++++++++++++++ docs/wizutil.md | 130 +++++++++++ 9 files changed, 2420 insertions(+) create mode 100644 docs/Running the Game.md create mode 100644 docs/shape_mob.md create mode 100644 docs/shape_mudlle.md create mode 100644 docs/shape_object.md create mode 100644 docs/shape_room.md create mode 100644 docs/shape_script.md create mode 100644 docs/shape_zone.md create mode 100644 docs/wizset.md create mode 100644 docs/wizutil.md diff --git a/docs/Running the Game.md b/docs/Running the Game.md new file mode 100644 index 00000000..266767fd --- /dev/null +++ b/docs/Running the Game.md @@ -0,0 +1,82 @@ +# Running the Game + +The live, building, and coding ports on `` run as `systemd` +services. Use `systemctl` to start, stop, or restart each port: + +- `rotslive.service` +- `rotsbuilding.service` +- `rotscoding.service` + +## Port Overview + +| Purpose | Port | Working Directory | Autorun Script | Restart Command | +|----------|------|-----------------------------|----------------|-----------------------------------------| +| Live | 3791 | `/rots/live-default3791` | `autorun3791` | `systemctl restart rotslive` | +| Building | 4802 | `/rots/dev-building4802` | `autorun4802` | `systemctl restart rotsbuilding` | +| Coding | 4810 | `/rots/dev-coding4810` | `autorun4810` | `systemctl restart rotscoding` | + +Each directory follows the structure created by `make setup`: + +```text +-- /rots/ + - backups + - bin + - core + - levgen + - lib + - log + - src + - www +``` + +### Deploying Code to Any Port + +1. Upload the source via SFTP to the appropriate `src` directory. +2. In `src`, move the previous code base into the `backup/` folder after running `make clean` to avoid stale files. +3. Copy the new code into `src` and compile with `make all -j6`. +4. Restart the port so the changes take effect: + - In-game: `shutdown reboot` + - Or via shell: `systemctl restart ` + +> [!NOTE] The coding port does not maintain backups; treat it as a scratch space for compilation and testing. + +## Restoring Characters (Live Port) + +1. Change to the live working directory: `cd /rots/live-default3791`. +2. Enter the daily backups: `cd backups/daily`. Within this directory you will find: + +```text +-- backups/daily + - exploits/ + - players/ + - plrobjs/ +``` + +> [!TIP] Daily backups are retained for 30 days. + +1. Choose the correct archive: + - `exploits/` for exploit logs + - `players/` for player files + - `plrobjs/` for player objects +2. Copy the desired archive to a temporary working directory in your home folder: + +```bash +cp -v /rots/live-default3791/backups/daily/players/players-2026-02-01.tar.gz ~/temp/ +``` + +1. Extract the archive inside the temp directory: + +```bash +cd ~/temp +tar -xvzf players-2026-02-01.tar.gz ./ +``` + +> [!INFO] Files extract relative to the original tree (e.g., `./rots/live-default3791/lib/players`). + +1. Review or modify the restored files as needed, then copy them back into place: + +```bash +sudo cp -v rots/live-default3791/lib/players/A-E/aahz.32 /rots/live-default3791/lib/players/A-E/ +``` + +> [!IMPORTANT] Adjust the destination path to match the file type you are restoring. diff --git a/docs/shape_mob.md b/docs/shape_mob.md new file mode 100644 index 00000000..8ffc1878 --- /dev/null +++ b/docs/shape_mob.md @@ -0,0 +1,402 @@ +# Shape Mob Command + +`shape mob` is the in-game tool for creating or modifying mobile prototypes. +Mob definitions live in `world/mob/.mob` (or the alternate `world/prx` +paths for special zones). This guide describes the shaping interface, every +editable field, and the conventions expected by Return of the Shadow so you can +retire the old `shap_tbl` excerpts. + +## Prerequisites + +- **Permissions**: you must have builder rights for the zone (`get_permission`). +- **Vnum assignment**: reserve a mob number via `register` before creating a new + entry (`shape mob new ` automatically targets the right file). +- **Reference mob**: for quick sanity checks, keep another mob with similar + behaviour handy and compare stats with `/50`. + +## Workflow overview + +| Action | Command | +|--------|---------| +| Load/create | `shape mob ` to edit an existing mob, or `shape mob new ` to start from a blank template. | +| Mode toggle | `/simple` switches between simple (fields 1–12 + spirit) and extended editing; `/extended` switches back. | +| Show menu | `/0` (or any non-numeric input) prints the numeric command list for the current mode. | +| Edit field | `/` runs the field editor described below. | +| List current values | `/50` dumps the whole mob definition (or `/49` to run the guided creation sequence). | +| Save & implement | `/save` writes to disk (after a backup); `/implement` pushes the temp mob into memory for live testing; `/done` performs save → implement → free. | +| Exit without saving | `/free`. | + +Editing uses the standard editor syntax: + +- Multiline text: enter text, `%f` to format, `%e` to finish (`%q` aborts). +- Numeric fields: enter the full value (e.g., `42`), apply offsets (`+5`, `-2`), + or toggle bit numbers (`p7` sets bit 7, `m7` clears). Blank input keeps the + previous value. + +## Simple-mode fields + +Simple mode exposes the minimum set needed for quick tuning: + +| `/n` | Field | Notes | +|------|-------|-------| +| `/1` | Aliases | Lowercase keywords players type (`orc guard orc guard`). | +| `/2` | Reference description | Short desc (`a surly orc guard`). Used in lists. | +| `/3` | Full room description | The long description players see when entering the room. Must end in a newline and period. | +| `/4` | Detailed description | The text shown when someone `look `. | +| `/5` | Mob flags | Bitvector of behaviour flags (see “Mob flags” below). Input accepts sums or `p` toggles. | +| `/6` | Affects | Bitvector of permanent affects applied to the mob (see “Affect flags”). | +| `/7` | Level | Combat level. Keep within expected zone ranges (check `/50` on similar mobs). | +| `/8` | Sex | `0` neutral, `1` male, `2` female. | +| `/9` | Race | Use the `RACE_*` constants from `src/structs.h` (see table in `docs/shape_script.md`). | +| `/10` | Body type | Determines hit locations (0 tiny, 1 humanoid, 2 quadruped, 3 tentacled, 4 bird). | +| `/11` | Race aggression | Bitvector built from race IDs (`1 << RACE_HUMAN`, etc.). Aggressive mobs attack those races on sight even without the `AGGRESSIVE` flag. | +| `/12` | Butcher item | Vnum of the object dropped when butchering the corpse (`0` for none). | +| `/40` | Mob spirit | Role-play marker shown in prompts (`docs/shape_script.md` lists the titles). Use the constants from `prompt_spirit` in `src/consts.cpp`. | + +## Extended-mode field reference + +| `/n` | Field | Description / Notes | +|------|-------|---------------------| +| `/1` – `/6` | Same as simple mode. | +| `/7` | Alignment | Stored in `specials2.alignment`, range roughly `-1000..1000`. Positive = good. | +| `/8` | Level | Combat level. | +| `/9` | Combat stats | Prompts for OB, parry, and dodge (three integers). Check balance against existing mobs. | +| `/10` | Hit points | Prompts for min and max hit (`min_hit`, `max_hit`). | +| `/11` | Damage | Base damage per swing (affects `points.damage`). | +| `/12` | Energy regen | Controls `points.ENE_regen`, which is used by `char_utils::get_energy_regen` and combat systems to determine both how quickly the mob regains energy and how fast it cycles attacks. Higher values shorten weapon recovery times (see ranger special shots, wild fighting handler, and `profs.cpp` multipliers), while lower values slow its swing rate. Defaults to roughly `70 + level * 2` in `new_mob`. | +| `/13` | Gold | Coins carried. Keep small unless the mob is meant to be a bank. | +| `/14` | Experience | Raw XP value. Use `/recalc` as a starting point and adjust sparingly. | +| `/16` | Current position | See `structs.h` `POSITION_*` defines (`POSITION_STANDING`, etc.). Determines the mob’s immediate pose when spawned. | +| `/17` | Default position | Fallback pose when not engaged (often `POSITION_STANDING` or `POSITION_RESTING`). | +| `/18` | Sex | 0 neutral, 1 male, 2 female. | +| `/19` | Race | `RACE_*` constant. Drives stat caps, languages, sunlight penalties. | +| `/20` | Race aggression | Bitvector of target races (see simple mode). | +| `/21` | Weight | Stored in hundredths of kg (`5000` = 50 kg). | +| `/22` | Height | Centimetres. | +| `/23` | Profession points | `GET_PROF_POINTS` per class (wizard/cleric/ranger/warrior). Enter four integers separated by spaces. | +| `/24` | Stamina (mana) | `constabilities.mana`. Use `/recalc` as reference when raising above default. | +| `/25` | Move points | `constabilities.move`. | +| `/26` | Body type | Same as `/10` in simple mode (0–4). | +| `/27` | Saving throw | Base save modifier (`GET_SAVE`). Negative is better. | +| `/28` | Stats | Enter `STR INT WILL DEX CON LEA` in that order. Range 0–40. | +| `/29` | Program number | ASIMA program ID (see `docs/shape_mudlle.md`). Clears `MOB_SPEC` when used. | +| `/30` | Language | The skill ID from `language_skills[]`/`LANG_*`. `LANG_BASIC` = common. | +| `/31` | Butcher item | Same as simple `/12`. | +| `/32` | Perception | `specials2.perception`; affects search/hide detection. | +| `/33` | Room death cry | Text shown in the room when the mob dies. | +| `/34` | Other-room death cry | Text broadcast to adjacent rooms. | +| `/35` | Corpse number | Vnum of the corpse object to spawn (`0` uses default). | +| `/36` | Resistances | Bitvector; see `MAN SHAPE MOB 36` (values align with spell schools such as fire, cold). | +| `/37` | Vulnerabilities | Bitvector, same mapping as resistances. | +| `/38` | Script number | Script vnum (see `docs/shape_script.md`). Works alongside ASIMA programs. | +| `/39` | RP flag | Bitmask of races allowed to roleplay with this mob (`specials2.rp_flag`). | +| `/40` | Mob spirit | Same as simple `/40`. | +| `/41` | Will teach | Toggles teaching capabilities (which skills/specs this trainer handles). Use bitmask defined in the training subsystem (`TRAIN_*`). | +| `/49` | Guided creation | Runs the field sequence recommended for new mobs. | +| `/50` | List | Prints every field (`/imp` plus `/50` is a good sanity check). | + +## Mob flags (`/5`) + +Bit numbers live in `src/structs.h` (`MOB_*`). Key ones: + +| Bit # | Flag | Description | +|-------|------|-------------| +| 0 | `MOB_SPEC` | Hard-coded special procedure. Clear when using ASIMA. | +| 1 | `MOB_SENTINEL` | Never roams. | +| 2 | `MOB_SCAVENGER` | Picks up/wears gear. | +| 3 | `MOB_ISNPC` | Always set. | +| 4 | `MOB_NOBASH` | Immune to bash. | +| 5 | `MOB_AGGRESSIVE` | Attacks any PC entering the room. | +| 6 | `MOB_STAY_ZONE` | Won’t leave its zone. | +| 7 | `MOB_WIMPY` | Flees when low on HP. | +| 8 | `MOB_STAY_SECT` (`STAY_TYPE`) | Only wanders within its sector type (e.g., water). | +| 9 | `MOB_IS_MOUNT` | Can be ridden. | +| 10 | `MOB_CAN_SWIM` | Doesn’t need a boat. | +| 11 | `MOB_MEMORY` | Remembers attackers and re-aggros them. | +| 12 | `MOB_HELPER` | Assists friends during fights. | +| 16 | `MOB_BODYGUARD` | Rescues its master. | +| 17 | `MOB_WRAITH` | Ghost-like (no corpse). | +| 18 | `MOB_SWITCHING` | Switches targets mid-fight. | +| 19 | `MOB_NORECALC` | Prevents `/recalc` from overwriting handcrafted stats. | +| 20 | `MOB_ACTIVE` | Acts immediately on room entry (50% chance). | +| 21 | `MOB_PET` | Tamed pet (usually set automatically). | +| 22 | `MOB_HUNTER` | Hunts down remembered attackers. | +| 23 | `MOB_ORC_FRIEND` | Recruitable by common orcs. | + +Use `p` / `m` to toggle bits or enter the summed integer. + +## Affect flags (`/6`) + +These map to the `AFF_*` bitvector (`src/structs.h`). Frequently used bits from +the old MAN SHAPE MOB 6 reference: + +| Bit # | Flag | Effect | +|-------|------|--------| +| 0 | `AFF_SENSE_LIFE` | Detect hidden/invisible players. | +| 1 | `AFF_INFRARED` | See in the dark. | +| 2 | `AFF_SNEAK` | Suppresses “leaves” messages when moving. | +| 3 | `AFF_HIDE` | Mob starts hidden (requires sense life to spot). | +| 4 | `AFF_DETECT_MAGIC` | Reserved for players; avoid setting. | +| 5 | `AFF_CHARM` | Acts as charmed (follows whoever issued `follow`). | +| 6 | `AFF_CURSE` | Broken—do not use. | +| 7 | `AFF_SANCTUARY` | Permanent sanctuary—heavily reduces damage. Use sparingly. | +| 8 | `AFF_TWOHANDED` | Forces two-handed wielding. | +| 13 | `AFF_BREATHE` | Breathe underwater. | +| 18 | `AFF_FLYING` | Leaves no tracks, immune to ground effects. | + +Setting an affect grants the mob the corresponding spell permanently—use with +caution, especially for sanctuary, flying, or invisibility. + +## Alignment guidelines (MAN SHAPE MOB 7) + +Alignment (`/7` in extended mode) is a flavour stat used by scripts and some +combat checks. Use these rough ranges when tuning mobs: + +| Race type | Recommended range | +|-----------|-------------------| +| Elves | `+200` to `+350` | +| Dwarves / Hobbits | `+150` to `+300` | +| Humans | `+100` to `+200` | +| Neutral creatures | `-100` to `+100` | +| Wargs | `-100` to `-200` | +| Orcs / Uruks | `-200` to `-350` | + +Avoid values beyond ±500 unless the mob is a unique lore figure. + +## Race aggression table (MAN SHAPE MOB 11) + +Race-aggression bits (field `/20`) let you target specific races even if the +mob lacks the global `AGGRESSIVE` flag. Add the bit value to the bitvector (or +`p`): + +| Target | Bit # | Value | +|--------|-------|-------| +| God | 0 | 1 | +| Human | 1 | 2 | +| Dwarf | 2 | 4 | +| Wood elf | 3 | 8 | +| Hobbit | 4 | 16 | +| High elf | 5 | 32 | +| Uruk | 11 | 2048 | +| Haradrim | 12 | 4096 | +| Orc | 13 | 8192 | +| Easterling | 14 | 16384 | +| Magus | 15 | 32768 | + +`62` makes a mob hostile to the “whitie” races, and `63488` to the “darkie” +factions. These bits stack with the global `AGGRESSIVE` flag. + +## Body types (MAN SHAPE MOB 10) + +Field `/26` chooses the hit-location template: + +| Value | Description | +|-------|-------------| +| 0 | Tiny/limbless creatures (snakes, slimes). | +| 1 | Humanoids (head, two arms, two legs). | +| 2 | Quadrupeds (four legs + head). Only these can be tamed as mounts. | +| 3 | Tentacled (octopi, aberrations). | +| 4 | Birds (wings + talons). | + +Pick the number that best matches the mob’s anatomy; scripts and combat tables +use it for butcher parts and hit messages. + +## Butcher items (MAN SHAPE MOB 12) + +Field `/31` sets the vnum dropped from butchering the corpse. Humanoids with no +special drop should use vnum `17` (the “body parts” placeholder). Non-humanoids +can point to custom meat/pelt objects. Use `0` to disable butchering entirely. + +## Languages (MAN SHAPE MOB 30) + +Field `/30` controls the language the mob speaks/listens for. The code currently +supports the three entries in `language_skills[]`: + +| Value | Constant | Notes | +|-------|----------|-------| +| 0 | `LANG_BASIC` | Westron/common. | +| 121 | `LANG_ANIMAL` | Used by beasts. | +| 122 | `LANG_HUMAN` | Human dialect (legacy). | +| 123 | `LANG_ORC` | Black-speech. | + +Mobs default to `LANG_BASIC`. Only change this if you have scripts that check +for specific dialects. + +## Perception defaults (MAN SHAPE MOB 32) + +Leaving `/32` at `-1` lets the engine assign a racial default: + +| Race type | Default perception | +|-----------|--------------------| +| Elves | 50 | +| Other humanoids | 30 | +| Undead (non-wraith) | 60 | +| Wraiths | 100 | + +Set an explicit number if you need sharper senses (higher) or dulled senses +(lower). Values feed the hide/search routines. + +## Death cries & corpses (MAN SHAPE MOB 33–35) + +- `/33` – in-room death cry. Defaults to “Your blood freezes as you hear its + death cry.” Enter a custom string to override. +- `/34` – other-room death cry echoed to adjacent rooms. +- `/35` – corpse vnum. `0` uses the generic corpse, which is a zero-capacity + container that inherits the mob’s weight. Custom corpses must still be + containers if you want loot to remain accessible. + +## Resistances and vulnerabilities (MAN SHAPE MOB 36/37) + +Fields `/36` (resistance) and `/37` (vulnerability) are bitvectors that map to +the specialization attack groups. Bits are shared with object resist/vuln flags: + +| Bit # | Group | Value | +|-------|-------|-------| +| 0 | None / general | 1 | +| 1 | Fire | 2 | +| 2 | Cold | 4 | +| 3 | Regeneration | 8 | +| 4 | Protection | 16 | +| 5 | Animals | 32 | +| 6 | Stealth | 64 | +| 7 | Wild fighting | 128 | +| 8 | Teleport | 256 | +| 9 | Illusion | 512 | +| 10 | Lightning | 1024 | +| 11 | Mind | 2048 | + +Most of these hooks are only consulted by a handful of spells/skills; when in +doubt, leave both vectors at 0. + +## Special procedures (MAN SHAPE MOB2 29) + +Field `/29` can reference built-in hard-coded behaviours instead of ASIMA +programs. Common IDs: + +| ID | Behaviour | +|----|-----------| +| 1 | Snake (poisons on hit). | +| 2 | Friendly gatekeeper (opens doors during day / on knock). | +| 3 | Caster-mystic (buffs/heals). | +| 4 | Caster/mage (offensive spells). | +| 5 | Warrior (bashes frequently). | +| 6 | Paranoid gatekeeper (keeps doors shut). | +| 7 | Jig (performs the jig command). | +| 8–13 | Exit blockers (north/east/south/west/up/down). | +| 14 | Resetter (practice resetter). | +| 15 | Ranger ambusher. | +| 26 | Summoner (calls adds during fights). | +| 27 | Reciter (reads textscrolls). | +| 28 | Herald (announces arrivals). | + +If you use a spec proc, remember to set the `SPECIAL` flag (`/5` bit 0). For +anything beyond these stock options, use ASIMA (`/29` with a program number) or +scripts (`/38`). + +## Best practices + +- **Use `/recalc` cautiously**: the command recalculates combat stats from the + level. It’s useful when starting but will wipe custom OB/hp/damage unless the + mob carries `MOB_NORECALC`. +- **Match zone expectations**: compare your mob’s `/50` output with similar + creatures already in the zone. Keep OB/HP/damage roughly aligned. +- **Body type matters**: choose the right `/26` for hit locations (humanoid vs + quadruped). Only body type 2 (quadruped) mobs can be tamed as mounts. +- **Race + sunlight**: orcs, uruks, and olog-hai suffer daylight penalties. If + your mob roams outside, consider equipping them with cloaks or scheduling + behaviour via scripts. +- **Programs vs scripts**: `/29` ASIMA programs run before `/38` script + triggers. Avoid using both unless you know which behaviour fires first. +- **Training mobs**: when using `/41`, ensure the trainer actually offers + corresponding lessons via the spec code—setting random bits does nothing if + the spec isn’t implemented. + +## Example: Updating a city guard + +Goal: create a level-40 human guard who challenges orcs at the gate, carries a +halberd, and speaks Westron. + +```text +shape mob 4005 +/1 gate guard guard human guard +/2 a vigilant gate guard +/3 A vigilant gate guard watches the traffic. +/4 The guard scans every traveler before waving them through. +/5 p1 p6 p12 # SENTINEL + STAY_ZONE + HELPER +/6 p1 # AFF_SENSE_LIFE +/7 200 # alignment +/8 40 # level +/9 95 60 20 # OB, parry, dodge +/10 1200 1500 # min/max hit +/11 28 +/12 30 +/13 15 # gold +/14 250000 # experience +/16 8 # current position (standing) +/17 8 # default position +/18 1 # male +/19 1 # RACE_HUMAN +/20 p13 # aggressive to orcs +/21 8500 +/22 185 +/23 10 10 10 10 # prof pools +/24 200 +/25 300 +/26 1 # humanoid +/27 -10 +/28 35 20 25 30 32 25 +/29 0 # no ASIMA program +/30 0 # LANG_BASIC +/31 0 # no butcher drop +/32 15 +/33 The guard crumples with a surprised gasp. +/34 You hear a guard fall nearby! +/35 0 +/36 0 +/37 0 +/38 4206 # gatekeeper script +/40 Master +/41 0 +/50 +/save +/implement +``` + +After shaping, add the mob to the zone file with an `M` command and kit it with +a halberd via `K`/`E`. + +## Example: Mountable warg for an orc patrol + +```text +shape mob 4802 +/1 warg mount patrol mount +/2 a hulking warg mount +/3 A hulking warg patiently waits for an orc rider. +/4 Slaver dripping from its jaws, the beast paws at the ground. +/5 p1 p6 p9 p10 p22 # ISNPC + STAY_ZONE + IS_MOUNT + CAN_SWIM + HUNTER +/8 32 # level +/9 85 20 10 +/10 900 1100 +/11 22 +/12 40 +/18 0 # neutral sex +/19 13 # RACE_ORC (shares penalties) +/21 18000 +/22 150 +/24 150 +/25 250 +/26 2 # quadruped +/32 5 # low perception +/29 0 +/38 0 # optional script for bucking non-orcs +/50 +/save +/implement +``` + +Use the `/49` guided sequence whenever you start a new mob; it steps through +aliases → descriptions → combat stats → loot → extras, mirroring the logical +order outlined above. + +Refer back to this document whenever you need the exact field semantics—the goal +is to keep shaping knowledge in one place so we can finally retire `shap_tbl`. diff --git a/docs/shape_mudlle.md b/docs/shape_mudlle.md new file mode 100644 index 00000000..1caaef60 --- /dev/null +++ b/docs/shape_mudlle.md @@ -0,0 +1,187 @@ +# Shape Mudlle Command (ASIMA) + +ASIMA (Assembler-Style Interpreter for Mobile Activity) is the in-game language +used to script “special” mobile behaviours without recompiling the server. This +file replaces the legacy `mudl_tbl` and describes how to load, edit, and assign +programs as well as the language primitives (stack, list, flow control) builders +need to write or maintain scripts. + +## Prerequisites & Program Numbers + +- Immortal level: only immortals may shape programs. You must also have + permission for the zone that owns the program (`get_permission(zone, ch)`). +- Program numbering: Programs live in `world/mdl/.mdl` files. Coordinate + with an implementor or use the `register` workflow to reserve a vnum before + editing (program #4205 lives in `world/mdl/42.mdl`). +- Assignment target: ASIMA only runs on mobiles. Removing a mobile’s `SPECIAL` + flag and setting its “program number” to an ASIMA vnum turns the script on. + Details appear in “Hooking a program to a mobile” below. + +## Session commands + +Start or resume via `shape program `. The shaper loads the program +from `world/mdl/.mdl`, creating a blank entry if none is found. + +| Command | Purpose | +|---------|---------| +| `/load ` | Re-read program text from disk. Usually invoked automatically by `shape program`. | +| `/show` | Display the current program number, its real index, and the raw ASIMA text. | +| `/edit` | Enter the line editor (type your ASIMA source, finish with a lone `@`). | +| `/save` | Write the updated program back to disk (backs up to `world/mdl/oldmdls/`). | +| `/implement` | Replaces the live program in memory (only works for existing programs; new ones require a reboot). | +| `/free` | Discard the in-memory buffer and exit shaping mode. | +| `/done` | Equivalent to `/implement`, `/save`, then `/free`. | + +> **Important:** `/implement` refuses to load a brand-new program because the +> runtime allocates space only at boot. After saving a new entry, coordinate a +> reboot so `boot_mudlle()` can add it to `mobile_program[]`. + +## Hooking a program to a mobile + +There are three ways to attach behaviour: + +1. **Hard-coded special** – reserved for stock guildmasters/quest NPCs. Avoid. +2. **Hard-coded proc selected at runtime** – use `shape mob /29` or zone + command `A 7 ` to pick from the limited list in `spec_pro.cpp`. +3. **ASIMA program** – clear the `SPECIAL` flag, set the mobile’s program number + to your ASIMA vnum (via `shape mob /29`), and ensure the mob’s regen command + (zone `M` or an `A 7`) sets `store_prog_number` appropriately. This is the + preferred route for custom logic. + +When a mobile with an ASIMA program resets, the runtime converts the text into +bytecode via `mudlle_converter` and stores it in `mobile_program[real_num]`. Use +`implement` to refresh the program of an existing mob without rebooting. + +## ASIMA language overview + +- **Instruction order**: arguments precede the command. For example, to add + 2 + 3, you write `2 3 +` (the stack stores both numbers and `+` consumes them). +- **Data structures**: + - **Stack**: holds integers for arithmetic and flow control. Commands like `t` + (duplicate), `T` (pop), and `x` (swap) manage it. Arithmetic operations `+`, + `-`, `*`, `/`, bitwise `&`, `|`, logic `>`, `<`, `=`, `!`, `~` operate on the + two lowest values. + - **List**: circular buffer storing references to strings, rooms, mobiles, + players, and objects. Commands such as `f` (fetch item into list), `l`/`L` + (walk forward/backward), `p` (duplicate), `P` (remove), `X` (swap) manage it. + Many commands act on the lowest list item (e.g., `s` says the string stored + there). +- **Flow control**: + - Use `@NNN` to mark a label and `MNNN` to push its address to the stack. + - `g` performs an unconditional goto to the address stored on the stack. + - `i` performs a goto if the previous stack value is non-zero. + - `Q/q` return FALSE (with/without reset), `R/r` return TRUE. +- **Call masks**: `I` pulls a bitmask from the stack to set triggers. Bits: + `1` = command handler, `2` = self (heartbeat), `4` = enter-room. Example: + `7I` enables all three. +- **Strings**: start with a backtick, end with `S` to push as a string literal. + Example: `` `Greetings, traveller.`S `` adds the text to the list, `s` says it. +- **Delays and randomness**: + - `d` consumes a stack value and waits that many pulses. + - `N` consumes a stack value and pushes a random number between 0 and value. + +### Interaction commands + +| Command | Description | +|---------|-------------| +| Movement (`mn`, `ms`, `me`, `mw`, `mu`, `md`) | Move the host mob north/south/etc. | +| `f` + letter | Fetch references into the list: `fs` self, `fa` argument text, `fi` number-from-stack as string, `fr` room, `fh` last command issuer, `fc` first char in room, `fp` first PC, `fm` first mob, `fN` next in room. | +| `v` + letter | Push stats of the lowest list item (or host) onto the stack: `vh/VH` hit/max hit, `vm/VM` mana, `vv/VV` move, `vl` level, `vc` command verb. Returns `1` on success, `0` otherwise. | +| `V` + letter | Set stats from the stack (hit/mana/move). Use with caution. | +| `s` | Say the lowest string in the list to the room. | +| `U` | Execute the command string stored in the list (acts like “force host to run command”). | +| `W` | Cast a spell: lowest list item is the spell command, the next item (if non-zero) is the target. | +| `g` / `i` | Goto unconditionally / conditionally (jump address must already be on the stack, typically via `MNNN`). | +| `_` | Interrupt (exit without resetting state). | +| `d` | Delay for N pulses (N is taken from the stack). | + +### Stack helpers + +- `t` – duplicate the last stack value (push a copy). +- `T` – pop the last stack value. +- `x` – swap the two lowest stack values. +- `.` – no-op; useful to separate numeric literals (`12.3` pushes 12 then 3). + +### List helpers + +- `l` / `L` – move forward / backward in the list. +- `p` – duplicate current item. +- `P` – remove current item. +- `c` / `C` – detect and optionally remove duplicate references. +- `=` / `!` – compare the two lowest items in the list and push 1/0 to the stack. + +### Return semantics + +- `R` / `r` return TRUE (reset / do not reset memory). +- `Q` / `q` return FALSE (reset / do not reset memory). Use `r`/`q` to keep the + stack/list contents between calls when you want stateful behaviour. + +## Example programs + +### Simple greeter (program #4205) + +``` +#4205 +7I ; handle command/self/enter_room triggers +`Greetings, traveler.`S +s ; say the string +r ; return TRUE without resetting lists/stack +``` + +Assign it to a mobile via `shape mob /29` (set to 4205) and clear the SPECIAL +flag. The mob will greet on heartbeat and when someone enters the room. + +### Conditional healer (program #4206) + +``` +#4206 +7I +fp ; put first player in room onto the list +vH.vh.= ; compare max HP to current HP +097i ; push label 97, conditional goto if HP equals max +`I see you're not well.`S s +Vh ; set HP from stack (must push desired value first) +R ; return TRUE and reset +@97 +`You look healthy.`S s +R +``` + +This script looks for the first player in the room, compares their current HP +to max HP, and either heals or compliments them. The label `@97` plus `097i` +demonstrates conditional flow: `i` jumps to label 97 if the preceding comparison +was TRUE. + +### Command relay (program #4207) + +``` +#4207 +1I ; command-only trigger +fa ; argument line (player input) to list +`say `S ; literal "say " +X+S ; concatenate "say " with the argument line +U ; execute the combined command (host repeats the player's request) +r +``` + +This turns the mobile into a parrot that repeats whatever players tell it +(`tell mobdude sing`). Because the call mask is `1`, the program only runs when +players issue commands at the mob (not on heartbeat). + +## Best practices + +- Keep ASIMA programs small. The language was designed for lightweight behaviours + (greeters, basic quest logic). Complex systems are still better handled via + hard-coded specials. +- Comment externally: there is no in-language comment syntax. Maintain a note in + the zone docs describing what each program number does. +- Back up the `.mdl` file before editing heavily. `/save` already writes a copy + to `world/mdl/oldmdls/`, but snapshotting your source is still wise. +- Use `/implement` after minor edits to existing programs so you can test them + immediately. +- New programs need a reboot before they can be referenced. Plan accordingly and + avoid hooking a brand-new vnum into mobs until after the restart. + +With this reference, you can retire `mudl_tbl`, edit programs entirely from the +CLI, and understand how ASIMA scripts interact with mobiles and zone regen. + diff --git a/docs/shape_object.md b/docs/shape_object.md new file mode 100644 index 00000000..37b8c23d --- /dev/null +++ b/docs/shape_object.md @@ -0,0 +1,411 @@ +# Shape Object Command + +Object shaping is the in-game workflow for creating or editing prototypes stored +under `world/obj/*.obj`. The entry point is `shape object …` inside +`src/shapemob.cpp`, while the interactive editor lives in `src/shapeobj.cpp`. +This guide explains how to start a session, what the slash commands do, and +what each numeric menu option edits. + +## Prerequisites + +- **Builder access** – `get_permission(zone, ch)` must grant write privileges + for the target zone. Implementors (`object_master_idnum`, etc.) bypass this, + but regular builders need explicit access. +- **Vnum assignment** – use the in-game `register` command (see `MAN WIZ REGISTER`) + to reserve a new + object vnum before running `shape object new `. Editing existing objects + requires knowing their vnums (`stat obj` or `show zone` helps). +- **Prompt awareness** – when the object editor is active your prompt number + changes to `6`, reminding you that every command must be prefixed with `/`. +- **Zone fit** – skim the OBJLIST guidelines to ensure the item type suits + the zone’s weapon/armor categories before you start shaping. + +## Starting a session + +| Command | When to use | Notes | +|---------|-------------|-------| +| `shape object ` | Edit an existing object | Loads the prototype from `world/obj/.obj` and places it in the editor buffer. | +| `shape object new ` | Begin a fresh object | Creates a blank template, points it at `world/obj/.obj`, and jumps into the creation sequence (`/49`). Follow up with `/add ` to assign the final vnum. | + +Once loaded you’ll see “You start shaping an object.” and the editor prompt +appears. All further commands must start with `/` (per the builder manual’s +GENERAL section). Inside the editor, `/help` or `/0` mirrors the `MAN SHAPE OBJ +` entries, so keep that manual handy for deep dives. + +## Session control commands (`extra_coms_obj`) + +| Command | Purpose & behaviour | +|---------|---------------------| +| `/create ` | Sets target files (`world/obj/.obj` and `world/obj/oldobjs/.obj`), allocates a blank template via `new_obj()`, and starts the creation chain. | +| `/load ` | Reads the specified object into the editor buffer (`load_object()`). Refuses if another object is already loaded. | +| `/save` | Writes the edited object back over its existing record (`replace_object()`), first backing up to `world/obj/oldobjs/.obj`. | +| `/add ` | Appends the buffer as a brand-new vnum at the end of the zone file (`append_object()`), also producing a backup copy. Use this after `shape object new`. | +| `/delete` | Two-step safety. First call arms deletion and asks for “yes”. Typing `yes` immediately afterward flags the next `/save` to remove the object from disk. | +| `/implement` | Calls `implement_object()` to push the edited object into the live `obj_proto[]` array without touching disk. Useful for testing changes right away. | +| `/done` | Convenience action: `/save`, `/implement`, then `/free`. Ends the session cleanly. | +| `/free` | Releases the editor buffer (`free_object()`), clears shaping flags, and restores your normal prompt/position. Always free before switching targets. | + +Entering any other word after `/` prints the supported verbs and leaves you in +edit mode. + +## Editing workflow + +Type `/0` (or any non-digit) to display the numeric menu (`list_help_obj()`), +then run `/1`, `/2`, etc. to change fields. Inputs fall into four patterns: + +1. **Text entry** (`LINECHANGE` / `DESCRCHANGE`) uses the standard `%f`/`%e` + editor. `%q` cancels and keeps the previous string. +2. **Single-value prompts** (`DIGITCHANGE`) leverage `string_to_new_value()`, so + you can enter absolute numbers (`123`), add/subtract (`+5`, `-2`), or toggle + bit positions (`p7`, `m3`). Blank lines keep the old value. +3. **Multi-value prompts** (e.g., `/12`, `/19`) expect space-separated numbers + on one line. Press Enter with no input to abort. +4. **Creation chain** (`/49`) toggles `SHAPE_CHAIN` and automatically steps you + through the recommended sequence (`obj_chain[]`). It’s a nice guided tour + when drafting new gear. + +## Field reference + +### Text fields + +| `/n` | Field | Notes | +|------|-------|-------| +| `/1` | Aliases | Space-separated keywords players type (`get axe`). For drink containers, make the liquid name the first alias (`beer mug`). | +| `/2` | Reference description | Short description shown in inventory lists (“a steel longsword”). No trailing period. | +| `/3` | Full (in-room) description | How the object appears in a room (“A steel longsword lies here.”). Capitalize and end with a period. | +| `/4` | Action description | Multi-line `look` text (paragraph). Treat like a room description with `%f` formatting. For ITEM_NOTE objects this is the readable body of the note. | + +### Extra descriptions + +| `/n` | Behaviour | +|------|-----------| +| `/5` | Push a new extra description onto the list (no input). Automatically chains to `/6` and `/7`. | +| `/6` | Edit the keyword list for the current extra description (lowercase words, space-separated, avoid punctuation). | +| `/7` | Edit the text for the current extra description. Use `%f` if needed. | +| `/8` | Remove the current/last extra description. | + +Extra descriptions work as a stack. Run `/5`, then `/6`/`/7` to populate the +new record. `/8` removes the most recent entry. + +### Flags and wear slots + +| `/n` | Field | Notes | +|------|-------|-------| +| `/9` | Type flag | See the `ITEM_*` constants in `structs.h`. Determines how `/12` values are interpreted. | +| `/10` | Extra flags | Bitvector (glow, humming, magic, nodrop, etc.). Use `p`/`m` to toggle. | +| `/11` | Wear flags | Bitvector (TAKE, FINGER, NECK, …). At minimum set `TAKE` for portable items and `WIELD` for weapons. | + +Extra-flag bits: + +- `0` (`1`) GLOW +- `1` (`2`) HUMMING +- `2` (`4`) DARK +- `3` (`8`) BREAKABLE (keys, brittle items) +- `4` (`16`) EVIL +- `5` (`32`) INVISIBLE +- `6` (`64`) MAGIC +- `7` (`128`) NODROP +- `8` (`256`) BROKEN +- `9` (`512`) ANTI_GOOD (avoid unless directed) +- `10` (`1024`) ANTI_EVIL (avoid) +- `11` (`2048`) ANTI_NEUTRAL (avoid) +- `12` (`4096`) NORENT + +Wear-flag bits: + +`0` TAKE, `1` FINGER, `2` NECK, `3` BODY, `4` HEAD, `5` LEGS, `6` FEET, `7` +HANDS, `8` ARMS, `9` SHIELD, `10` ABOUT BODY, `11` WAIST, `12` WRIST, `13` +WIELD, `14` HOLD, `15` THROW, `16` LIGHT-SOURCE, `17` BELT. + +### Core stats & metadata + +| `/n` | Field | Notes | +|------|-------|-------| +| `/12` | Values[0..4] | Five integers whose meaning depends on the type flag. Enter five numbers at once. See the “Object values” section below. | +| `/13` | Weight | Stored in hundredths of a kilogram. A one-kilogram item is `100`. Pickable objects **must** have a non-zero weight; non-takeable props may stay `0`. Use the shapetable guideline: wielding two-handed roughly requires Strength equal to the weight in kg (so 8 kg takes STR 8), while one-handed wielding needs double that. | +| `/14` | Cost | Shop price guideline (typically `10 * level^2` for levels ≤10, doubling thereafter). | +| `/15` | Rent / cost per day | Suggested rent per in-game hour (`level^2` for ≤5, otherwise `(level^3)/5`). Matches the original “cost per day” design. | +| `/16` | Level | Represents the quality/tier of the item. Keep it near the mobs that drop it. | +| `/17` | Rarity | Reserved for future random generators; leave at `0` unless directed otherwise. | +| `/18` | Material | Integer index into `object_materials[]` (`cloth`, `leather`, `metal`, etc.). | + +Common material ids: +`0` usual, `1` cloth, `2` leather, `3` chain, `4` metal, `5` wood, `6` stone, +`7` crystal, `8` gold, `9` silver, `10` mithril, `11` fur, `12` glass, +`13` plant. + +### Affects and scripts + +| `/n` | Field | Notes | +|------|-------|-------| +| `/19` | Object affects | Enter pairs like `( 18 10 ) ( 17 5 )` to apply +10 OB and +5 dodge. Slots beyond `MAX_OBJ_AFFECT` are ignored. Use `(0 0)` fillers if you’re not sure. | +| `/20` | Program number | Legacy prog hook; almost never used. Only touch if a senior implementor asks you to. | +| `/21` | Script number | Slots the object into the mudlle/script subsystem. Also restricted to special cases. | +| `/49` | Creation sequence | Walks you through aliases → descriptions → flags → stats using `obj_chain[]`. Great for new items. | +| `/50` | List | Calls `list_object()` and prints every field for auditing. | + +## Object value reference (command `/12`) + +Because `/12` edits five raw integers, you **must** consult the per-type +definitions below. New objects start with zeros (the TRASH +defaults), so adjust every slot unless you truly want a junk item. The table +below summarizes every entry documented there: + +| Entry | Value meanings | +|-------|----------------| +| LIGHT (type 1) | `value[2]` = burn hours (`0` = burnt out, `<0` = eternal). All other slots unused. | +| WEAPON (5) | `value[0]` OB, `value[1]` parry bonus, `value[2]` bulk (≈2/3 feet), `value[3]` attack category (2=whip, 8=axe, 11=pierce…), `value[4]` damage. | +| ARMOR (9) | `value[0]` `0` for auto absorb, `-1` to disable; `value[1]` min absorb; `value[2]` encumbrance; `value[3]` dodge bonus; `value[4]` reserved. | +| WORN (11) | Deprecated catch-all. Leave unused—create light armor with zeroed armor values instead. | +| OTHER (12) | All zeros. Use when no other category fits. | +| TRASH (13) | All zeros. Pure flavour items. | +| CONTAINER (15) | `value[0]` capacity (hundredths of kg), `value[1]` flags (1 closeable / 2 pickproof / 4 closed / 8 locked), `value[2]` key vnum (`-1` none), `value[3]` corpse rot timer, `value[4]` unused. | +| NOTE (16) | `value[0]` language id (tongue). Others unused. | +| DRINKCON (17) | `value[0]` max units, `value[1]` current units, `value[2]` liquid type (`LIQ_WATER` … `LIQ_CLEARWATER`), `value[3]` poison flag, `value[4]` unused. | +| KEY (18) | `value[0]` key/lock id (match door’s lock vnum). Others unused. | +| FOOD (19) | `value[0]` hours of fullness; `value[3]` poison flag; rest unused. | +| MONEY (20) | `value[0]` number of coins. Others unused. | +| BOAT (22) | No special values; leave zeros. | +| FOUNTAIN (23) | Same layout as DRINKCON. | +| SHIELD (24) | `value[0]` dodge bonus, `value[1]` parry bonus, `value[2]` encumbrance, `value[3]` shield block coefficient, `value[4]` reserved. | +| LEVER (25) | `value[0]` room vnum containing the door, `value[1]` direction (0–5 for N/E/S/W/U/D). Always mark levers as NOTAKE and set the matching door flag. | + +Copy values from similar objects with `/50` when in doubt, and stick to the +attack-category guidelines listed under the weapon summary above when selecting +message types. + +### Liquid type ids + +| Name | Id | Drunkness | Fullness | Thirst | +|------|----|-----------|----------|--------| +| LIQ_WATER | 0 | 0 | 1 | 10 | +| LIQ_BEER | 1 | 3 | 2 | 5 | +| LIQ_WINE | 2 | 5 | 2 | 5 | +| LIQ_ALE | 3 | 2 | 2 | 5 | +| LIQ_DARKALE | 4 | 1 | 2 | 5 | +| LIQ_WHISKY | 5 | 6 | 1 | 4 | +| LIQ_LEMONADE | 6 | 0 | 1 | 8 | +| LIQ_FIREBRT | 7 | 10 | 0 | 0 | +| LIQ_LOCALSPC | 8 | 3 | 3 | 3 | +| LIQ_SLIME | 9 | 0 | 4 | -8 | +| LIQ_MILK | 10 | 0 | 3 | 6 | +| LIQ_TEA | 11 | 0 | 1 | 6 | +| LIQ_COFFE | 12 | 0 | 1 | 6 | +| LIQ_BLOOD | 13 | 0 | 2 | -1 | +| LIQ_SALTWATER | 14 | 0 | 1 | -2 | +| LIQ_CLEARWATER | 15 | 0 | 0 | 13 | + +### Bitvector reference (`OBJ 19` / `OBJ BITVECTOR`) + +- `/19` expects `(location modifier)` pairs. `location` corresponds to the + APPLY table below (OB, dodge, regen, spell bonuses, etc.). +- For `APPLY_BITVECTOR` (location `28`), `modifier` represents the bit number + shown in the affect-bit table (e.g., `AFF_DETECT_HIDDEN = 0`, + `AFF_SANCTUARY = 7`). Use `p`/`m` syntax when editing extra/wear flags, but + stick to `(location modifier)` tuples for `/19`. +- For `APPLY_SPELL` (location `27`), encode `modifier` as `256 * spell_level + + spell_number`. Location `30` (RESISTANCE) and `31` (VULNERABILITY) treat the + modifier as the bit position documented in `MAN SHAPE MOB 36`. +- Keep a reference of the effect list handy; when unsure, default to `(0 0)` and + ask an implementor before granting powerful affects like sanctuary or haste. + The original table cautions that some flags do not behave as expected, so test + thoroughly before shipping unusual combinations. + +The affect-bit reference: + +| Bit # | Affect | +|-------|--------| +| 0 | AFF_DETECT_HIDDEN | +| 1 | AFF_INFRARED | +| 2 | AFF_SNEAK | +| 3 | AFF_HIDE | +| 4 | AFF_DETECT_MAGIC | +| 5 | AFF_CHARM | +| 6 | AFF_CURSE | +| 7 | AFF_SANCTUARY | +| 8 | AFF_TWOHANDED | +| 9 | AFF_INVISIBLE | +| 10 | AFF_MOONVISION | +| 11 | AFF_POISON | +| 12 | AFF_PROTECT_EVIL | +| 13 | AFF_PARALYSIS | +| 14 | AFF_GROUP | +| 15 | AFF_CONFUSE | +| 16 | AFF_SLEEP | +| 17 | AFF_BASH | +| 18 | AFF_DETECT_EVIL | +| 19 | AFF_DETECT_INVISIBLE | +| 20 | AFF_FEAR | +| 21 | AFF_BLIND | +| 22 | AFF_FOLLOW | +| 23 | AFF_SWIM | +| 24 | AFF_HUNT | +| 25 | AFF_EVASION | +| 26 | AFF_WAITING | +| 27 | AFF_WAITWHEEL | +| 28 | AFF_ORC_DELAY | +| 29 | AFF_CONCENTRATION | +| 30 | AFF_HAZE | + +Use the following `APPLY_*` codes when filling the `(location modifier)` pairs: + +| Code | Applies to | Notes | +|------|------------|-------| +| 0 | APPLY_NONE | Placeholder, no effect. | +| 1 | APPLY_STR | Strength | +| 2 | APPLY_DEX | Dexterity | +| 3 | APPLY_INT | Intelligence | +| 4 | APPLY_WIS | Wisdom | +| 5 | APPLY_CON | Constitution | +| 6 | APPLY_LEA | Leadership | +| 7 | APPLY_PROF | Proficiency points | +| 8 | APPLY_LEVEL | Character level | +| 9 | APPLY_AGE | Age | +| 10 | APPLY_CHAR_WEIGHT | Weight | +| 11 | APPLY_CHAR_HEIGHT | Height | +| 12 | APPLY_MANA | Stamina/mana | +| 13 | APPLY_HIT | Hit points | +| 14 | APPLY_MOVE | Movement points | +| 15 | APPLY_GOLD | Money | +| 16 | APPLY_EXP | Experience | +| 17 | APPLY_DODGE | Dodge bonus | +| 18 | APPLY_OB | Offensive bonus | +| 19 | APPLY_DAMROLL | Damage bonus | +| 20 | APPLY_SAVING_SPELL | Saving throws | +| 21 | APPLY_WILLPOWER | Will | +| 22 | APPLY_REGEN | Energy regen | +| 23 | APPLY_VISION | Positive values give infravision, negatives blind | +| 24 | APPLY_SPEED | Initiative/speed | +| 25 | APPLY_PERCEPTION | Search/listen | +| 26 | APPLY_ARMOR | Generic armor modifier | +| 27 | APPLY_SPELL | Encodes spell/level via `256*level + spell_number` | +| 28 | APPLY_BITVECTOR | Adds/removes affect bits (see table above) | +| 29 | APPLY_MANA_REGEN | Mana regen per tick | +| 30 | APPLY_RESISTANCE | Bitvector from `MAN SHAPE MOB 36` | +| 31 | APPLY_VULNERABILITY | Bitvector from `MAN SHAPE MOB 36` | + +## Example workflows + +### Modify an existing weapon + +``` +shape object 2503 # load an existing longsword +/50 # inspect current stats +/1 +mithril greatsword +/2 +a gleaming mithril greatsword +/3 +A gleaming mithril greatsword has been left here. +/4 + Etched runes crawl along the blade, humming with latent fire. +%f +%e +/9 +5 # ensure it's still a weapon +/12 +110 30 6 8 32 # OB 110, parry 30, bulk 6, axe slash, damage 32 +/13 +650 # 6.5 kg two-hander +/16 +45 +/18 +10 # mithril material +/19 +( 18 12 ) ( 17 5 ) # +12 OB, +5 dodge +/14 +20250 +/15 +3645 +/save +/implement +/done +``` + +### Create a brand-new quest note + +``` +register # get the next open vnum, say 4205 +shape object new 42 # start a template in zone 42 +/49 # run through the guided sequence +/1 +note parchment +/2 +a sealed parchment note +/3 +A sealed parchment note flutters here. +/4 + Wax stamped with a silver falcon holds the parchment closed. +%f +%e +/9 +16 # ITEM_NOTE +/12 +5 0 0 0 0 # language 5 (Sindarin) +/13 +5 # light as paper +/14 +500 +/15 +125 +/16 +10 +/18 +1 # cloth +/19 +( 0 0 ) ( 0 0 ) # no magical affects +/add 42 # assign the next object vnum in zone 42 +/save +/implement +/done +``` + +### Retune a drink container instead of duplicating one + +``` +shape object 13302 # waterskin full of water +/5 # add an extra desc for the scent +/6 +brew smell +/7 + A sweet scent of mulled cider rises from the mouth of the flask. +%f +%e +/12 +40 40 2 0 0 # 5 drinks of cider (LIQ_WINE=2) +/18 +5 # leather +/19 +( 19 2 ) ( 0 0 ) # +2 damage (maybe the brew inspires courage) +/14 +800 +/15 +160 +/save +/implement +/done +``` + +These sequences show both modification and net-new creation: inspect with `/50`, +update text fields, tune stats/values, adjust special fields, then `/save`, +`/implement`, `/done`. + +## Troubleshooting & tips + +- “No object loaded for shaping” – you issued a numeric command before + `shape object …` or `/load`. Run `/load ` or restart. +- “You released an object and stopped shaping” – you may have typed `/free` + accidentally. Reload and resume editing. +- Remember that `/12` overwrites all five values. If you only want to change + one, re-enter all five numbers, or use `/49` to walk the defaults again. +- When duplicating an item, load the source, `/save` it under a new vnum using + `/add`, then immediately change aliases/descriptions to avoid identical + objects. +- Keep an eye on weight vs. wear slots: weapons need `WIELD`, shields need + `SHIELD`, armour must include the appropriate body slot plus `TAKE`. +- Only assign programs/scripts if you have mudlle support in place. Ordinary + builder items should leave `/20` and `/21` at `0`. + +Documenting `shape object` alongside `shape room` creates a consistent reference +for builders. Future sections (mob, zone, script) can point back here for slash +command etiquette and numeric input conventions. diff --git a/docs/shape_room.md b/docs/shape_room.md new file mode 100644 index 00000000..d5cb253c --- /dev/null +++ b/docs/shape_room.md @@ -0,0 +1,295 @@ +# Shape Room Command + +Room shaping is the online builder workflow for creating or editing rooms in +place without recompiling. The `shape room` entry point lives in +`src/shapemob.cpp` (`ACMD(do_shape)`), while the editor logic is implemented in +`src/shaperom.cpp`. This guide documents how to enter the mode, which `/` +commands are available, and what each numeric editor option changes. + +> The broader shaping system (objects, mobiles, zones, programs) works the same +> way. We are starting the documentation effort with rooms, so future sections +> can reuse the terminology established here. + +## Prerequisites + +- Builder permissions: `do_shape` only lets non-gods shape rooms. Higher level + staff can shape any prototype, but you still need zone permissions before + writing (`get_permission` in `create_room()` / `replace_room()`). +- Location: `shape room current` uses your current room number, so ensure you + are standing in the room you want to copy before starting. +- Prompt: once shaping, your prompt changes to include the builder mode number + so you know which interpreter is active. + +## Room Writing Guidelines + +### Level 91 (Lower Maias) + +- Room titles start at column 0, use title case (“Woods in the Valley”), and + never end with a period. +- Describe the location, not the visitor. Avoid implying actions (“You shiver”), + emotions, racial biases, or times of day unless the room enforces them. +- Keep a neutral voice and avoid second-person pronouns, exclamation points, or + sentence fragments. +- Ensure descriptions are at least four lines long, each indented with three + spaces. Run `%f` to wrap them neatly. +- Stay lore-friendly: Fourth Age Middle-earth allows creative flora/fauna but + not cars, firearms, or modern tech. Death traps are banned. +- Door keywords should be single lowercase words. Use the `exit_width` field for + unusual widths (default `0` lets the sector decide). + +### Level 93 (Maias) + +- Populate the zone’s metadata in `shape zone` as soon as you claim an area. +- Unless directed otherwise, lay out zones as rectangles—8 rooms north/south by + 5 or 10 rooms east/west—so future connectors are straightforward. + +## Starting a room shaping session + +| Command | When to use | Notes | +|---------|-------------|-------| +| `shape room current` | Edit the room you are standing in | `do_shape` converts `current` into the real room number and runs `load ` for you (`src/shapemob.cpp:1998-2015`). | +| `shape room 1234` | Edit any existing room by vnum | Replace `1234` with the virtual room number. The loader reads from `world/wld/.wld` (see `SHAPE_ROM_DIR`). | +| `shape room new ` | Create a blank room at the end of a zone file | Calls `create_room()` which checks zone permissions, opens `world/wld/.wld`, and prepares a new `room_data`. Immediately `/add ` afterward to persist it. | + +Once executed, you receive “You start shaping a room.” and your prompt number +switches to `4` to indicate the room editor is active. All subsequent commands +must be prefixed with `/` (per the builder manual’s GENERAL section). + +## Session control commands + +While shaping, entering any non-numeric `/command` routes through +`extra_coms_room()` (`src/shaperom.cpp:1629-1778`). These drive the lifecycle: + +| Command | Purpose & behaviour | +|---------|---------------------| +| `/load ` | Calls `load_room()` to populate `SHAPE_ROOM(ch)->room` with another vnum while leaving the editor running. Useful for quickly hopping between adjacent rooms. | +| `/create ` | Allocates a blank `room_data`, remembers `world/wld/.wld` as the working file, and marks the slot as dirty so `/add` or `/save` knows where to write. | +| `/save` | Runs `replace_room()`: copies the source `.wld` file to `world/wld/oldroms/.wld`, then rewrites the original entry with your edited data. Keeps the existing vnum. | +| `/add ` | Runs `append_room()`: same backup process as `/save`, but appends your new room to the end of the zone file and assigns the next available vnum. | +| `/delete` | First invocation arms deletion and prompts for confirmation. Typing `yes` immediately afterward toggles `SHAPE_DELETE_ACTIVE`, so the next `/save` removes the room from disk. Any other response cancels the delete. | +| `/implement` | Calls `implement_room()` to push the in-memory struct into the live `world[]` array without touching disk. Use this after `/save` to see your updates instantly in game. | +| `/done` | Convenience macro: if a room is loaded it performs `/save`, then `/implement`, then `/free`. Ends the session with one command. | +| `/free` | Calls `free_room()`, releases all allocated descriptions/exits/affects, resets prompts, and moves your character back to their previous position. Always free the editor before switching to another shaper target. | + +If you enter something else, the helper prints the allowed keywords (“save, +delete, implement, done, free”) and leaves you in edit mode. + +## Shaping workflow tips + +- Every shaping command (besides the initial `shape room …`) must start with `/`. + `/help`, `/0`, and `/50` are always available reminders. +- `/imp` shows what you’ve built; `/50` prints the current field values. +- `/free` quits without saving. `/save` writes to disk, `/implement` syncs the + live world, and `/done` performs save → implement → free in one shot. +- Always `/save` before `/free` unless you intend to discard edits. +- FAQ nuggets: + - `%e` must be on its own line to finish multiline text. `%q` aborts an edit. + - `/50` lists most commands and field states; `/help` or `/0` lists the rest. + - `/save` followed by `/done` is redundant because `/done` already saves and + implements, but running `/save` first gives you an explicit confirmation. +- `/free` ends shaping immediately—use `/done` if you want to save as you exit. +- Always indent descriptions manually (three spaces), run `%f`, then `%e`. +- Mob/object population limits are defined in the zone script (`L` commands). + If you need “exactly one mob” logic, update the zone data rather than the + room itself. + +## Editing workflow + +Type `/0` or any non-digit to display all numeric editor commands (handled by +`list_help_room()`), then use `/` to edit a field. Inputs fall into three +categories: + +1. Text entry (`LINECHANGE` / `DESCRCHANGE` macros) uses the standard `%f`/`%e` + editor; `%q` keeps the previous value. +2. Numeric entry (`DIGITCHANGE`) accepts absolute numbers, delta modifiers + (`+17`, `-2`), or bit toggles (`p5`, `m3`) just like the rest of the shaping + system. +3. Selection prompts temporarily change your prompt to ask for an exit + direction (letters `N`, `S`, `E`, `W`, `U`, `D`). + +`string_to_new_value()` backs every numeric prompt, so inputs like `p1` or `m4` +edit individual bits, while plain integers overwrite the whole field. Leaving +the prompt blank keeps the previous value. + +### Bitvector input cheat sheet + +Use these formats to manipulate flags: + +- `17` — set the full value to 17. +- `+17` / `-17` — add or subtract. +- `p17` — set bit 17 (`1 << 17`). +- `m17` — clear bit 17. + +Example: to set SENTINEL (`2`) and WIMPY (`128`) on a mob flag, either enter +`138` once or run `p1` then `p7`. `/50` after each change to confirm the result. + +### Room field commands + +| `/n` | Field | Description | +|------|-------|-------------| +| `/1` | Name | One-line room title. Stored verbatim, so follow `GUIDELINES` (title case, no trailing punctuation). | +| `/2` | Description | Multiline description. The editor swaps `#`→`+` and `~`→`-` automatically to keep `.wld` files intact; run `%f` before `%e` for proper wrapping. | +| `/3` | Room flags | Bitvector; use `p`/`m` to toggle individual bits or enter summed values directly (see `ROOM_*` in `structs.h`). `p7` sets flag 7, `m2` clears flag 2, `+4` adds 4, etc. | +| `/4` | Sector type | Numeric sector id from `sector_types` (inside, city, forest, mountain...). Values live in `constants.cpp`. | +| `/17` | Room level | Integer stored in `room_data::level` for quest tooling and scaling. | +| `/18` | Top room affect | Rewrites the first `struct affected_type` entry using four integers: `type location modifier bitvector`. Duration is forced to `-1`, so the effect is permanent until removed. | +| `/19` | Add affect | Pushes a fresh affect struct onto the list and enables chaining so `/18` runs next. | +| `/20` | Remove affect | Pops the top affect entry. Repeat to remove multiple entries. | +| `/50` | List | Prints the current state of every editable field, including the selected exit, extra descriptions, and the first affect block. | + +Text commands (`/1`, `/2`, `/13`, `/14`) honour the `%q` shortcut to cancel +edits. Numeric commands remember the previous value, so submitting a blank line +keeps the old value. + +### Exit commands + +1. `/5` — Select exit direction (must run this before editing exit-specific + fields). Accepts `n`, `s`, `e`, `w`, `u`, or `d`. If no exit exists the + editor allocates empty keyword/description strings so you can build it from + scratch. +2. `/6` — Exit flags (`room_direction_data::exit_info`). Supports all + combinations including hidden/no-look/heavy doors. Flag bits live in + `src/structs.h` (`EX_ISDOOR`, `EX_CLOSED`, `EX_LOCKED`, `EX_NOFLEE`, + `EX_PICKPROOF`, `EX_DOORISHEAVY`, `EX_NO_LOOK`, `EX_ISHIDDEN`, etc.). Use + numeric additions or `p`/`m` to toggle bits—for example `p0 p1 p9` + makes a closed, hidden, no-flee door. +3. `/7` — Remove the selected exit entirely and clear `exit_chosen`. Use this + when deleting links or cleaning up auto-generated exits. +4. `/8` — Exit keyword list. Provide space-separated words (e.g., `door hatch + trapdoor`). +5. `/9` — Exit description text (shows when players look at the door). `%f` + works here as well. +6. `/10` — Key vnum for locked exits. Set to `0` if no key is required. +7. `/11` — Destination room vnum. Enter the virtual number of the target room. + Remember to create the reverse exit manually. +8. `/12` — Exit width. Defaults to `0` (derived from sector type). Override + when you need narrow crawlways or oversized gates. + +Selecting an exit automatically creates placeholder `room_direction_data` +structures if one does not exist (`src/shaperom.cpp:718-734`), so you can +configure brand-new doors without leaving the editor. + +### Extra description commands + +| `/13` | Edit keyword (space-separated) for the current extra description record. | +| `/14` | Edit the corresponding description text. | +| `/15` | Push a new extra description onto the stack. The editor automatically sets `SHAPE_CHAIN`, so `/13` and `/14` fire next without retyping the numbers. | +| `/16` | Remove the current extra description (or the only one if it is the last). | + +Extra descriptions behave like a stack: `/15` adds to the top, `/16` pops it. +Use `/50` after `/15`/`/16` to confirm you are editing the intended entry. + +### Room affects + +- `/18` expects four integers separated by spaces: `type location modifier + bitvector`. For `ROOMAFF_SPELL` entries the `location` is the spell number + (see `skills[]`). The editor forces `duration = -1`, so affects persist until + someone removes them. +- `/19` appends a blank affect node to the head of the list, prints “A new + affection added.”, and enables chaining so `/18` triggers immediately. Use + this combo to add fog, damage auras, or `ROOMAFF_TRAP` behaviours. +- `/20` removes the head node. Run it repeatedly to clear the list from top to + bottom. + +If you try `/18` without any affect data present the shaper prints “No room +affections found.”, so remember to `/19` first. + +### `list` snapshot + +`/50` calls `list_room()` and prints: + +- Room name, description, flags, sector, and selected exit details. +- Exit keyword/description/key/destination/width for the currently selected exit + (run `/5` first to choose). +- The first extra description (keyword + text) and the first affect record if + present. + +Use it before `/save` as a final sanity check or after `/load` to understand an +existing room’s structure. + +## Example workflows + +### Modify an existing room + +```text +shape room current # load the room you are standing in +/5 # choose which exit to edit +n # at the prompt, enter “n” to pick the north exit +/11 # set the destination vnum +1605 # send the new room number +/8 # change the door keywords +oak door +%e +/2 # edit the room description + You stand before a weathered oak door... +%f +%e +/15 # add an extra description for the door +/13 +door oak door +/14 + The door is banded with iron. +%f +%e +/save +/implement +/done +``` + +This sequence highlights the typical cadence: select an exit, edit linked fields +in any order, review with `/50`, and save/implement when finished. + +### Create a new room from scratch + +```text +shape room new 16 # create a template (zone 16 covers rooms 1600-1699) +/49 # optional: walk the chained command list +/1 +Mist-Draped Bridge +/2 + Wisps of mist cling to the old stone bridge, hiding the drop below. +%f +%e +/3 +p0 p4 # example: DARK + NOMOB flags +/4 +3 # SECT_FIELD (adjust to taste) +/5 +n # select the north exit +/11 +1602 # point to the destination room +/8 +arch doorway bridge +/10 +0 # no key +/12 +180 # narrow exit width +/13 +%q # no extra description yet +/17 +35 # room level +/save # writes to world/wld/16xx.wld and backs up +/implement # updates the live world array +/done +``` + +Repeat for the south/east/west exits as needed, then `/imp` to double-check +your work in-game. Every new zone already includes 40 blank rooms, so stay +within your allocated number range. + +## Troubleshooting tips + +- “You have nothing to shape” — you ran a numeric command before loading a + room. Use `/load ` or restart with `shape room current`. +- “You are already shaping something” — you forgot to `/free` your previous + object/mob/room. Either finish and `/done`, or `/free` to start fresh. +- “You may not create room here” — the zone does not grant you permission (see + `get_permission()`), or you mistyped the zone number. Contact the zone owner + or the implementor. +- Accidentally deleted an exit or description? Because `/save` makes a backup in + `lib/backups/rooms/`, you can copy the `.bak` back in place or reload the room + without saving to revert to the last known state. + +With `shape room` documented, future scripting-doc sections can reference this +file rather than repeating the basics of prompts, `/save` vs `/implement`, and +the slash command syntax. diff --git a/docs/shape_script.md b/docs/shape_script.md new file mode 100644 index 00000000..2196a7e0 --- /dev/null +++ b/docs/shape_script.md @@ -0,0 +1,456 @@ +# Shape Script Command + +Scripts are short command sequences that attach to mobiles (and, partially, to +objects) and execute when a trigger fires: someone enters a room, speaks, wears +an item, etc. They are stored in `world/scr/.scr` and edited entirely +in-game. This guide replaces the legacy `scr_tbl` so builders can shape scripts +without hunting through old text files. + +## Prerequisites + +- Immortal access plus zone permissions (`get_permission(zone, ch)`). +- A reserved script vnum (matching the zone number, e.g., script 4205 lives in + `world/scr/42.scr`). Coordinate with an implementor if unsure. +- Target mobile: scripts currently run on mobiles; object hooks exist but only + a subset of triggers honor them. Assign the script vnum to a mobile via + `shape mob /38` and clear the `SPECIAL` flag unless combining it with a + hard-coded proc. + +## Working with `shape script` + +1. `shape script ` to load or create a program. New scripts get a blank + header but no commands. +2. Use the numeric menu (`/0`): + - `/1` show previous/current/next command. + - `/2` set a mask (filter) by command letter, room, etc. + - `/3` change the current command type. + - `/4` edit parameters for the current command. + - `/5` edit the one-line comment/description attached to the current command. + - `/6` / `/7` move to the next/previous command (respecting the “current + room” filter set via `/12`). + - `/8` jump to a specific command number. + - `/9` delete the current command (prompts for `y/n`). + - `/10` insert a new command after the current one. `/11` inserts before. + - `/12` set the “current room” for filtering (`0` = show entire script). + - `/13` swap the current command with the next. + - `/14` run a syntax check (flags unterminated `BEGIN/END`, etc.). + - `/20` change the script name; `/21` change the script description. + - `/50` list the entire script. +3. Editing fields uses the same conventions as other shapings: + - Text entry (`/5`, `/20`, `/21`) opens the `%f/%e` editor. + - Numeric prompts accept direct values (e.g., `42`), offsets (`+5`), or + bit toggles (`p3` sets bit 3, `m3` clears). Blank lines keep the old value. +4. `/save` writes the script back to disk (after backing up to + `world/scr/old/`). `/implement` copies the temporary version into the live + `script_table` if the script existed when the MUD booted. `/done` performs + `/implement`, `/save`, then `/free`. New scripts require a reboot before + they can be implemented. +5. `/free` abandons changes and exits shaping. Always `/save` first if you care + about the edits. + +### Script file structure + +Each entry in a `.scr` file looks like: + +``` +#