Manual:DIL Manual/AGENT
- DikuMUD3 DIL (DikuMUD Interactive Language) Reference
This document provides a comprehensive AGENT.md reference for DIL - the scripting language used in DikuMUD3 for creating interactive game content, NPCs, objects and complex game systems.
- Compiling a zone
To compile a zone: cd vme/zone/ && vme/bin/vmc -I../include/ zonename.zon
- Zones with good DIL examples
- vme/zone/haon_dor.zon - vme/zone/udgaard.zon - vme/zone/midgaard.zon - vme/zone/cypress.zon
- Quest helper DIL functions
Look in **vme/zone/quests.zon** for the DIL library of quest helper functions broadly exemplified in **vme/zone/haon-dor.zon**. Look in **vme/zone/randomt.zon** for generating random treasure in DIL.
- 📚 **Core Documentation**
- 🔗 Quick Access**: This condensed list covers all 314+ DIL language features. Each entry is reference their respective .wiki files in the DikuMUD3 DIL Manual documentation system. [string] for example is detailed in **vme/src/mcp/string.wiki**
- 🔧 **Language Overview**
- Data Types
- **[integer]** - Numeric data type: `health := 100; damage := 25; remaining := health - damage;` - **[string]** - Text data type: `name := "Player"; message := name + " has " + itoa(health) + " HP";` - **[unitptr]** - Unit pointer: `target := activator; npc := findunit("guard", UNIT_ST_NPC);` - **[integerlist]** - Integer collections: `skills := {5, 3, 6, 9}; level := skills.[2]; // == 6` - **[stringlist]** - String collections: `names := {"Alice","Bob","Charlie"}; first := names.[0];` - **[null]** - Null value: `if (self.fighting == null) { /* not fighting anyone */ }`
- Type Conversion
- **[atoi]** - String to integer: `num := atoi("42"); // num == 42` - **[itoa]** - Integer to string: `str := itoa(100); // str == "100"`
- Built-in Variables
- **[self]** - Current executing unit: `hp_pct := (self.hp * 100) / self.max_hp;` - **[activator]** - Unit that triggered program: `if (activator.level >= IMMORTAL_LEVEL) { /* admin access */ }` - **[argument]** - Command arguments: `wait(SFB_CMD, command("hello")); if (argument != "") { send("Hello " + argument + "!"); }` - **[command]** - Command name: `if (command("north")) { act("You go north.", A_ALWAYS, self, null, null, TO_CHAR); }` - **[cmdstr]** - Full command string: `full_cmd := cmdstr + " " + argument; act("Full: " + full_cmd, A_ALWAYS, self, null, null, TO_CHAR);` - **[cmdptr]** - Command pointer: `cmd := getcommand("say"); if (cmd) { send("Command level: " + itoa(cmd.level), self); }` - **[excmdstr]** - Command string (lowercase): `if (excmdstr != "quit") { sendtext("You must type 'quit' to quit.", self); }` - **[excmdstr_case]** - Command string (case-sensitive): `if (length(excmdstr_case) < 5) { sendtext("Password too short.", self); }` - **[heartbeat]** - Program timing: `heartbeat := PULSE_SEC * 10; // Tick every 10 seconds` - **[medium]** - Operation medium: `container := medium; if (container != null) { act("Using $1n as medium.", A_ALWAYS, self, container, null, TO_CHAR); }` - **[power]** - Action power: `if (power > 100) { sendtext("This is a very powerful spell!", self); }`
- Control Structures
- **[if]** - Conditional statement: `if (self.hp > 10) { exec("say Hehe!", self); } else { exec("say ouch!", self); }` - **[while]** - Loop construct: `while (self.inside) { if (self.position & POSITION_SLEEPING) break; pause; }` - **[foreach]** - Loop construct: `foreach (UNIT_ST_PC|UNIT_ST_NPC, u) { if (u.hp < u.max_hp) { u.hp := u.hp + 6; } pause; }` - **[goto]** - Unconditional jump: `:main_loop: pause; goto main_loop; // Create infinite loop` - **[on_goto]** - Goto construct: `on direction goto (north, south, east, west); // 0=north, 1=south, etc.` - **[break]** - Exit loop: `foreach (UNIT_ST_PC, target) { if (target.hp < target.max_hp / 2) { break; } }` - **[continue]** - Continue iteration: `foreach (UNIT_ST_OBJ, item) { if (not isset(item.manipulate, MANIPULATE_TAKE)) continue; }` - **[return]** - Return from function: `return(damage); // Functions: return value; Procedures: return;` - **[quit]** - Terminate program: `if (input == "exit") { sendtext("Goodbye!", self); quit; }` - **[block]** - Block commands: `wait(SFB_CMD, command("forbidden")); block; send("Command blocked!", activator);`
- ⚠️ IMPORTANT:** DIL does NOT support C-style `for` loops. Use `while` loops instead.
- Operators
- **[and]** - Logical operators: `if (self.level >= 10 and self.hp > 50) { /* ready for combat */ }` - **[or]** - Logical operators: `if (self.level < 10 or self.hp <= 0) { /* not ready */ }` - **[not]** - Logical operators: `if (not (self.fighting)) { /* not in combat */ }` - **[set]** - Bit operations: `set(self.pcflags, PC_WIZARD); // Set wizard mode flag` - **[unset]** - Bit operations: `unset(self.charflags, CHAR_SELF_DEFENCE); // Clear defence flag` - **[isset]** - Bit operations: `if (isset(self.flags, UNIT_FL_BURIED)) { /* item is buried */ }`
- Unit Fields
- **[type]** - Unit type: `if (target.type == UNIT_ST_PC) { sendtext("This is a player character.", self); }` - **[inside]** - Unit positioning: `item := chest.inside; if (item != null) { sendtext("Found: " + item.name, self); }` - **[outside]** - Unit positioning: `container := item.outside; if (container != null) { act("$1n is in $2n", A_ALWAYS, self, item, container, TO_CHAR); }` - **[bright]** - Lighting: `torch_brightness := bright(self); sendtext("Torch brightness: " + itoa(torch_brightness), self);` - **[setbright]** - Set brightness: `setbright(torch, 3); // Make torch emit more light` - **[light]** - Lighting: `light_count := self.light; sendtext("This unit contains " + itoa(light_count) + " light sources.", self); }` - **[illum]** - Contained light: `if (container.illum > 0) { sendtext("Container glows from within.", self); }` - **[name]** - Object identification: `item_name := item.name; sendtext("This is called: " + item_name, self); }` - **[title]** - Object identification: `obj.title := "A shiny sword"; sendtext("Item title updated to: " + obj.title, self); }` - **[descr]** - Object identification: `extra.descr := "This item glows with magical energy."; // Set extra description` - **[inside_descr]** - Internal description: `room.inside_descr := "This room has been modified."; // Change room description` - **[outside_descr]** - External description: `target.outside_descr := "A mysterious figure stands here."; // Set external description` - **[weight]** - Weight: `total_weight := pc.weight; capacity := pc.capacity; if (total_weight > capacity) { /* overburdened */ }` - **[baseweight]** - Weight: `empty_weight := baseweight(chest); // Get chest weight without contents` - **[set_weight]** - Set total weight: `set_weight(item, 50); // Set total weight including contents` - **[set_weight_base]** - Set base weight: `set_weight_base(item, 20); // Set empty weight of object` - **[capacity]** - Carrying capacity: `max_cap := capacity(self); remaining := max_cap - weight(self); send("Capacity: " + itoa(remaining) + " remaining", self);` - **[flags]** - Unit flags: `if (isset(self.flags, UNIT_FL_INVISIBLE)) { unset(self.flags, UNIT_FL_INVISIBLE); act("You become visible!", A_ALWAYS, self, null, null, TO_CHAR); }` - **[idx]** - Unique ID: `guard_id := guard.idx; // Get unique identifier for tracking` - **[key]** - Lock identifier: `if (item.key != "") { sendtext("Key required: " + item.key, self); }` - **[loadcount]** - Instance tracking: `if (item.loadcount > 10) { destroy(item); log("Too many instances loaded"); }` - **[nameidx]** - Database name: `unit_id := item.nameidx + "@" + item.zoneidx; // Unique reference` - **[manipulate]** - Object handling: `if (isset(item.manipulate, MANIPULATE_TAKE)) { sendtext("You can take this item.", self); }` - **[minv]** - Wizard invisibility: `if (item.minv > self.level) { sendtext("You can't see this item.", self); }` - **[names]** - Name list: `item_names := item.names; if ("sword" in item_names) { sendtext("This is a sword.", self); }` - **[opendiff]** - Lock difficulty: `if (chest.opendiff > 20) { sendtext("This chest has a very difficult lock.", self); }` - **[openflags]** - Open/lock state: `if (isset(door.openflags, EX_LOCKED)) { sendtext("The door is locked.", self); }` - **[zone]** - Zone name: `zone_name := item.zone; sendtext("This item belongs to zone: " + zone_name, self); }`
- Object Fields
- **[value]** - Economics: `weapon_damage := weapon.value[1]; // Get damage from weapon value array` - **[cost]** - Economics: `obj.cost := 1000; obj.rent := 10;` - **[rent]** - Economics: `obj.cost := 1000; obj.rent := 10;` - **[objectflags]** - Classification: `if (isset(item.objectflags, OBJ_MAGIC)) { act("$1n glows with magic!", A_ALWAYS, item, null, null, TO_ROOM); }` - **[objecttype]** - Classification: `if (obj.flags & OBJ_FL_MAGIC) { /* magic item */ }`
- Room Fields
- **[roomflags]** - Room properties: `if (isset(my_room.roomflags, ROOM_FL_DARK)) { sendtext("This room is dark.", self); }` - **[exit_to]** - Room exits: `if (room.exit_to[NORTH] != null) { sendtext("There is an exit to the north.", self); }` - **[exit_names]** - Room exits: `names := room.exit_names[NORTH]; if (length(names) > 0) { act("Exit: " + names.[0], A_ALWAYS, self, null, null, TO_CHAR); }` - **[mapx]** - Coordinates: `if (room.mapx != -1 and room.mapy != -1) { act("Room coordinates: X=$2d, Y=$3d", A_ALWAYS, self, room.mapx, room.mapy, TO_CHAR); }` - **[mapy]** - Coordinates: `distance := (room2.mapx - room1.mapx) + (room2.mapy - room1.mapy);` - **[exit_diff]** - Exit difficulty: `difficulty := room.exit_diff[NORTH]; if (skillcheck(SKI_CLIMB, ABIL_DEX, difficulty) > 0) { sendtext("You climb successfully!", self); }` - **[exit_info]** - Exit flags: `if (isset(room.exit_info[NORTH], EX_CLOSED)) { unset(room.exit_info[NORTH], EX_CLOSED); act("Door opens!", A_ALWAYS, self, null, null, TO_ALL); }` - **[exit_key]** - Exit key: `key_name := room.exit_key[NORTH]; if (key_name != "" and findsymbolic(key_name, FIND_UNIT_IN_ME)) { sendtext("You have the key!", self); }` - **[movement]** - Terrain type: `if (room.movement == SECT_WATER_SAIL) { sendtext("This area requires swimming or a boat!", self); }`
- Character Fields
- **[level]** - Character levels: `if (self.level >= 10) { sendtext("You are experienced!", self); }` - **[vlevel]** - Character levels: `lvl := self.level; exp := self.exp;` - **[exp]** - Experience points: `xp := self.exp; sendtext("You have " + itoa(xp) + " experience points.", self); }` - **[experience]** - Modify experience: `experience(1000, player); // Give 1000 XP for quest completion` - **[exptol]** - Experience to level: `if (self.exptol <= 0) { sendtext("You can level up now!", self); }` - **[hp]** - Health: `hp_pct := (self.hp * 100) / self.max_hp; if (self.hp < 10) { /* low health */ }` - **[max_hp]** - Health: `hp_pct := (self.hp * 100) / self.max_hp; target.hp := target.max_hp; // Full heal` - **[position]** - States: `if (self.position == POSITION_FIGHTING) { /* in combat */ } else if (self.position == POSITION_SLEEPING) { /* asleep */ }` - **[defaultpos]** - States: `if (self.position == POSITION_FIGHTING) { /* in combat */ }` - **[fighting]** - Combat: `opp := self.fighting; if (opp != null) { sendtext("Fighting " + opp.name, self); }` - **[opponent]** - Combat: `opp := self.fighting;` - **[master]** - Social relationships: `leader := self.master; if (leader != null) { act("You are following $1n.", A_ALWAYS, self, leader, null, TO_CHAR); }` - **[follower]** - Social relationships: `first := self.follower; if (first != null) { act("$1n is your first follower.", A_ALWAYS, self, null, first, TO_CHAR); }` - **[followercount]** - Social relationships: `count := self.followercount; if (count > 0) { sendtext("You have " + itoa(count) + " followers.", self); }` - **[opponentcount]** - Combat opponents: `if (self.opponentcount > 1) { sendtext("You are fighting multiple opponents!", self); }` - **[follow]** - Following control: `follow(prisoner, guard); // Make prisoner follow guard` - **[abilities]** - Character attributes: `str := self.abilities[ABIL_STR]; dex := self.abilities[ABIL_DEX];` - **[race]** - Character race: `if (self.race == RACE_ELF) { sendtext("You are an elf!", self); }` - **[sex]** - Character gender: `if (activator.sex == SEX_MALE) { sendtext("Greetings, sir!", activator); }` - **[speed]** - Combat speed: `if (self.speed > target.speed) { sendtext("You strike first!", self); }` - **[ability_points]** - Available points: `points := self.ability_points; if (points >= 5) { /* can train */ }` - **[ability_costs]** - Training costs: `cost := self.ability_costs[ABIL_STR]; if (money >= cost) { /* can afford training */ }` - **[ability_levels]** - Training requirements: `required := self.ability_levels[ABIL_STR]; if (level >= required) { /* can train */ }` - **[skill_points]** - Available skill points: `points := self.skill_points; if (points >= 10) { sendtext("You can train skills!", self); }` - **[skills]** - Skill system: `skill := self.skills.[SKI_BASH];` - **[skill_costs]** - Skill training costs: `cost := self.skill_costs[SKI_SWORD]; if (self.exp >= cost) { sendtext("Can afford skill!", self); }` - **[skill_levels]** - Current skill levels: `level := self.skill_levels[SKI_SWORD]; sendtext("Sword skill: " + itoa(level), self);` - **[spells]** - Magic system: `spell := self.spells.[SPL_FIREBALL];` - **[spell_costs]** - Spell learning costs: `cost := self.spell_costs[SPL_FIREBALL]; if (self.exp >= cost) { sendtext("Can afford spell!", self); }` - **[spell_levels]** - Current spell levels: `level := self.spell_levels[SPL_FIREBALL]; sendtext("Fireball level: " + itoa(level), self); }` - **[alignment]** - Moral standing: `align := alignment(self); if (align < -500) { send("You are evil!", self); }` - **[birth]** - Character creation: `age_days := (realtime - birth(self)) / 86400; send("You are " + itoa(age_days) + " days old.", self); }` - **[drunk]** - Character state: `if (self.drunk > 10) { sendtext("You are drunk!", self); }` - **[full]** - Hunger level: `if (self.full > 20) { sendtext("You are very hungry.", self); } else if (self.full < 5) { sendtext("You feel quite full.", self); }` - **[thirst]** - Thirst level: `if (self.thirst < 5) { sendtext("You are extremely thirsty!", self); } else { self.thirst := 24; // Fully quench thirst }` - **[endurance]** - Physical stamina: `if (self.endurance < 2) { sendtext("You are exhausted!", self); } else { self.endurance := self.endurance - 1; }` - **[max_endurance]** - Maximum stamina: `end_pct := (self.endurance * 100) / self.max_endurance; sendtext("Endurance: " + itoa(end_pct) + "%", self); }` - **[mana]** - Magic points: `if (self.mana < 50) { sendtext("Not enough mana!", self); } else { self.mana := self.mana - 50; }` - **[max_mana]** - Maximum magic: `mana_pct := (self.mana * 100) / self.max_mana; sendtext("Mana: " + itoa(mana_pct) + "%", self); }` - **[charflags]** - Character flags: `if (isset(activator.charflags, CHAR_OUTLAW)) { send("You are an outlaw!", activator); }` - **[crimes]** - Criminal record: `if (activator.crimes > 0) { send("You have " + itoa(activator.crimes) + " crimes on record.", activator); }` - **[height]** - Physical size: `height_diff := pc.height - armor.height; // Check size compatibility` - **[hometown]** - Player origin: `link(player, findroom(player.hometown)); // Send player home` - **[lifespan]** - Maximum age: `if (age > self.lifespan) { sendtext("You have exceeded your lifespan!", self); }` - **[natural_armour]** - Base armor: `if (npc.natural_armour > 50) { sendtext("This NPC has heavy natural armor.", self); }` - **[position_update]** - Update position: `target.hp := target.hp - damage; position_update(target); // Update after HP change` - **[reset_level]** - Reset to level 1: `reset_level(player); // Reset character to starting level` - **[reset_race]** - Reset race attributes: `reset_race(player); // Reset weight, height, age, lifespan, training costs` - **[reset_vlevel]** - Reset virtual level: `reset_vlevel(player); // Reset virtual level to 1` - **[weapon_costs]** - Weapon training costs: `cost := self.weapon_costs[WPN_SWORD]; if (money >= cost) { sendtext("Can afford sword training!", self); }` - **[weapon_levels]** - Weapon skill levels: `level := self.weapon_levels[WPN_SWORD]; sendtext("Sword skill level: " + itoa(level), self); }` - **[weapons]** - Weapon skills: `skill := self.weapons[WPN_SWORD]; if (skill > 0) { sendtext("You know sword fighting!", self); }`
- PC fields
- **[profession]** - PC properties: `if (activator.profession == PROFESSION_THIEF) { sendtext("Welcome, shadow walker!", activator); }` - **[guild]** - PC properties: `if (activator.guild == "thief") { sendtext("Welcome, brother of the shadows!", activator); }` - **[pcflags]** - Player flags: `if (isset(self.pcflags, PC_PK_RELAXED)) { sendtext("You are signed up for player killing.", self); }` - **[playtime]** - Time played: `hours_played := self.playtime / 3600; sendtext("You have played for " + itoa(hours_played) + " hours.", self); }` - **[quests]** - Quest list: `found_quest := "dragon_slaying" in self.quests; if (found_quest) { sendtext("You have the dragon quest!", self); }` - **[switched]** - Character control: `controlled := self.switched; if (controlled) { sendtext("You are controlling: " + controlled.name, self); }`
- NPC fields
- **[npcflags]** - NPC properties: `npcflags(npc, npc.npcflags | NPC_AGGRESSIVE); // Make NPC aggressive`
- String Processing
- **[length]** - Get string length: `l := length("hello"); // == 5` - **[in]** - String/list membership: `if ("fox" in "The quick brown fox") { /* found substring */ }` - **[left]** - Extract left characters: `l := left("hello", 2); // == "he"` - **[right]** - Extract right characters: `r := right("world.txt", 3); // == "txt"` - **[strcmp]** - String comparison: `if (strcmp(password, stored) == 0) { sendtext("Access granted.", self); }` - **[replace]** - Replace text: `new := replace("fox", "cat", "The quick brown fox jumps over the lazy dog.");` - **[getword]** - Extract first word: `s:="hello sam"; w := getword(s); // w="hello", s="sam"` - **[toupper]** - Convert to uppercase: `shout := toupper("help!"); // == "HELP!"` - **[tolower]** - Convert to lowercase: `answer := tolower(argument); if (answer == "yes") { sendtext("Great!", self); }` - **[strncmp]** - Compare with length limit: `if (strncmp("hello", "help", 3) == 0) { sendtext("Strings match for first 3 chars.", self); }` - **[mid]** - Extract substring: `weapon_type := mid("longsword +2 of fire", 1, 9); // == "longsword"`
- Stringlist Processing
- **[addstring]** - Add to list: `mylist := addstring(mylist, "new_item"); // Add item to list` - **[substring]** - Remove from stringlist: `sl:={"hello","world"}; substring(sl, "world") // sl now only contains "hello"` - **[in]** - Stringlist membership: `if ("fox" in {"hello", "fox"}) { /* found substring */ }` - **[split]** - Split into stringlist: `words := split("hello world this is a test", " ");` - **[getwords]** - Get stringlist: `sl := getwords("hello world"); // sl is {"hello", "world"}, original preserved`
- Extra Description Functions
- **[addextra]** - Add descriptions: `addextra(item, 0, "A finely crafted sword with ancient runes."); // Add main description` - **[delstr]** - Delete string files: `if (delstr("news.txt")) { sendtext("News file deleted.", self); }` - **[extra]** - .extra field: `desc := item.extra.descr; // Get extra description text` - **[extraptr]** - Extra description pointer: `ex := self.extra; if (ex != null) { sendtext("Found: " + ex.descr, self); }` - **[loadstr]** - File operations: `result := loadstr("news.txt", content); if (result > 0) { sendtext(content, self); }` - **[savestr]** - File operations: `result := savestr("news.txt", message, "w"); if (result > 0) { sendtext("News saved!", self); }` - **[subextra]** - Remove extra descriptions: `subextra(item.extra, "quest_marker"); // Remove quest marker from item` - **[vals]** - Extra values: `total := extra.vals.[0] + extra.vals.[1]; // Sum numeric values from extra description`
- ⚠️ Critical Notes:**
- Stringlists use dot notation: `words.[0]` NOT `words[0]` - Convert integers for concatenation: `"Value: " + itoa(num)` - substring() removes from stringlists, doesn't extract substrings
- 💬 **Communication System**
- Communication Functions
- **[sendtext]** - Basic messaging: `sendtext("Hello, " + player_name + "!", target); // Send formatted text to specific player` - **[pagestring]** - Paginated output: `pagestring("a very long string will be paged...", self);` - **[prompt]** - User interface: `self.prompt := "[%n%h/%Hhp %m/%M]> "; // Set custom prompt with health/mana display` - **[act]** - Message formatting: `act("You hit $3n for $2d damage!", A_ALWAYS, self, null, victim, damage, TO_CHAR);` - **[sact]** - String formatting: `desc := sact("$1n is standing here.", A_SOMEONE, target, null, null, TO_CHAR); // Format message as string` - **[textformat]** - Format text: `formatted := textformat("&nHello &y" + self.name + "&nWelcome!"); // Format with escape codes`
- ⚔️ **Combat System**
- Combat Functions
- **[set_fighting]** - Set combat: `set_fighting(self, intruder); // Start fighting intruder` - **[stop_fighting]** - Stop combat: `stop_fighting(self, null); // Stop all combat` - **[attack_type]** - Combat data: `current_attack := self.attack_type; sendtext("Your current attack type is: " + weapon_name(current_attack), self);` - **[weapon_info]** - Combat data: `wpn_info := weapon_info(WPN_SWORD); if (wpn_info.[0] == 2) { sendtext("Two-handed weapon", self); }` - **[weapon_name]** - Combat data: `name := weapon_name(WPN_SWORD); sendtext("Weapon type: " + name, self);` - **[meleeattack]** - Attack actions: `damage := meleeattack(self, victim, 0, 0, TRUE); if (damage > 0) { act("You hit $1n for $2d damage!", A_ALWAYS, self, victim, damage, TO_CHAR); }` - **[attack_spell]** - Attack actions: `damage := attack_spell(SPL_FIREBALL, caster, target, 0, ""); if (damage > 0) { act("Your fireball deals " + itoa(damage) + " damage!", A_ALWAYS, caster, null, target, damage, TO_CHAR); }` - **[cast_spell]** - Spell casting: `damage := cast_spell(SPL_FIREBALL, self, self, target, 0, ""); if (damage > 0) { send("Fireball deals " + itoa(damage) + " damage!", self); }` - **[meleedamage]** - Combat calculations: `damage := meleedamage(attacker, 50); // Calculate melee damage with base 50` - **[defensive]** - Combat calculations: `defense_bonus := defender.defensive - attacker.defensive; if (defense_bonus > 50) { act("Superior defense!", A_ALWAYS, defender, null, null, TO_CHAR); }` - **[offensive]** - Combat calculations: `off_bonus := target.offensive - attacker.offensive; if (off_bonus > 20) { act("Significant offensive advantage!", A_ALWAYS, attacker, null, null, TO_CHAR); }` - **[change_speed]** - Combat timing: `change_speed(target, 12); // Delay target's next combat action by 12 pulses`
- ✨ **Affects & Magic System**
- Affect Functions
- **[addaff]** - Add affects: `addaff(player, ID_SANCTUARY, -1, 0, 0, 0, 0, 0, APF_DURATION | APF_FIRST, 0); // Permanent sanctuary` - **[isaff]** - Check affects: `if (isaff(player, ID_BLIND)) { send("You are blind!", player); }` - **[getaffects]** - Get all affects: `affects := getaffects(player); // Get list of all active affects` - **[subaff]** - Remove affects: `subaff(player, ID_BLIND); // Remove blindness affect from player`
- 💰 **Economic System**
- Money Management
- **[purse]** - Character money: `gold := purse(player, GOLD_PIECE); if (gold >= 100) { sendtext("You have enough gold!", player); }` - **[transfermoney]** - Money operations: `success := transfermoney(buyer, seller, 1000); // Transfer 1000 coins` - **[moneystring]** - Money operations: `price := moneystring(2500, 1); // "25 gold pieces" or "25 gp"`
- ⏰ **Time & Calendar**
- Time Functions
- **[mudhour]** - Game time: `hour := mudhour(); if (hour >= 6 and hour < 18) { sendtext("Shop is open!", self); }` - **[mudday]** - Game time: `h := mudhour(); d := mudday(); m := mudmonth(); y := mudyear();` - **[mudmonth]** - Game time: `h := mudhour(); d := mudday(); m := mudmonth(); y := mudyear();` - **[mudyear]** - Game time: `h := mudhour(); d := mudday(); m := mudmonth(); y := mudyear();` - **[asctime]** - Format time string: `time_str := asctime(); // "Mon Dec 5 12:34:56 2025"` - **[realtime]** - Real time: `start_time := realtime(); // Record start time in seconds` - **[weather]** - Weather conditions: `if (weather == SKY_CLOUDLESS) { sendtext("Clear skies today!", self); }` - **[wait]** - Time control: `wait(SFB_CMD, command("hello")); // Wait for "hello" command` - **[sleep]** - Sleep control: `sleep; // Put unit to sleep`
- 🛠️ **Utility Functions**
- Mathematics
- **[rnd]** - Random number: `num := rnd(1, 100); // Random 1-100` - **[openroll]** - Open roll: `roll := openroll(100, 5); // Open-ended 1-100 roll with 5% continuation chance` - **[max]** - Maximum: `result := max(a, b); // Larger of two numbers` - **[min]** - Minimum: `result := min(a, b); // Smaller of two numbers` - **[skill_name]** - Get skill name: `name := skill_name(SKI_SWORD); sendtext("Skill: " + name, self);` - **[spellindex]** - Find spell index: `idx := spellindex("fireball"); if (idx != -1) { cast_spell(idx, self, self, target, 0, ""); }` - **[spellinfo]** - Get spell details: `name := spellinfo(idx, realm, sphere, mana, off, resist, med, targ); sendtext("Spell: " + name + " (Mana: " + itoa(mana) + ")", self); }` - **[strdir]** - List DIL files: `files := strdir("spell_*"); foreach (file in files) { sendtext("Found: " + file + ".dil", self); }`
- System Utilities
- **[log]** - Logging: `log("Player " + self.name + " completed quest at " + asctime());` - **[flog]** - File logging: `flog("quests.log", self.name + " completed quest: " + quest_name, "a");` - **[logcrime]** - Crime logging: `logcrime(thief, victim, CRIME_STEALING);` - **[info]** - Player info: `email_expd := "$email" in pc.info; if (email_expd != null) email := email_expd.descr;` - **[hasfunc]** - Function check: `if (item.hasfunc == 1) { act("$1n has special powers.", A_ALWAYS, item, null, null, TO_ROOM); }` - **[access]** - Access control: `if (access(player, restricted_zone) >= 3) { sendtext("Access granted!", player); }` - **[reboot]** - System restart: `act("Rebooting MUD now...", A_ALWAYS, self, null, null, TO_ALL); reboot(0);` - **[dispatch]** - External messaging: `dispatch("discord msg #bugs @" + self.name + " bug report: " + report_text); // Send to external dispatcher` - **[gamestate]** - Player state: `gamestate(player, GS_MENU); // Put player in menu mode` - **[getcmd]** - Command lookup: `cmd := getcmd("say"); if (cmd) { sendtext("Command level: " + itoa(cmd.level), self); }` - **[getinteger]** - System values: `mana_reg := getinteger(DIL_GINT_MANAREG, player, 0); // Get mana regen rate` - **[loglevel]** - Command logging: `cmd := getcommand("kick"); if (cmd.loglevel > 0) { sendtext("Command logs at level " + cmd.loglevel, self); }`
- 📋 **Unit Operations**
- Finding & Managing Units
- **[findunit]** - Unit search: `target := findunit(self, "sword", FIND_UNIT_INVEN, null); // Find sword in inventory` - **[findsymbolic]** - Unit search: `target := findsymbolic("sword@midgaard"); if (target != null) { act("Found: $1n", A_ALWAYS, self, target, null, TO_CHAR); }` - **[findrndunit]** - Unit search: `random_pc := findrndunit(self, FIND_UNIT_ZONE, UNIT_ST_PC); if (random_pc != null) { act("Random player: $1n", A_ALWAYS, self, random_pc, null, TO_CHAR); }` - **[target]** - Symbolic search: `guard := target("guard@midgaard"); if (guard) { sendtext("Found guard: " + guard.name, self); }` - **[symname]** - Get symbolic name: `sym := symname(target); if (sym != "") { sendtext("Symbolic name: " + sym, self); }` - **[unitdir]** - File search: `files := unitdir("corpse*"); // Find all corpse files in units directory` - **[visible]** - Visibility check: `if (visible(self, target)) { sendtext("You can see " + target.name, self); }` - **[load]** - Unit creation: `item := load("sword@midgaard"); if (item != null) { link(item, self); act("Item created!", self, null, null, TO_CHAR); }` - **[delunit]** - Unit lifecycle: `if (delunit("temp_guardian.unit")) { act("Temporary unit deleted.", A_ALWAYS, self, null, null, TO_CHAR); }` - **[destroy]** - Unit lifecycle: `item := findunit(self, "sword", FIND_UNIT_IN_ME, null); if (item) { destroy(item); act("Item destroyed!", self, null, null, TO_CHAR); }` - **[clone]** - Unit lifecycle: `copy := clone(original_item); if (copy != null) { link(copy, self); }` - **[getfollower]** - Follower access: `follower := getfollower(leader, 0); // Get first follower` - **[getopponent]** - Opponent access: `opponent := getopponent(character, 0); // Get first opponent`
- Unit Validation
- **[can_carry]** - Carry validation: `result := can_carry(player, item, 1); if (result == 0) { /* can carry */ }`
- Finding & Managing Rooms
- **[findroom]** - Room operations: `room := findroom("temple@midgaard"); if (room != null) { link(self, room); }` - **[pathto]** - Room operations: `direction := pathto(self, target_room); // Get direction to target` - **[setroomexit]** - Room connections: `setroomexit(room, DIR_NORTH, target_room); // Create north exit` - **[lastroom]** - Previous location: `prev_room := lastroom(player); if (prev_room) { sendtext("You came from: " + prev_room.name, player); }`
- Object Management
- **[equip]** - Get Equipment: `weapon := equip(self, WEAR_HANDS); if (weapon != null) { sendtext("Wielding: " + weapon.name, self); }` - **[unequip]** - Remove Equipment: `weapon := equipment(self, WEAR_WIELD); if (weapon != null) { unequip(weapon); act("You unequip " + weapon.title, A_ALWAYS, self, null, null, TO_CHAR); }` - **[equipment]** - Check Equipment: `weapon := equipment(player, WEAR_WIELD); if (weapon != null) { send("You are wielding " + weapon.name, player); } else { send("You are not wielding anything.", player); }` - **[addequip]** - Add Equipment: `ring := load("ring@midgaard"); if (ring != null) { addequip(ring, WEAR_FINGER_L); }` - **[fits]** - Equipment validation: `reason := fits(player, armor, -1); if (reason != "") { sendtext("Armor doesn't fit: " + reason, player); }`
- Movement & Linking & Lighting
- **[link]** - Unit positioning: `link(item, player); // Move item to player's inventory` - **[remove]** - Inventory management: `remove(mylist, 2); // Remove element at index 2` - **[insert]** - Inventory management: `insert(mylist, 1, "new_item"); // Insert at position 1` - **[islight]** - Lighting: `if (islight(self)) { sendtext("This item provides light.", self); }`
- 🎭 **DIL Program Management**
- Program Structure
- **[dilbegin]** - Program definition: `dilbegin my_function(param : string);` - **[var]** - Variable declaration: `dilbegin f(); var i : integer; name : string; code { /* program logic here */ } dilend` - **[external]** - External function: `dilbegin f(); external integer my_func@function(u : unitptr); code { /* program logic here */ } dilend` - **[code]** - Code block: `dilbegin f(); code { /* program logic here */ } dilend` - **[dilend]** - Program definition: `} dilend`
- Program Control
- **[priority]** - Execution control: `priority; // Block other DIL programs until nopriority called` - **[nopriority]** - Execution control: `nopriority(); // Allow other DIL programs to execute again` - **[waitnoop]** - Program flow: `waitnoop; // Temporarily release secure constraints for command execution`
- Event Handling
- **[secure]** - Security control: `secure(victim, victim_fled); // Jump to victim_fled: if target disappears` - **[unsecure]** - Security control: `secure(item, :item_lost); // Process item safely; unsecure(item); // Remove protection when done` - **[on_activation]** - Event handlers: `on_activation((self.position <= POSITION_SLEEPING) or (self.position == POSITION_FIGHTING), skip); // Skip if asleep or fighting` - **[interrupt]** - Program flow: `intr_id := interrupt(SFB_MSG, argument == "help", help_handler); // Handle help requests` - **[clear]** - Program flow: `clear(intridx); // Remove interrupt handler by index`
- DIL Operations
- **[dilcall]** - Program management: `success := dilcall("heal_spell", self, 0, "minor"); // Call DIL template` - **[dilfind]** - Program management: `if (dilfind("quest_active", player)) { sendtext("Quest already in progress.", self); }` - **[dilcopy]** - Program management: `new_prog := dilcopy("guard_ai", self); // Create guard AI instance` - **[dildestroy]** - Program management: `count := dildestroy("temp_effect", self); // Remove temporary effect` - **[sendtoalldil]** - Inter-program communication: `sendtoalldil("SYSTEM: Reboot in 5 minutes!", "admin_*"); // Alert all admins` - **[store]** - Inter-program communication: `store(chest, "chest_backup." + chest.zoneidx, TRUE); // Save chest and contents` - **[restore]** - Unit restoration: `item := restore("saved_item", null); if (item != null) { link(item, self); }`
- DIL Message Functions
- **[send]** - Inter-program messaging: `send("task_complete"); pause; send("cleanup_ready"); // Send to waiting DIL programs with SFB_MSG` - **[send_done]** - Completion messages: `send_done("cast", self, null, target, 1, "fireball", null, 0); // Notify spell completion` - **[send_pre]** - Preparation messages: `result := send_pre("cast", self, null, target, 1, "fireball", null, 0); if (result == SFR_BLOCK) { sendtext("Spell blocked!", self); }` - **[sendto]** - Basic messaging: `sendto("The sword glows with ancient power.", self); // Send to all DIL programs within a unit` - **[sendtoall]** - Broadcast functions: `sendtoall("SYSTEM SHUTDOWN IN 10 MINUTES!", "sys_control"); // Send to all matching DIL programs globally` - **[sendtoalldil]** - Broadcast functions: `sendtoall("NEW SPELL AVAILABLE: fireball", "spell_*"); // Send to specific DIL programs by pattern`
- PC multi-line editing function
- **[beginedit]** - Start editing mode: `beginedit(self); wait(SFB_EDIT, self == activator); // Get edited text in argument` - **[killedit]** - Force exit editing: `killedit(self); // Stop editing session immediately` - **[editing]** - Check editing status: `if (self.editing) { sendtext("Already editing!", self); }`
- Development Tools
- **[shell]** - Execute system command: `result := shell("backup_script.sh"); // Returns 0 if thread created` - **[exec]** - Execute a command: `exec("say Hello there!", target); // Force target char to speak`
- 🌍 **World Management**
- Zone management
- **[findzone]** - Zone operations: `zone := findzone("midgaard"); if (zone != null) { sendtext("Found zone: " + zone.name, self); }` - **[zoneidx]** - Zone properties: `unit_id := self.zoneidx; // Returns "name@zone" format` - **[loadlevel]** - Zone restrictions: `if (self.level < zone.loadlevel) { sendtext("Zone requires level " + zone.loadlevel, self); }` - **[resetmode]** - Zone properties: `mode := zone.resetmode; // Get zone reset mode (read-only)` - **[resettime]** - Zone properties: `zone.resettime := 30; // Set reset interval to 30 minutes` - **[zonereset]** - Zone resets: `zonereset(zone); // Reset all NPCs and objects in zone` - **[help]** - Zone information: `target_zone := findzone(zone_name); help_text := target_zone.help; act(help_text, A_ALWAYS, self, null, null, TO_CHAR);` - **[roomcount]** - Zone properties: `count := zone.roomcount; // Get number of rooms in zone` - **[rooms]** - Zone properties: `room := zone.rooms; while (room) { sendtext(room.name, self); room := room.next; }` - **[npcs]** - Zone properties: `npc := npcs(zone); while (npc) { sendtext(npc.name, self); npc := npc.next; }` - **[npccount]** - Zone properties: `count := npccount(zone); // Get number of NPCs in zone` - **[objs]** - Field Object operations: `zone := findzone("midgaard"); first_obj := zone.objs; while (first_obj != null) { act("Zone object: $1n", A_ALWAYS, self, first_obj, null, TO_CHAR); first_obj := first_obj.next; }` - **[objcount]** - Object operations: `zone := findzone("midgaard"); count := zone.objcount; act("Zone has $1d objects", A_ALWAYS, self, count, null, TO_CHAR);` - **[creators]** - Zone authors: `if (zone.creators) { foreach (name in zone.creators) { act("Creator: " + name, self); } }` - **[fname]** - Zone filename: `zone_file := zone.fname; sendtext("Zone file: " + zone_file, self);` - **[notes]** - Zone documentation: `zone.notes := "Administrative zone - restricted access"; // Set zone notes`
- List management
- **[global_head]** - Inter-program communication: `first_unit := global_head(); // Get first unit in global list` - **[room_head]** - Room operations: `room := room_head(); while (room) { sendtext(room.name, self); room := room.next; }` - **[obj_head]** - Object operations: `first_obj := obj_head(); while (first_obj != null) { act("Object: $1n", A_ALWAYS, self, first_obj, null, TO_CHAR); first_obj := first_obj.next; }` - **[pc_head]** - PC operations: - **[npc_head]** - NPC operations: `npc := npc_head(); while (npc) { sendtext(npc.name, self); npc := npc.next; }` - **[zone_head]** - Zone operations: `first_zone := zone_head(); // Get first zone in global list` - **[zhead]** - Zone operations: `zone := zhead(); while (zone) { sendtext(zone.name, self); zone := zone.next; }` - **[chead]** - Command operations: `cmd := chead(); while (cmd) { act("Command: " + cmd.name, A_ALWAYS, self, null, null, TO_CHAR); cmd := cmd.next; }` - **[command_head]** - Command list: `cmd := command_head(); while (cmd) { act("Found: " + cmd.name, self); cmd := cmd.next; }` - **[ghead]** - Global list: `unit := ghead(); while (unit) { sendtext(unit.name, self); unit := unit.gnext; }`
- List Operations
- **[next]** - Next element: `item := item.next; // Move to next item in list` - **[gnext]** - Global next: `unit := unit.gnext; // Move to next unit in global list` - **[gprevious]** - Previous element: `prev_unit := unit.gprevious; // Get previous unit in global list`
- 🔐 **Security & Validation**
- Security Functions
- **[check_password]** - Account management: `if (check_password(player, argument)) { send("Password verified.", player); } else { send("Invalid password.", player); }` - **[set_password]** - Account management: `set_password(player, "newpass123"); // Change player password` - **[delete_player]** - Account management: `if (isplayer("troublemaker")) { delete_player("troublemaker"); }` - **[access]** - Security control: `if (access(player, restricted_zone) >= 3) { sendtext("Access granted!", player); }` - **[isplayer]** - Player validation: `if (not isplayer(player_name)) { sendtext("Player not found.", self); }`
- God Functions
- **[switch]** - Character control: `switch(self, target); sendtext("You have switched to " + target.name + ".", self);`
- Pay to Play functions (not in use)
- **[paycheck]** - Character money: `if (paycheck(shop_room, player)) { sendtext("Access granted!", player); }` - **[acc_balance]** - Bank accounts: `balance := player.acc_balance; if (balance >= 10000) { sendtext("You have $100+ in your account!", player); }` - **[acc_total]** - Bank accounts: `total_credit := player.acc_total; if (total_credit >= 50000) { sendtext("Premium account!", player); }` - **[acc_modify]** - Bank accounts: `acc_modify(player, -5000); // Remove $50 from account` - **[payonly]** - Payment status: `if (zone.payonly) { sendtext("This zone requires payment to access.", self); }`
- Color Functions (not in use)
- **[addcolor]** - Add colors: `addcolor(player, "&red"); // Add red to player's color palette` - **[delcolor]** - Remove colors: `delcolor(player, "&red"); // Remove red from player's color palette` - **[changecolor]** - Modify colors: `if (changecolor(player, "clan_color", "&c+w&bn")) { send("Clan color updated!", player); }` - **[getcolor]** - Color processing: `processed := getcolor("&brBold red text&n"); // Process color codes`
- 🏷️ **Constants & Types** (vme/include/vme.h and vme/include/values.h)
- SFB
- 🏷️ **Constants & Types** (vme/include/vme.h and vme/include/values.h)
These are #define constants from the source headers. Use them in DIL for flags, types, etc. (e.g., `if (self.type == UNIT_ST_PC)`). Values are numeric; descriptions are based on code comments/context.
- State Function Bits (SFB_*)
Used in wait(), interrupt(), etc., for event triggers.
| Constant | Description | |----------|-------------| | SFB_CMD | Normal command trigger. | | SFB_TICK | Tick-message trigger. | | SFB_DEAD | Death-message trigger. | | SFB_COM | Combat-event trigger. | | SFB_MSG | Message-event trigger. | | SFB_SAVE | Save-event trigger. | | SFB_DONE | Indicates something has been done. | | SFB_ALL | (SFB_CMD \| SFB_TICK \| SFB_DEAD \| SFB_COM \| SFB_MSG \| SFB_SAVE) | All common triggers. |
- Unit Types (UNIT_ST_*)
For unit.type checks (e.g., in findunit()).
| Constant | Description | |----------|-------------| | UNIT_ST_NPC | Non-player character. | | UNIT_ST_PC | Player character. | | UNIT_ST_ROOM | Room. | | UNIT_ST_OBJ | Object. |
- Positions (POSITION_*)
For char.position, defaultpos, etc.
| Constant | Description | |----------|-------------| | POSITION_DEAD Dead (use corpse). | | POSITION_MORTALLYW | Mortally wounded. | | POSITION_INCAP | Incapacitated. | | POSITION_STUNNED | Stunned. | | POSITION_SLEEPING | Sleeping. | | POSITION_RESTING | Resting. | | POSITION_SITTING | Sitting. | | POSITION_FIGHTING | Fighting (do not set manually). | | POSITION_STANDING | Standing. |
- Character Flags (CHAR_*)
For char.charflags.
| Constant | Description | |----------|--------------| | CHAR_PROTECTED | Protected by law system. | | CHAR_LEGAL_TARGET | Internal (do not use). | | CHAR_OUTLAW | Outlaw status (do not use). | | CHAR_GROUP | In group (do not use). | | CHAR_BLIND | Blinded. | | CHAR_HIDE | Hidden. | | CHAR_MUTE | Mute. | | CHAR_SNEAK | Sneaking. | | CHAR_DETECT_ALIGN | Detect alignment (PCs only). | | CHAR_DETECT_INVISIBLE | See invisible. | | CHAR_DETECT_MAGIC | Detect magic (PCs only). | | CHAR_DETECT_POISON | Detect poison (PCs only). | | CHAR_DETECT_UNDEAD | Detect undead (PCs only). | | CHAR_DETECT_CURSE | Detect curse (PCs only). | | CHAR_DETECT_LIFE | Detect life (PCs only). | | CHAR_WIMPY | Wimpy mode (flee low HP). | | CHAR_SELF_DEFENCE | Internal use (do not use). | | CHAR_PEACEFUL | No auto-attack. | | CHAR_KILL_SELF | Self-kill detection. |
- Object Flags (OBJ_*)
For obj.objectflags.
| Constant | Description | |----------|-------------| | OBJ_NO_UNEQUIP |Cannot unequip. | | OBJ_TWO_HANDS | Requires two hands. | | OBJ_NOCOVER | Doesn't cover body part. | | OBJ_NO_DUAL | Cannot dual-wield. | | OBJ_VEHICLE | Vehicle type (extended). |
(Truncate for brevity; full list includes OBJ_MAGIC from snippets.)
- Room Flags (ROOM_*)
For room.roomflags (e.g., UNIT_FL_INDOORS in zone).
| Constant | Description | |----------|-------------| | UNIT_FL_INDOORS Indoors (no weather). | | UNIT_FL_NO_WEATHER | No weather effects. | | UNIT_FL_NO_MOB | No mobiles allowed. | | ROOM_FL_DARK | Dark
- Flags
- **[ROOM_*]** - Room flags - **[CMD_*]** - Command flags - **[SPL_*]** - Spell flags - **[SKI_*]** - Skill flags - **[ABIL_*]** - Ability flags
- Zone Macros (vme/include/wmacros.h vme/include/monster.h)
Used in .zon files (e.g., for NPC/object defs). Not directly in DIL, but referenced. Easy way to create all kind of standard objects or characters.
| Macro | Expansion/Usage | Description | |-------|-----------------|-------------| | M_AVG_HUMAN(level, sex) | (Sets height, weight, etc. based on level/sex) | Average human NPC stats. | | WEAPON_DEF(wpn_type, def_bonus, att_bonus) | (Sets value[0-4] for weapons) | Defines weapon type/damage. | | BONUS_AVERAGE | 0 | Average bonus (use -5 to 5 for poor/excellent). | | OUT_DARK_NON_NOON | (Sets outside_descr based on time) | Dark description except noon. |
- 🚨 **Critical Compilation Guidelines**
- Reserved Keywords (⚠️ IMPORTANT)
The following words are **reserved** in DIL and **cannot be used as variable names**: - `opponent` - **Function name, not variable** - use `opp`, `target_unit`, or `target` instead - `for` - **Not supported** - use `while` loops instead - Other reserved words may exist during compilation
- Variable Naming Conventions
- **Maximum name length:** 16 characters for unit names - **Use descriptive names:** `target_unit` instead of `target` - **Use underscores:** `current_room` for readability - **Avoid all reserved keywords** listed above
- Common DIL Patterns and templates
These examples demonstrate common DIL idioms using the language features above. Use them as starting points for your scripts.
- **Infinite Event Loop (NPC AI):** ```
// Skip if NPC is busy on_activation((self.position <= POSITION_SLEEPING) or (self.position == POSITION_FIGHTING), skip);
- start:
wait(SFB_CMD | SFB_MSG, activator.type == UNIT_ST_PC); // Wait for any command or message from PC if (not visible(self, activator)) // We deal with players we can see goto start; u := activator; secure(u, start); // If pc disappears we jump to start // Handle event...
- cleanup:
unsecure(u); goto start;
- **Secure Target Handling (Prevent Null Pointers):**
target := findunit(self, "playername", FIND_UNIT_SURRO, UNIT_ST_PC);
if (target != null) {
secure(target, lost_target);
// Safe operations on target...
}
unsecure(target);
- lost_target:
log("Target lost");
```
- **Random Decision Tree:** ``` chance := rnd(1, 100); if (chance <= 30) {
// Action 1
} else if (chance <= 70) {
// Action 2
} else {
// Action 3
}
- **Quest Flag Check/Update (Using Extra):** if ("rabbit quest ongoing" in activator.quests) {
sendtext("Bring me that skin.", activator);
} else if ("rabbit quest complete" in activator.quests) {
sendtext("Quest already complete!", activator);
} else {
addextra(pc.quests, {"rabbit quest ongoing"}, "kill the killer rabbit");
} ```
- **Coordinated NPC Movement and Event Triggering (Multi-NPC AI):** ```
external
integer walk_room@function (s:string,i:integer); // Import pathfinding function
var
tf:integer; // Temp for walk result
target_room: string; // Destination symbolic name
code
{
// Skip if busy or invalid state
on_activation((self.position <= POSITION_SLEEPING) or (self.position == POSITION_FIGHTING), skip);
- start:
// Wait for trigger (e.g., message from another DIL)
wait(SFB_MSG, argument == "raid_start");
// Move to target with pathfinding
target_room := "cabin@haon_dor";
tf := walk_room@function(target_room, 4); // 4 = speed/mode; adjust as needed
if (tf == TRUE)
{
// Perform action at destination
exec("emote raids the cabin!", self);
pause;
// Signal completion to other NPCs/DILs
sendto("raid_complete", findsymbolic("orc_chief@haon_dor"));
}
// Return or loop
tf := FALSE;
while (tf == FALSE)
tf := walk_room@function("orc_cave@haon_dor", 4);
goto start; // Back to waiting loop
}
// The orc chief could send a message like this
// sendto("raid_start", findsymbolic("orc1@somezone"));
``` - **Advanced pattern for handcuffing player** ``` dilbegin cuff_target(deputy : unitptr, targ : unitptr); var
cuffs : unitptr;
code {
act("You cuff $3n.", A_SOMEONE, deputy, null, targ, TO_CHAR);
follow(targ, deputy); // Force target to follow
unequip(equipment(targ, WEAR_WRIST_R)); // Remove existing wrist item
cuffs := load("cuffs@midgaard"); // Now inside the deputy
link(cuffs, targ); // Move cuffs to target
addequip(cuffs, WEAR_WRIST_R); // Equip cuffs on the char it is .inside
dilcopy("cuffed@midgaard("+self.name+")", targ); // Apply restraint DIL to cuffs
return;
} dilend ```
- **Handcuffs blocks all DILs** ``` // The person with this DIL can only execute a few commands // fnpri is the execution priority of the DIL (the order in which they are executed on events) // unique means there can not be a duplicate of this running, only one copy per unit // aware means events from the unit itself also trigger events (normally excluded) dilbegin fnpri(FN_PRI_RESCUE-2) aware unique cuffed2(depname : string); var
i : integer; u : unitptr;
code {
interrupt(SFB_COM, activator == self, broken); // If in combat, stop this
:loop: wait(SFB_CMD, TRUE);
:postloop:
if (activator == self.master)
{
if (activator.position < POSITION_STANDING)
goto loop;
/* Do this trickery to allow the cuffed person to follow! */
if (command("north") or command("east") or command("south") or
command("west") or command("up") or command("down"))
{
wait(SFB_CMD, TRUE);
if (activator == self)
{
if (command("north") or command("east") or
command("south") or command("west") or
command("up") or command("down"))
{
goto loop;
}
}
goto postloop;
}
goto loop;
}
else if (activator == self)
{
if (command("look") or command("say") or
command("ask") or command("tell"))
goto loop;
else
{
block; // Block execution of all other commands
act(depname + " prevents you from removing the hand cuffs.",
A_ALWAYS, self, null, null, TO_CHAR);
}
}
goto loop;
:broken:
i := dildestroy("cuffed@midgaard", self);
quit;
} dilend
// Make sure the handcuffs blocks execution of other DILs. // So it's one worse priority (-1) than "cuffed2" (-2). // meaning cuffed2 will run, and cuffed will block. // dilbegin fnpri(FN_PRI_RESCUE-1) aware unique cuffed(depname : string); var
cuffs : unitptr; deputy : unitptr; i : integer;
code {
cuffs := equipment(self, WEAR_WRIST_R); secure(cuffs, broken);
deputy := self.master; secure(deputy, broken);
if ((deputy == null) or (cuffs == null))
{
log("Cuffed lost either deputy or cuffs");
goto broken;
}
heartbeat := PULSE_SEC * 1;
:loop:
if (cuffs.equip != WEAR_WRIST_R)
goto broken;
priority; // Block all DILs running with a worse priority on this unit
wait(SFB_TICK|SFB_CMD, activator == self);
goto loop;
:broken:
i := dildestroy("cuffed2@midgaard", self);
quit;
} dilend ```
- **Guard routine utilizing vme/zone/quest.zon DIL functions**
```
dilbegin guardroutine(guardloc: string, dayguard: integer);
external DailyRoutine@quests(sl : stringlist, sarg2 : string);
var sch: intlist;
code {
if (dayguard) sch := {5, 18}; // Day schedule
while (TRUE) {
pause;
if (mudhour == sch.[0]) { // Wake/move at hour
DailyRoutine@quests({"wake", "walkto::$2t", ...}, "");
}
if (mudhour == sch.[1]) { // Evening routine
DailyRoutine@quests({"walkto::grunting_inn@midgaard", "buy beer", "sleep"}, "");
}
}
} dilend ```
- **Corpse scavenger** ``` dilbegin janitors(rate: integer); var trash: unitptr; code {
foreach (UNIT_ST_OBJ, trash) { // Scan surroundings for objects
if (trash.objecttype == ITEM_CONTAINER && isaff(trash, ID_CORPSE)) {
// No need to secure item, foreach does it automatically
exec("get all from corpse", self);
// Scavenger would quickly be unable to carry more
}
}
} dilend ```
- ZONE example with DIL examples
``` /* filename minimal-zone password testpass changedby AI Assistant EmailAdd request compile version 1 END HEADER*/
- include <macros.h>
%zone title "Minimal Test Zone@Mainland" lifespan 10 reset RESET_ANYHOW creators {"ai_assistant"}
notes "A minimal zone demonstrating all major Haon-Dor zone fields and expressions. Contains two comprehensive DIL examples: 1. comprehensive_demo() - Advanced DIL patterns for LLM learning 2. simple_demo() - Basic DIL patterns for beginners Both examples demonstrate key concepts: variables, control flow, string/integer operations, unit manipulation, combat, time functions, and more."
help "This is a minimal test zone created to demonstrate all of the key zone fields and expressions used in Haon-Dor. Contains comprehensive DIL examples perfect for learning DIL programming patterns. Zone by AI Assistant."
%dil /* ========================================================================
COMPREHENSIVE DIL EXAMPLE - Perfect for LLM Learning This example demonstrates the most important DIL concepts and patterns ======================================================================== */
dilbegin demo(); var
target_player : unitptr; // Unit pointer for finding players random_num : integer; // For random number generation message_text : string; // For string manipulation item_found : unitptr; // For object manipulation oppo : unitptr; // For combat operations plyr : unitptr; health_pct : integer; // For calculations current_hour : integer; // For time functions intr_hdl : integer; sl : stringlist; i : integer;
code {
// Set heartbeat to check every 5 seconds heartbeat := PULSE_SEC * 5; // Skip execution if NPC is asleep or fighting on_activation((self.position <= POSITION_SLEEPING), skip); interrupt(SFB_COM, activator == self, imfighting);
:main_loop:
// ====================================================================
// 1. COMMAND WAITING - Basic interaction pattern
// ====================================================================
wait(SFB_CMD, command("say"));
if (not ("hello" in argument) and not ("greet" in argument))
goto main_loop;
if (activator.type == UNIT_ST_PC)
{
plyr := activator;
secure(plyr, main_loop);
// String concatenation with itoa() for numbers
message_text := "Greetings, " + plyr.name + "! Your level is " + itoa(plyr.level) + ".";
exec("say " + message_text, self);
pause; // After this pause plyr is null
// Random number generation and conditional logic
random_num := rnd(1, 100);
if (random_num > 70)
{
exec("emote smiles warmly.", self);
pause;
}
else if (random_num > 30)
{
exec("emote nods politely.", self);
pause;
}
else
{
exec("emote watches you with interest.", self);
pause;
}
}
// ====================================================================
// 2. TIME FUNCTIONS - Demonstrate game time awareness
// ====================================================================
current_hour := mudhour;
sl := {"say Good morning to you!", "say Good afternoon!", "say What a lovely time to be alive!"};
if (current_hour >= 6 and current_hour < 12)
i := 0;
else if (current_hour >= 12 and current_hour < 18)
i := 1;
else
i := 2;
exec(sl.[i], self); pause;
// ====================================================================
// 3. UNIT SEARCHING - Find and interact with nearby units
// ====================================================================
target_player := findrndunit(self, FIND_UNIT_SURRO, UNIT_ST_PC);
if (target_player != null)
{
// Health percentage calculation
health_pct := (target_player.hp * 100) / target_player.max_hp;
if (health_pct < 50)
{
act("$1n says 'You look wounded! Let me help you.'",
A_SOMEONE, self, null, null, TO_ROOM);
pause;
// Healing demonstration
target_player.hp := target_player.hp + 10;
act("$1n touches $3n, who feels slightly better.",
A_SOMEONE, self, null, target_player, TO_NOTVICT);
act("$1n touches you, and you feel slightly better.",
A_SOMEONE, self, null, target_player, TO_VICT);
}
}
// ====================================================================
// 4. OBJECT MANIPULATION - Search for and handle items
// ====================================================================
foreach (UNIT_ST_OBJ, item_found)
{
if (item_found.outside == self) // Item is on the NPC
{
if (item_found.objecttype == ITEM_WEAPON)
{
exec("say I am armed with " + item_found.title, self);
break; // Exit foreach loop after finding first weapon
}
}
else if (item_found.outside == self.outside) // Item is in the same room
{
if (item_found.objecttype == ITEM_CONTAINER)
{
exec("say I see a container here: " + item_found.title, self);
break;
}
}
}
pause;
// ====================================================================
// 5. COMBAT OPERATIONS - Basic combat AI
// ====================================================================
if (self.position == POSITION_FIGHTING)
{
oppo := self.fighting;
if (oppo != null)
{
// Combat taunts based on oppo type
if (oppo.type == UNIT_ST_PC)
{
exec("say You should not have challenged me!", self);
}
else
{
exec("say Die, foul creature!", self);
}
pause;
// Wait for combat to end
while (self.position == POSITION_FIGHTING)
pause;
}
}
// ====================================================================
// 6. MOVEMENT AND EXPLORATION - Room navigation
// ====================================================================
random_num := rnd(1, 10);
if (random_num <= 3) // 30% chance to move
{
exec("say I think I'll take a little walk.", self);
pause;
// Try random directions (simplified example)
random_num := rnd(1, 4);
if (random_num == 1)
exec("north", self);
else if (random_num == 2)
exec("south", self);
else if (random_num == 3)
exec("east", self);
else
exec("west", self);
pause;
}
// ====================================================================
// 7. ALIGNMENT AND ROLEPLAYING - React to player alignment
// ====================================================================
if (plyr.type == UNIT_ST_PC)
{
if (plyr.alignment < -300)
{
exec("say Your evil nature disturbs me.", self);
pause;
exec("emote watches you warily.", self);
}
else if (plyr.alignment > 300)
{
exec("say It is good to see a noble soul like yourself!", self);
pause;
exec("emote smiles warmly.", self);
}
else
{
exec("say You seem to walk the middle path.", self);
pause;
}
}
// ====================================================================
// 8. MEMORY AND STATE - Simple quest/interaction tracking
// ====================================================================
// Check if player has a specific quest flag (simplified example)
if (plyr.type == UNIT_ST_PC)
{
// This would normally check quest flags, simplified for demo
exec("say I remember meeting you before.", self);
pause;
}
unsecure(plyr); // Loop back to main loop goto main_loop;
- imfighting:
exec("say Darn it! I hate being in combat all the time", self);
// We could clear the interrupt here with
// clear(intr_hdl);
// But let's keep it coming.
goto main_loop;
} dilend
/* ========================================================================
SIMPLE DIL EXAMPLE - Basic patterns for beginners This demonstrates fundamental DIL concepts in a simple, clear way ======================================================================== */
dilbegin basic(); var
player : unitptr; // Unit pointer for players roll : integer; // Random number storage greeting : string; // String manipulation
code {
// Check every 10 seconds
heartbeat := PULSE_SEC * 10;
// Don't run if asleep or fighting
on_activation((self.position <= POSITION_SLEEPING) or
(self.position == POSITION_FIGHTING), skip);
:start:
// Wait for any command from players or npcs
wait(SFB_CMD, TRUE);
// Only respond to players, not other NPCs
if (activator.type != UNIT_ST_PC)
goto start;
player := activator;
// If the char leaves, reset and go back to start // right after the exec, player can be reset // unless we add this secure secure(player, start);
// Basic greeting with string concatenation
greeting := "Hello, " + player.name + "!";
exec("say " + greeting, self);
pause;
// We cannot use activator down here because it'll be null
// Random response pattern
roll := rnd(1, 3);
if (roll == 1)
{
exec("say It's a fine day for adventure!", self);
}
else if (roll == 2)
{
exec("hug " + player.name, self);
}
else
{
exec("say May fortune favor you!", self);
}
pause;
// Simple health check
if (player.hp < player.max_hp / 2)
{
exec("say You look wounded - be careful!", self);
pause;
}
unsecure(player);
// Loop back goto start;
} dilend
%rooms
/* Minimal room demonstrating all key room fields */ minimal_room
names {"minimal room", "room"} title "A Minimal Room" descr "This is a minimal room demonstrating all the key room fields and expressions used in Haon-Dor. The room is simple but contains examples of all major room properties."
extra {"wall", "walls"} "The walls are made of simple stone blocks."
extra {"floor", "ground"} "The floor is made of rough wooden planks."
extra {"ceiling"} "The ceiling is low and made of dark wooden beams."
movement SECT_INSIDE gmap(100,100)
flags {UNIT_FL_INDOORS, UNIT_FL_NO_WEATHER, UNIT_FL_NO_MOB} OUT_DARK_NON_NOON
end
/* Second room for exit demonstrations */ minimal_room2
title "Another Minimal Room" descr "This is the second minimal room, used to demonstrate various exit types and room connections."
extra {"ceiling"} "The ceiling is low and made of dark wooden beams."
movement SECT_INSIDE gmap(100,101)
flags {UNIT_FL_INDOORS, UNIT_FL_NO_WEATHER}
end
/* Third room for special terrain */ minimal_room3
title "Room with Special Terrain" descr "This room demonstrates special terrain types and movement sectors. The ground here is different from other rooms."
extra {"terrain", "ground"} "The terrain here is rough and uneven, making movement difficult."
movement SECT_FOREST gmap(100,99)
OUT_DARK_NON_NOON
end
%objects
/* Minimal object demonstrating all key object fields */ minimal_key
names {"key", "minimal key"} title "a small minimal key" descr "This is a small, simple key that seems to fit minimal locks."
extra {"lock", "mechanism"} "The key has a simple mechanism with basic teeth."
extra {"$identify"} "This key can be identified to reveal its purpose."
cost 10 IRON_PIECE rent 1 IRON_PIECE
manipulate {MANIPULATE_TAKE, MANIPULATE_HOLD} type ITEM_KEY
weight 1
bright 0
key minimal_key
end
/* Weapon example */ minimal_sword
names {"sword", "minimal sword"} title "a minimal sword" descr "This is a simple, unadorned sword with minimal decoration."
extra {"blade", "hilt"} "The blade is plain but sharp. The hilt is wrapped in basic leather."
extra {"$identify"} "This sword can be identified to reveal its craftsmanship."
extra {"$improved identify"} "This sword can be identified to reveal its craftsmanship."
WEAPON_DEF(WPN_SCIMITAR, BONUS_AVERAGE, BONUS_AVERAGE)
cost 50 IRON_PIECE rent 5 IRON_PIECE flags {UNIT_FL_MAGIC}
weight 10 bright 0
end
/* Container example */ minimal_chest
names {"chest", "minimal chest"} title "a minimal chest" descr "This is a simple wooden chest with minimal decoration."
extra {"lock", "hinges"} "The chest has a simple lock and basic metal hinges."
extra {"$identify"} "This chest can be identified to reveal its contents capacity."
cost 25 IRON_PIECE rent 2 IRON_PIECE
manipulate {MANIPULATE_TAKE, MANIPULATE_HOLD} type ITEM_CONTAINER
weight 15
bright 0
capacity 50 key minimal_key
end
%mobiles
/* Minimal NPC demonstrating all key NPC fields */ minimal_guard
names {"guard", "minimal guard"} title "a minimal guard" descr "This is a simple guard with minimal equipment and basic appearance."
extra {"armor", "weapon"} "The guard wears basic leather armor and carries a simple sword."
extra {"$identify"} "This guard can be identified to reveal his combat capabilities."
dilcopy demo@minimal_zone();
M_AVG_HUMAN(5, SEX_MALE)
alignment 100 exp 100
position POSITION_STANDING end
/* Second NPC for variety */ minimal_merch
names {"merchant", "minimal merchant"} title "a minimal merchant" descr "This is a simple merchant with basic goods and minimal haggling skills."
extra {"goods", "wares"} "The merchant has a small selection of basic goods for sale."
extra {"$identify"} "This merchant can be identified to reveal her shop inventory."
dilcopy basic@minimal_zone(); dilcopy qstSimpleQuest@quests("Merchant Quest",
{"say I need a specific key - or ANY kind of carrot! Can you help?",
"say Please bring me the key quickly!"},
{"say Thanks for the key!"},
"Fetch a key for minimal merchant in minimal world.",
{"thank $1N",
"emote adores his new key."},
500, 1 * COPPER_MULT, {""}, "");
// The empty stringlist {""} could be a custom DIL function to validate each item, // e.g. if a barrel is more than half full of water or if it's a real boat "boatcheck@quests". // By default it's just checking name matching dilcopy qstItemsWanted@quests(QUEST_MY_FETCH, {"carrot", "minimal_key@minimal_zone"}, {""},
{"say Perfect, that's just the key I needed!"},
"say I need a specific key, not that garbage!");
M_AVG_HUMAN(3, SEX_FEMALE)
alignment 0 exp 50
position POSITION_STANDING end
%reset
/* Reset actions demonstrating all key reset expressions */
/* Load NPCs */ load minimal_guard into minimal_room max 1 {
equip minimal_sword position WEAR_WIELD max 1
}
load minimal_merch into minimal_room2 max 1
/* Load objects */ load minimal_key into minimal_guard max 1 load minimal_chest into minimal_room max 1 load minimal_key into minimal_chest max 1 load minimal_sword into minimal_room max 1
/* Door states */ door minimal_room NORTH {EX_OPEN_CLOSE}
/* Local objects (zone-wide) */ load minimal_key local 5
/* Zone max objects */ load minimal_sword zonemax 10
/* Zone reset time */
%end ```