Blogs

New Hedit 5/8/2011

No idea what version this would be. I guess 3.0

This is has been sorely lacking on my part. This is a complete Re-write of Hedit. It is based off the player file and index system. This will change hedit/help as you know it. I have removed all legacy parts of the code. I would leave the old in and hand patch this. This way you can load the original help files, then write a script to save out the files in the new format. It is how I did it, and it took less then an hour. I will not write you a script, I do not have time to do that. Unique to this is the master/child record. I deal with tons of databases on top of my network engineering duties. It became clear that having duplicate entries for the same damn entry was pointless. So I made it so there was a master(parent) record, and a child record. The child has 2 lines to it, and points back to the master(parent) record. do_help is now different, it performs a search on the keyword you provide, which 1 exception. If you do help files manually, you can hedit "reload" and it will reload the help index for you. You can also delete a help entry, but this is done via hedit . Option 5 will be delete entry? It will delete out the help entry, then delete the help index entry and reload the index.
do_help will call the keyword from the index, it will load it, if it see's this is a master record, it will load the help file then free the structure. If it see's the entry is a child record, it will immediately go to the child index keyword and look it up. It will continuously load the help entry, until it finds the master record. I recommend just give it the master right off the bat. I have run this through Valgrind, and the 2 mem leaks I had, are gone now.
I also put in find_help which was based off of the spell lookup function. I sped up the lookup by eliminating the word if it did not match the first 2 characters. Hence you cannot have a single character help entry.

Keep in mind, this completely changes your help files. If you just patch this file into your game, it will remove all old references to the old hedit system.

New items:

base dir>/lib/help/ =====> Where all the help files are now.
base dir>/lib/help/index ===> The index file. See below for format.
base dir>/lib/help/A-E ===> Help entries saved by first character
base dir>/lib/help/F-J
base dir>/lib/help/K-O
base dir>/lib/help/P-T
base dir>/lib/help/U-Z
base dir>/lib/help/ZZZ

Formats:
Index file: vi < base dir>/lib/help/index
ID#> < Last edit> < keywords>
~ ===> File must end with this.

A sample:
0 1303580069 midgaardmap
1 1303580173 map
2 1303580226 midgaard map
82 1303583358 dexterity
83 1303583377 dex
~

Master record:
A sample master record below. NONE is just a place holder, since a master record does not need an index keyword.
format :<1 = TRUE Master record><1 = Min Level>< Last person to edit file>< NONE>
< entry>
~ ====> Must end the entry with '~' for fread string.
$ ====> End of file.

1 1 Cunning NONE
Dexterity measures how agile a character is. It typically reduces the
delay and increases the chance of success for combat skills, as well as
modifying your armor class and affecting your rate of movement
regeneration.

~
$

Child record:
format: <0 - FALSE child record><1 = Min Level>< Last person to edit file>< Child index keywords>
A sample child record below:

Unique to this format is that it is a child record, which the 0 represents. It see this, and immediately goes to the child index keywords. There is a limit of on how many index keywords, it is what ever index is specified to be (mine is MAX_INPUT_LENGTH). It must be an active master help record, or it will not let you save it out. There are several safety features to keep the master/child relationship intact.

0 1 Cunning dexterity
$

Server Move Complete

Thanks to Welcor the move went smoothly.


Be sure to update your SVN links, the new checkout is via:

svn checkout http://tbamud.com/svn/circlemud tbamud


and you can always browse the websvn at:

http://tbamud.com/websvn


Working on moving

I am currently in the process of moving the server to a VPS on dailyvps.co.uk.


I've had the account there for a while to assess the stability, and as far as I can tell, we can expect somewhere in the range of 6-8 yearly downtimes of 4-5 hours each. This is better than at our current server, where router issues have blocked access to port 80 on several occasions, sometimes for days.


There are quite a lot of different services to move:

websites

databases

subversion repo

users


Most of the major setup of the target server has been completed - updating of php version, for instance, was quite tricky due to no readily available version of php5 for centOS on an x64 architecture. However, I'm pressed for time, and the server move will have to fit in my schedule along with family and work.


That last sentence basically meant: I'll get to it ;)


Using GDB to Debug - A Quick Guide

A common question asked by MUD owners is "My MUD keeps crashing, how do I use GDB to debug my MUD and find the problem". I hope to be able to answer that question with my quick guide below.

This is not a comprehensive tutorial that will teach you everything there is to know about GDB, but it will give you enough basic knowledge to hopefully root out the cause of any MUD crash.

What is GDB
GDB is a programmers debug tool. It allows you to run the program within a controlled environment which will 'catch' a crash, and let you examine the status of the program at the time of the crash. It does much more than just this (like letting you pause your running program, and step through it one line at a time, or even examine your crashed program days after a crash, even when it wasn't running within GDB).

Running your MUD in Debug Mode
GDB should be run on your MUD from it's root folder (not in the bin subfolder as you might expect).

Next, type gdb bin/circle to start GDB, and tell it to use your MUD executable. You will now be given the gdb prompt instead of your normal unix-shell one.

Next, type run <port> to run your mud (obviously entering a port number to use, not typing "<port>"). You should see all your normal syslog info appear on your screen, and your MUD will eventually give a message saying "No connections, going to sleep.".

You should then be able to connect to your MUD on the specified port in the normal way. Your next step is to crash the MUD. Obviously, if you have a crash bug, you might have some idea of what's causing it. When you manage this, the MUD will appear to pause, and you will get a message in GDB, and your gdb prompt will return. You can now start debugging.

Normally, the first command you type will be bt (for 'backtrace') which will show you the function tree, with the lowest level at the top. This shows the files each function call is in, and the line number it's called from. Below is an example of how this could look:


Program received signal SIGSEGV, Segmentation fault.
load_char (name=0x22c540 "Jamdog", ch=0x10ebcb48) at players.c:248
248 GET_SKILL(ch, i) = 0;
(gdb) bt
#0 load_char (name=0x22c540 "Jamdog", ch=0x10ebcb48) at players.c:248
#1 0x0045f967 in nanny (d=0x10eb62f0, arg=0x22ca50 "Jamdog") at interpreter.c:1417
#2 0x00440f80 in game_loop (mother_desc=3) at comm.c:903
#3 0x004410e0 in init_game (port=4000) at comm.c:531
#4 0x004414f6 in main (argc=1, argv=0x10011c80) at comm.c:378

Now, this may look scary and confusing, but it's relatively simple. The top line (beginning #0) is the point of the crash - it's the line that was being executed when the crash happened. Here is the breakdown of that line:

#0 - indicates the function depth. As you can see above, #0 is called from within the function at #1, and #1 is called from within the function at #2. The 'main' function will always be the last in the list.

load_char - This is the name of the function where the crash happened. Sometimes it is a function in the C core code, and not the MUD code, so look for the first function you recognise in the list.

(name=0x22c540 "Jamdog", ch=0x10ebcb48) - This shows the variables that were passed to the function. In this case, a string at memory address 0x22c540 and a char_data struct at memory address 0x10ebcb48. GDB handily shows the string for you, which was 'Jamdog'. You can see the contents of ch with the print command (see below)

at players.c:248 - This shows the file and line number where the crash happened.

You can now use the following commands to debug most problems:

  • info local - display the status of 'local' variables, variables created in the current function.
  • list - list the source code. Shows 3 or 4 lines above and below the current location.
  • print <variable> - display the staus of the specified variable (does not need to be local, and can view within structs this way - e.g. print ch->name or print *ch).
  • up - move 'up' the function tree (shown by bt) to the previous function.
  • kill - stop the current MUD program.
  • quit - exit from gdb (prompts to kill a running program)

Here is an example of these in use:

(gdb) info local
id = 0
i = 2
fl = (FILE *) 0x10011f74
fname = "plrfiles/F-J/jamdog.plr"
buf = "\204b\026aPÄ\"\000\000\000\000\000ÿ\001", '\0' , "\001"
buf2 = "X\006\000\000ðÃ\"\000\001\000\000\000$Ä\"\000(Ä\"", '\0' , "PE\02"
line = "ðé$\000í"...
tag = "þÿ\000\000\2268"
ch = (struct char_data *) 0x10ebcb48
(gdb) print *ch
$1 = {pfilepos = 50, nr = 65535, in_room = 791, was_in_room = 65535, wait = 1, player = {passwd = "Ja3pvo1SmcUB6", name = 0x8b117a0 "Jamdog", short_descr = 0x0, long_descr = 0x0, description = 0x0, bio = 0x0, title = 0x8b16308 "the Omnipresence", sex = 1 '\001', chclass = 4 '\004', level = 200, adm_level = 4, time = { birth = 1243951538, logon = 1288030404, played = 2148763}, weight = 146 '\222', height = 174 } }
(gdb) print ch->in_room
$2 = 791
(gdb) list
243 /* character initializations */
244 /* initializations necessary to keep some things straight */
245 ch->affected = NULL;
246 mudlog(NRM, LVL_GOD, TRUE, "Setting skills to zero");
247 for (i = 1; i <= MAX_SKILLS; i++)
248 GET_SKILL(ch, i) = 0;
249 mudlog(NRM, LVL_GOD, TRUE, "Setting sex");
250 GET_SEX(ch) = PFDEF_SEX;
251 GET_CLASS(ch) = PFDEF_CLASS;
252 GET_LEVEL(ch) = PFDEF_LEVEL;
(gdb) up
#1 0x0045f967 in nanny (d=0x10eb62f0, arg=0x22ca50 "Jamdog")
at interpreter.c:1417
1417 if ((player_i = load_char(tmp_name, d->character)) > -1) {
(gdb)

Running GDB on your MUD After a Crash
Obviously, you can't keep your MUD in debug mode all the time. If it crashed while you were asleep, it would 'pause' the MUD until you found it in the morning.

For that reason, modern tbaMUD releases do a 'core dump' in the event of a crash. This is basically a snapshot, or photograph of the MUD at crash time, and this can still be used by GDB to trace the problem.

The core dump is saved in your lib folder, but because it's so big (commonly 10-15Mb), only the mot recent core dump is kept.

To load the core dump into GDB, you need to be in the root folder of your MUD, and type:
gdb bin/circle lib/core
GDB will load in the normal way, but you don't need to run the MUD. You can immediately type bt to see the backtrace from crash-time, and you can use all the above commands to debug what happened. Because the MUD isn't running however, you won't be able to step-through the program (which usually isn't necessary anyway, and why I didn't add it to this guide).

New Admin and Mortal Levels

A major fundamental change has been made to the way TBA works, and although we are still ironing out the creases, I hope everyone agrees that it's a change for the better.


With the old system, MUD 'Imms' were always Imms, and were forced to log on a second, mortal character for proper testing of their zones. A few imms may have had the ability to change their level to become mortal, but this required a code change to allow them to return to normal.


The old admin levels were tacked onto the end of the mortal levels, like an afterthought of the original coders.


The new system that is now being used on TBA, and will be in the next release of the tbaMUD codebase, changes everything.


Mortal and Admin levels have been separated, and at any time, any imm can disable their godhood, becoming truly mortal for testing their shiny new zone, then can just turn their 'immness' back on. This is done by use of the new mortal command to become mortal, and the return command to return to normality.


As part of the new admin levels, admin privileges can be assigned (or removed) to any player, even mortals. You no longer have to be an Imm to build your own zone. A level 1 player could be given a zone and build privileges for that zone, and while in that zone, the player will be able to build using OLC, but outside that zone, be a normal mortal again.


Obviously, this has been a HUGE update, affecting virtually every file in the MUD source, so there may be teething problems following it's introduction. Please feel free to log any bugs you find (using bug report <header> in TBA), and I'll look into them as soon as I can.


tbaMUD 3.62 Release

Download tbaMUD 3.62 or the patch at http://tbamud.com


More bug fixes and an updated World.


Changelog:

[Sep 19 2010] - Rumble

Updated World and files for 3.62 release.

[Sep 11 2010] - Rumble

Fixed non-unix file formats and permissions. (thanks Mirad)

[Jul 26 2010] - Rumble

Corrected several bad wear flags on objects. (thanks Insomnia)

[Jul 12 2010] - Rumble

Fixed bad sector types. (thanks Insomnia)

[Jul 06 2010] - Rumble

Numerous typo fixes and World files added to SVN. (thanks Insomnia)

Fixed medit alignment menu to return you to stats menu after entry. (thanks Tink)

[Jul 03 2010] - Rumble

Fixed oedit to allow setting of UNDEFINED object type. (thanks Tink)

Fixed alias bug from using * as first character. (thanks Dio)

[Jun 20 2010] - Rumble

Fixed two memory leaks. (thanks Dio)

[Jum 19 2010] - Fizban

Changed Helpcheck from level 34 to level 32.

[Jun 07 2010] - Fizban

Enabled nohassled immortals to give cursed items to other players.

[Jun 18 2010] - Rumble

Fixed unaffect clearing ch affects and not vict. (thanks Drefs)

[May 24 2010] - Rumble

Fixed bug in redit copy duplicating people in room. (thanks Anderyu)

[May 19 2010] - Rumble

Gave Arne Troffaes proper credit for fixing Cygwin syslog rotation.

[May 16 2010] - Rumble

Fixed bash, NO_BASH flag was being ignored. (thanks Anderyu)

[Apr 22 2010] - Rumble

Fixed osetval not modifying AC on players. (thanks Rudeboyrave)

[Apr 12 2010] - Rumble

Fixed gain_exp quest reward that gave gold and not xp. (thanks Vatiken)

Mar 17 2010] - Fizban

Made non-existant objects in pfiles be skipped instead of crashing the MUD (thanks drefs)

Fixed plist overflow.

[Mar 11 2010] - Rumble

Updated do_export_zone to include shop files (thanks Kyle)

[Mar 07 2010] - Rumble

Updated do_scan. (thanks Zman)

[Feb 13 2010] - Rumble

Removed delete object option in oedit menu.

[Feb 08 2010] - Rumble

Added CIRCLE_WINDOWS check for tell m-w usage of getpid(). (thanks Kyle)

Changed zcheck to ignore object type TRASH when checking for a cost of 0.

Changed teleport level to LVL_BUILDER.

[Jan 27 2010] - Rumble

Updated World (thanks Parna) and other text files for release.

[Jan 18 2010] - Fizban

Edited scuba check so immortals with nohassle on don't need one.

[Jan 18 2010] - Rumble

Fixed bad AFF_SCUBA check for underwater rooms.


Creating a Floaty

Introduction

I wrote this guide to help people add a rather simple floating item. This code is written, tested, and works on stock tbaMUD 3.61. It may or may not work if your game is heavily modified or from an older version of CWG.

Note

To use a floating item, the player must release (keyword). It will only work on an item that is set to be a FLOATING wear item.

The Code

*** In act.item.c in wear_bitvectors, add it to the end of the list. ***
*** Note: Don't forget to add a comma after the last one! (And don't put one after your new addition...)  ***

  int wear_bitvectors[] = {
    ITEM_WEAR_TAKE, ITEM_WEAR_FINGER, ITEM_WEAR_FINGER, ITEM_WEAR_NECK,
    ITEM_WEAR_NECK, ITEM_WEAR_BODY, ITEM_WEAR_HEAD, ITEM_WEAR_LEGS,
    ITEM_WEAR_FEET, ITEM_WEAR_HANDS, ITEM_WEAR_ARMS, ITEM_WEAR_SHIELD,
    ITEM_WEAR_ABOUT, ITEM_WEAR_WAIST, ITEM_WEAR_WRIST, ITEM_WEAR_WRIST,
-    ITEM_WEAR_WIELD, ITEM_WEAR_TAKE, ITEM_WEAR_ANKLE, ITEM_WEAR_ANKLE
+   ITEM_WEAR_WIELD, ITEM_WEAR_TAKE, ITEM_WEAR_ANKLE, ITEM_WEAR_ANKLE,
+   ITEM_WEAR_FLOATING 
  };


*** In const char *already_wearing, add it to the bottom. Don't forget to pay attention to the commas! *** "You're already wearing something around both of your wrists.\r\n", "You're already wielding a weapon.\r\n", - "You're already holding something.\r\n" + "You're already holding something.\r\n", + "You're already using something as a floaty.\r\n" }; *** In *keywords, add it to the bottom. There must be a comma at the end of the last line! *** "hands", "arms", "shield", "about", "waist", "wrist", "!RESERVED!", "!RESERVED!", "!RESERVED!", "ankle", "!RESERVED!", + "floating", };

*** After do_wear, add a similar command. *** ACMD(do_release) { char arg[MAX_INPUT_LENGTH]; struct obj_data *obj; one_argument(argument, arg); if (!*arg) send_to_char(ch, "Release what?\r\n"); else if (!(obj = get_obj_in_list_vis(ch, arg, NULL, ch->carrying))) send_to_char(ch, "You don't seem to have %s %s.\r\n", AN(arg), arg); else { if (!CAN_WEAR(obj, ITEM_WEAR_FLOATING)) send_to_char(ch, "You can't release that as a floaty.\r\n"); else perform_wear(ch, obj, WEAR_FLOATING); } }

*** In constants.c in *wear_where, add it to the bottom of the list. *** *** Note: Mind the comma! *** "<worn on waist> ", "<worn around wrist> ", "<worn around wrist> ", "<wielded> ", "<held> ", "<worn around ankle> ", - "<worn around ankle> " + "<worn around ankle> ", + "<floating nearby> "

*** In *equipment_types, add it to the bottom of the list. *** "Worn around left wrist", "Wielded", "Held", "Worn on ankles", "Worn on ankles", + "Floating nearby",

*** In *wear_bits, add it to the bottom of the list. *** "WAIST", "WRIST", "WIELD", "HOLD", "ANKLE", + "FLOATING", "\n" };

*** In objsave.c, add it to the bottom of the list in the big list of checks. *** case WEAR_HOLD: if (CAN_WEAR(obj, ITEM_WEAR_HOLD)) break; if (IS_WARRIOR(ch) && CAN_WEAR(obj, ITEM_WEAR_WIELD) && GET_OBJ_TYPE(obj) == ITEM_WEAPON) break; location = LOC_INVENTORY; break; case WEAR_ANKLE_R: case WEAR_ANKLE_L: if (!CAN_WEAR(obj, ITEM_WEAR_ANKLE)) location = LOC_INVENTORY; break; + case WEAR_FLOATING: + if (!CAN_WEAR(obj, ITEM_WEAR_FLOATING)) + location = LOC_INVENTORY; + break; default: location = LOC_INVENTORY; }

*** In act.wizard.c in zarmor, add it to the list at the bottom. *** *** There should not be a comma at the end of this list. *** {ITEM_WEAR_WAIST, 10, "Belt"}, {ITEM_WEAR_WRIST, 10, "Wristwear"}, {ITEM_WEAR_HOLD, 10, "Held item"}, - {ITEM_WEAR_ANKLE, 10, "Anklewear"} + {ITEM_WEAR_ANKLE, 10, "Anklewear"}, + {ITEM_WEAR_FLOATING, 10, "Floating"}

*** In structs.h in the #defines for all the wear spots, add it to the bottom. *** *** Be sure to increment the total afterward! *** #define WEAR_WRIST_L 15 /**< Equipment Location Left Wrist */ #define WEAR_WIELD 16 /**< Equipment Location Weapon */ #define WEAR_HOLD 17 /**< Equipment Location held in offhand */ #define WEAR_ANKLE_R 18 /**< Equipment Location for right ankle */ #define WEAR_ANKLE_L 19 /**< Equipment Location for left ankle */ + #define WEAR_FLOATING 20 /**< Equipment Location for floaty */ - #define NUM_WEARS 20 + #define NUM_WEARS 21

*** In the item wear defines, add it to the bottom. *** *** Again, note the increment in NUM_ITEM_WEARS. *** #define ITEM_WEAR_WRIST 12 #define ITEM_WEAR_WIELD 13 #define ITEM_WEAR_HOLD 14 #define ITEM_WEAR_ANKLE 15 + #define ITEM_WEAR_FLOATING 16 - #define NUM_ITEM_WEARS 16 + #define NUM_ITEM_WEARS 17

*** Finish up the code by adding in the necessities for do_release...*** *** In act.h, right at the top, add after do_wield. *** ACMD(do_wield); ACMD(do_release);

*** Finally, in interpreter.c, add it in the alphabetical listing. *** { "recite" , "reci" , POS_RESTING , do_use , 0, SCMD_RECITE }, { "receive" , "rece" , POS_STANDING, do_not_here , 1, 0 }, + { "release" , "rel" , POS_RESTING , do_release , 0, 0 }, { "remove" , "rem" , POS_RESTING , do_remove , 0, 0 }, { "remort" , "remo" , POS_STANDING, do_remort , 1, 0 },



Conclusion

Not hard at all. Please feel free to comment with suggestions for additions, or point out if I've missed anything. :)

Adding Luck

Introduction

I wrote this guide to help people add in another basic stat, like Str, Dex, Cha, etc. This code is written, tested, and works on stock tbaMUD 3.61. It may or may not work if your game is heavily modified or from an older version of CWG.

Note

In most cases Luck (LCK) is added after Charisma (CHA). Following these directions to the letter will cause your player files to "break" so if you want to avoid that you may want to re-arrange where some things go.

The Code

*** In class.c find the void roll_real_abils and change some numbers so that a new table is created. ***

/* Roll the 6 stats for a character... each stat is made of the sum of the best
 * 3 out of 4 rolls of a 6-sided die.  Each class then decides which priority
 * will be given for the best to worst stats. */
void roll_real_abils(struct char_data *ch)
{
  int i, j, k, temp;
- ubyte table[6]
+ ubyte table[7]
  ubyte rolls[4];

- for (i = 0; i < 6; i++)
+ for (i = 0; i < 7; i++)
    table[i] = 0;

- for (i = 0; i < 6; i++) {
+  for (i = 0; i < 7; i++) {

    for (j = 0; j < 4; j++)

-       rolls[j] = rand_number(1, 6);
+      rolls[j] = rand_number(1, 7);

    temp = rolls[0] + rolls[1] + rolls[2] + rolls[3] -
      MIN(rolls[0], MIN(rolls[1], MIN(rolls[2], rolls[3])));

-  for (k = 0; k < 6; k++)
+  for (k = 0; k < 7; k++)
      if (table[k] < temp) {
        temp ^= table[k];


*** Scroll down a little, and find the rea_abils sets. *** ch->real_abils.wis = table[0]; ch->real_abils.intel = table[1]; ch->real_abils.str = table[2]; ch->real_abils.dex = table[3]; ch->real_abils.con = table[4]; ch->real_abils.cha = table[5]; + ch->real_abils.lck = table[6]; *** In act.wizard.c, find the first instance of GET_CON, or the part that looks similar to the following. *** send_to_char(ch, "Str: [%s%d/%d%s] Int: [%s%d%s] Wis: [%s%d%s] " - "Dex: [%s%d%s] Con: [%s%d%s] Cha: [%s%d%s]\r\n", + "Dex: [%s%d%s] Con: [%s%d%s] Cha: [%s%d%s] Lck: [%s%d%s]\r\n", CCCYN(ch, C_NRM), GET_STR(k), GET_ADD(k), CCNRM(ch, C_NRM), CCCYN(ch, C_NRM), GET_INT(k), CCNRM(ch, C_NRM), CCCYN(ch, C_NRM), GET_WIS(k), CCNRM(ch, C_NRM), CCCYN(ch, C_NRM), GET_DEX(k), CCNRM(ch, C_NRM), CCCYN(ch, C_NRM), GET_CON(k), CCNRM(ch, C_NRM), - CCCYN(ch, C_NRM), GET_CHA(k), CCNRM(ch, C_NRM)); + CCCYN(ch, C_NRM), GET_CHA(k), CCNRM(ch, C_NRM), + CCCYN(ch, C_NRM), GET_LCK(k), CCNRM(ch, C_NRM));

*** Further down, find the section that looks similar and change, after real_abils.cha. *** if (!IS_NPC(vict) && GET_LEVEL(ch) >= LVL_GRGOD) { if (GET_LEVEL(vict) >= LVL_IMMORT) for (i = 1; i <= MAX_SKILLS; i++) SET_SKILL(vict, i, 100); if (GET_LEVEL(vict) >= LVL_GRGOD) { vict->real_abils.str_add = 100; vict->real_abils.intel = 25; vict->real_abils.wis = 25; vict->real_abils.dex = 25; vict->real_abils.str = 18; vict->real_abils.con = 25; vict->real_abils.cha = 25; + vict->real_abils.lck = 25;

*** In do_wizutil, add it into SCMD_REROLL. *** case SCMD_REROLL: send_to_char(ch, "Rerolled...\r\n"); roll_real_abils(vict); log("(GC) %s has rerolled %s.", GET_NAME(ch), GET_NAME(vict)); send_to_char(ch, "New stats: Str %d/%d, Int %d, Wis %d, Dex %d, Con %d, Cha %d, Lck %d\r\n", GET_STR(vict), GET_ADD(vict), GET_INT(vict), GET_WIS(vict), - GET_DEX(vict), GET_CON(vict), GET_CHA(vict)); + GET_DEX(vict), GET_CON(vict), GET_CHA(vict), GET_LCK(vict)); break;

*** In set_fields, add it to the large list at the bottom. Don't forget the comma at the end. *** { "weight", LVL_BUILDER, BOTH, NUMBER }, { "wis", LVL_BUILDER, BOTH, NUMBER }, /* 55 */ { "questpoints", LVL_GOD, PC, NUMBER }, { "questhistory", LVL_GOD, PC, NUMBER }, + { "lck", LVL_BUILDER, BOTH, NUMBER }, { "\n", 0, BOTH, MISC } };

*** Shortly after that, add it to the bottom of the values list. It should be the next number available. *** case 56: /* questpoints */ GET_QUESTPOINTS(vict) = RANGE(0, 100000000); break; case 57: /* questhistory */ qvnum = atoi(val_arg); if (real_quest(qvnum) == NOTHING) { send_to_char(ch, "That quest doesn't exist.\r\n"); return FALSE; } else { if (is_complete(vict, qvnum)) { remove_completed_quest(vict, qvnum); send_to_char(ch, "Quest %d removed from history for player %s.\r\n", qvnum, GET_NAME(vict)); } else { add_completed_quest(vict, qvnum); send_to_char(ch, "Quest %d added to history for player %s.\r\n", qvnum, GET_NAME(vict)); } break; } + case 58: /* lck */ + if (IS_NPC(vict) || GET_LEVEL(vict) >= LVL_GRGOD) + RANGE(3, 25); + else + RANGE(3, 18); + vict->real_abils.lck = value; + affect_total(vict); + break; default: send_to_char(ch, "Can't set that!\r\n"); return (0); }

*** Finally, in zaffs[] where they have the applies, add it in after CHA *** {APPLY_WIS, -5, 3, "wisdom"}, {APPLY_CON, -5, 3, "constitution"}, {APPLY_CHA, -5, 3, "charisma"}, + {APPLY_LCK, -5, 3, "luck"}, {APPLY_CLASS, 0, 0, "class"}, {APPLY_LEVEL, 0, 0, "level"}, {APPLY_AGE, -10, 10, "age"},

*** In utils.h find the #defines and add it to the bottom, after CHA. *** #define GET_STR(ch) ((ch)->aff_abils.str) #define GET_ADD(ch) ((ch)->aff_abils.str_add) #define GET_DEX(ch) ((ch)->aff_abils.dex) #define GET_INT(ch) ((ch)->aff_abils.intel) #define GET_WIS(ch) ((ch)->aff_abils.wis) #define GET_CON(ch) ((ch)->aff_abils.con) #define GET_CHA(ch) ((ch)->aff_abils.cha) + #define GET_LCK(ch) ((ch)->aff_abils.lck)

*** In handler.c make sure it's in the applies at the bottom, after CHA. *** case APPLY_CON: GET_CON(ch) += mod; break; case APPLY_CHA: GET_CHA(ch) += mod; break; + case APPLY_LCK: + GET_LCK(ch) += mod; + break;

*** Make sure it has the MIN and MAX as well, a little further down, after CHA. *** GET_DEX(ch) = MAX(0, MIN(GET_DEX(ch), i)); GET_INT(ch) = MAX(0, MIN(GET_INT(ch), i)); GET_WIS(ch) = MAX(0, MIN(GET_WIS(ch), i)); GET_CON(ch) = MAX(0, MIN(GET_CON(ch), i)); GET_CHA(ch) = MAX(0, MIN(GET_CHA(ch), i)); + GET_LCK(ch) = MAX(0, MIN(GET_LCK(ch), i)); GET_STR(ch) = MAX(0, GET_STR(ch));

*** In players.c in character initializations, add it after CHA. *** ch->real_abils.dex = PFDEF_DEX; ch->real_abils.intel = PFDEF_INT; ch->real_abils.wis = PFDEF_WIS; ch->real_abils.con = PFDEF_CON; ch->real_abils.cha = PFDEF_CHA; + ch->real_abils.lck = PFDEF_LCK; GET_HIT(ch) = PFDEF_HIT; GET_MAX_HIT(ch) = PFDEF_MAXHIT;

*** A little further down there are all the cases. Add it where it makes sense alphabetically. *** case 'L': if (!strcmp(tag, "Last")) ch->player.time.logon = atol(line); else if (!strcmp(tag, "Lern")) GET_PRACTICES(ch) = atoi(line); else if (!strcmp(tag, "Levl")) GET_LEVEL(ch) = atoi(line); else if (!strcmp(tag, "Lmot")) GET_LAST_MOTD(ch) = atoi(line); else if (!strcmp(tag, "Lnew")) GET_LAST_NEWS(ch) = atoi(line); + else if (!strcmp(tag, "Lck ")) ch->real_abils.lck = atoi(line); break;

*** Even further down, look for GET_CHA with the fprint and add it right after. *** if (GET_CON(ch) != PFDEF_CON) fprintf(fl, "Con : %d\n", GET_CON(ch)); if (GET_CHA(ch) != PFDEF_CHA) fprintf(fl, "Cha : %d\n", GET_CHA(ch)); + if (GET_LCK(ch) != PFDEF_LCK) fprintf(fl, "Lck : %d\n", GET_LCK(ch)); if (GET_AC(ch) != PFDEF_AC) fprintf(fl, "Ac : %d\n", GET_AC(ch)); if (GET_GOLD(ch) != PFDEF_GOLD) fprintf(fl, "Gold: %d\n", GET_GOLD(ch));

*** In db.c in parse_simple_mob, add it after real_abils.cha. *** mob_proto[i].real_abils.con = 11; mob_proto[i].real_abils.cha = 11; + mob_proto[i].real_abils.lck = 11; if (!get_line(mob_f, line)) { log("SYSERR: Format error in mob #%d, file ended after S flag!", nr); exit(1);

*** Further down, where it defines the ranges, add it after CHA. *** CASE("Con") { RANGE(3, 25); mob_proto[i].real_abils.con = num_arg; } CASE("Cha") { RANGE(3, 25); mob_proto[i].real_abils.cha = num_arg; } + CASE("Lck") { + RANGE(3, 25); + mob_proto[i].real_abils.lck = num_arg; + } CASE("SavingPara") { RANGE(0, 100); mob_proto[i].char_specials.saved.apply_saving_throw[SAVING_PARA] = num_arg; }

Even further down, add it again after real_abils.cha. *** for (i = 0; i < 5; i++) GET_SAVE(ch, i) = 0; ch->real_abils.intel = 25; ch->real_abils.wis = 25; ch->real_abils.dex = 25; ch->real_abils.str = 25; ch->real_abils.str_add = 100; ch->real_abils.con = 25; ch->real_abils.cha = 25; + ch->real_abils.lck = 25; for (i = 0; i < 3; i++) GET_COND(ch, i) = (GET_LEVEL(ch) == LVL_IMPL ? -1 : 24);

*** In medit.c, in init_mob, add after real_abils.cha = 11; *** GET_WEIGHT(mob) = 200; GET_HEIGHT(mob) = 198; mob->real_abils.str = mob->real_abils.intel = mob->real_abils.wis = 11; mob->real_abils.dex = mob->real_abils.con = mob->real_abils.cha = 11; + mob->real_abils.lck = 11; mob->aff_abils = mob->real_abils; GET_SAVE(mob, SAVING_PARA) = 0; GET_SAVE(mob, SAVING_ROD) = 0;

*** In CONFIG_MEDIT_ADVANCED, make the following changes. *** if (CONFIG_MEDIT_ADVANCED) { /* Bottom section - non-standard stats, togglable in cedit */ write_to_output(d, "(%sF%s) Str: %s[%s%2d/%3d%s]%s Saving Throws\r\n" "(%sG%s) Int: %s[%s%3d%s]%s (%sL%s) Paralysis %s[%s%3d%s]%s\r\n" "(%sH%s) Wis: %s[%s%3d%s]%s (%sM%s) Rods/Staves %s[%s%3d%s]%s\r\n" "(%sI%s) Dex: %s[%s%3d%s]%s (%sN%s) Petrification %s[%s%3d%s]%s\r\n" "(%sJ%s) Con: %s[%s%3d%s]%s (%sO%s) Breath %s[%s%3d%s]%s\r\n" - "(%sK%s) Cha: %s[%s%3d%s]%s (%sP%s) Spells %s[%s%3d%s]%s\r\n\r\n", + "(%sK%s) Cha: %s[%s%3d%s]%s (%sP%s) Spells %s[%s%3d%s]%s\r\n" + "(%sT%s) Lck: %s[%s%3d%s]%s\r\n", cyn, nrm, cyn, yel, GET_STR(mob), GET_ADD(mob), cyn, nrm, cyn, nrm, cyn, yel, GET_INT(mob), cyn, nrm, cyn, nrm, cyn, yel, GET_SAVE(mob, SAVING_PARA), cyn, nrm, cyn, nrm, cyn, yel, GET_WIS(mob), cyn, nrm, cyn, nrm, cyn, yel, GET_SAVE(mob, SAVING_ROD), cyn, nrm, cyn, nrm, cyn, yel, GET_DEX(mob), cyn, nrm, cyn, nrm, cyn, yel, GET_SAVE(mob, SAVING_PETRI), cyn, nrm, cyn, nrm, cyn, yel, GET_CON(mob), cyn, nrm, cyn, nrm, cyn, yel, GET_SAVE(mob, SAVING_BREATH), cyn, nrm, - cyn, nrm, cyn, yel, GET_CHA(mob), cyn, nrm, cyn, nrm, cyn, yel, GET_SAVE(mob, SAVING_SPELL), cyn, nrm + cyn, nrm, cyn, yel, GET_CHA(mob), cyn, nrm, cyn, nrm, cyn, yel, GET_SAVE(mob, SAVING_SPELL), cyn, nrm, + cyn, nrm, cyn, yel, GET_LCK(mob), cyn, nrm ); }

*** Further down, in the cases, add in a new one for T. *** case 'k': case 'K': if (!CONFIG_MEDIT_ADVANCED) { write_to_output(d, "Invalid Choice!\r\nEnter Choice : "); return; } OLC_MODE(d) = MEDIT_CHA; i++; break; + case 't': + case 'T': + if (!CONFIG_MEDIT_ADVANCED) { + write_to_output(d, "Invalid Choice!\r\nEnter Choice : "); + return; + } + OLC_MODE(d) = MEDIT_LCK; + i++; + break; case 'l': case 'L': if (!CONFIG_MEDIT_ADVANCED) { write_to_output(d, "Invalid Choice!\r\nEnter Choice : "); return; } OLC_MODE(d) = MEDIT_PARA; i++; break;

*** A little down from that, find MEDIT_CHA and add below. *** case MEDIT_CHA: GET_CHA(OLC_MOB(d)) = LIMIT(i, 11, 25); OLC_VAL(d) = TRUE; medit_disp_stats_menu(d); return; + case MEDIT_LCK: + GET_LCK(OLC_MOB(d)) = LIMIT(i, 11, 25); + OLC_VAL(d) = TRUE; + medit_disp_stats_menu(d); + return;

*** Near the very bottom, add it as well. *** GET_DEX(OLC_MOB(d)) = LIMIT((mob_lev*2)/3, 11, 18); GET_CON(OLC_MOB(d)) = LIMIT((mob_lev*2)/3, 11, 18); GET_CHA(OLC_MOB(d)) = LIMIT((mob_lev*2)/3, 11, 18); + GET_LCK(OLC_MOB(d)) = LIMIT((mob_lev*2)/3, 11, 18);

*** In genmob.c in write_mobile_espec, add it after CHA. *** if (GET_WIS(mob) != 11) fprintf(fd, "Wis: %d\n", GET_WIS(mob)); if (GET_CON(mob) != 11) fprintf(fd, "Con: %d\n", GET_CON(mob)); if (GET_CHA(mob) != 11) fprintf(fd, "Cha: %d\n", GET_CHA(mob)); + if (GET_LCK(mob) != 11) + fprintf(fd, "Lck: %d\n", GET_LCK(mob)); if (GET_CLASS(mob) != 1) fprintf(fd, "Class: %d\n", GET_CLASS(mob));

*** In oasis.h add it into the #defines. I added it right below MEDIT_CHA. *** *** Note: Make sure your numbers are in proper order! *** #define MEDIT_DEX 32 #define MEDIT_CON 33 #define MEDIT_CHA 34 + #define MEDIT_LCK 35

*** In pfdefaults.h, make sure it's defined after PFDEF_CHA. *** #define PFDEF_WIS 0 #define PFDEF_CON 0 + #define PFDEF_CHA 0 #define PFDEF_LCK 0 #define PFDEF_HIT 0 #define PFDEF_MAXHIT 0

*** In structs.h, add it after CHA and reorder the numbers. *** *** Note: You can change this if you don't want to reorder player files. *** #define APPLY_WIS 4 /**< Apply to wisdom */ #define APPLY_CON 5 /**< Apply to constitution */ #define APPLY_CHA 6 /**< Apply to charisma */ + #define APPLY_LCK 7 /**< Apply to luck */ #define APPLY_CLASS 8 /**< Reserved */ #define APPLY_LEVEL 9 /*

*** In char_ability_data, after cha. *** struct char_ability_data { sbyte str; /**< Strength. */ sbyte str_add; /**< Strength multiplier if str = 18. Usually from 0 to 100 */ sbyte intel; /**< Intelligence */ sbyte wis; /**< Wisdom */ sbyte dex; /**< Dexterity */ sbyte con; /**< Constitution */ sbyte cha; /**< Charisma */ + sbyte lck; /**< Luck */ };

*** In constants.c in apply_types, add it after CHA. *** "INT", "WIS", "CON", "CHA", + "LCK", "CLASS", "LEVEL", "AGE",

*** In utils/plrtoascii.c in char_ability_data_plrtoascii, add it after cha. *** sbyte wis; sbyte dex; sbyte con; sbyte cha; + sbyte lck; };

*** A little further down, add it to the char ability data, or cad. *** if (cad->con != PFDEF_CON) fprintf(outfile, "Con : %d\n", cad->con); if (cad->cha != PFDEF_CHA) fprintf(outfile, "Cha : %d\n", cad->cha); + if (cad->lck != PFDEF_LCK) + fprintf(outfile, "Lck : %d\n", cad->lck); /* char_point_data */ cpd = &(player.points);

Conclusion

Hopefully this goes smoothly for anybody looking to use this guide. Please don't hesitate to post comments or questions. I'll try my best to answer them. :)

Saving variables on mobs/rooms/objs

We recently got a question on the forum (http://www.tbamud.com/forum/thread/743) , asking if it could be possible to save variables over reboots.


Well, now you can! I've made a shoddy first hack at something to that end.


I'm not entirely sure the AVL implementation license (MIT) is compatible with the circle license, so I think it needs some rewriting before it can be incoorporated in a release. But hey, as long as it's downloaded separately, there's no problems.


After installing the patch and the tree.h file, you have acces to two new commands in your dg scripts:



persist varname


Stores the current value of the variable with the given name to disk. It is stored based on type (room/mob/object) and vnum - so several mobs sharing a script or trigger will currently have separate values stored.

%self.persisted(varname)%


is substitued with the value from persistent storage


Be advised that currently the code has not been thoroughly tested, does not contain a lot of comments or log statements.


DG Script Wiki

After a few weeks of hard work, I have transposed many of tbaMUD's help files and trigger examples into wiki format for your web-viewing pleasure!


Although a wiki is always a 'work-in-progress', I think you'll find it quite complete for your referencing needs. Here you go:


http://awesomeness.allowed.org/wiki/


Thanks to everyone who wrote the in-game documentation, and everybody on-line (like Rumble and Fizban) who answered my many questions.


-Ax


Syndicate content