is the continued development of the codebase formerly known as CircleMUD.
Download it here:tba MUD Download Patches, Snippets and Source
tbaMUD is an acronym for The Builder Academy Multi User Dungeon - And we use it to mean both the Academy for training, and the codebase that is made freely available to the community.
TbaMUD's vision is to provide the MUDding community a stable and functional codebase that includes an in-depth World and help files that makes it ready to be molded into a custom MUD by its coders and builders. We also provide multiple resources to allow for feedback, contribution, and the sharing of ideas within the MUDding community to ensure constant development and improvements.
TbaMUD is highly developed from the programming side, but highly UNdeveloped on the game-playing side. So, if you're looking for a huge MUD with billions of spells, skills, classes, and races, tbaMUD will disappoint you. TbaMUD still has only the 4 original Diku classes, the original spells, and the original skills. On the other hand, if you're looking for a highly stable, developed, organized, and well documented "blank slate" MUD on which you can put your OWN ideas for spells, skills, and classes, then tbaMUD is what what you are looking for.
TBA is a low stress, no deadline, training environment where anyone with motivation can work at their own pace to learn as much or as little as they desire about building on the tbaMUD codebase (formerly known as CircleMUD). TBA is a MUDding resource dedicated to improving the MUDding community.TBA has been operating since 2000 and has trained 1000's of builders.
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:
Be advised that currently the code has not been thoroughly tested, does not contain a lot of comments or log statements.
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://dgscript.sytes.net/wiki/
Thanks to everyone who wrote the in-game documentation, and everybody on-line (like Rumble and Fizban) who answered my many questions.
-Ax
Who wouldn't want a MUD on a thumdrive they can run from any computer!?!
Dare asked me about running tbaMUD on a thumb drive. I had never tried this so I did a bit of research and did not find a simple linux solution. There are a few websites that offer ubuntu desktop like penlinux.com but the ubuntu server install failed. I thought about VMWare, but I didn't want a complex solution so I fell back on my old faithful Cygwin.
The instructions are simple. Just go to www.cygwin.com and run their setup.exe. Install from Internet, choose your thumbdrive as the installation directory, leave default package directiory, leave default direct connection, and choose a mirror. When you get to the "select packages" section you do have to expand the devel group and add: "gcc-core C compiler" the "make: the GNU version of the make utility" and "subversion: a version control system" To add a package just click where it says "skip" and it will change to the current version number available.
You can rerun setup.exe at any time to add other packages like patchutils, etc.
Now open cygwin.bat from the thumbdrive, change directory to you thumbdrive (cd f: for me) and checkout the latest tbamud:
svn checkout http://tbamud.com/svn/circlemud/circlemud tbamud
(alternatively you can just download and uncompress tbamud-#.tgz from tbamud.com)
Checkout takes a while once complete:
cd tbamud
./configure
(wait for it, once complete)
cd src
make (q then enter to dismiss the license, wait for it again, once complete)
cd ..
bin/circle
Then point your MUD client of choice to localhost port 4000.
That is it. I was a bit disappointed with the speed. I haven't compiled a MUD that slowly in a decade, but it may just be my ancient thumbdrive. It is a sacrifice to be expected for portability.
Total time required: 2 hours (can you do better?)
It used 422MB on my 2GB thumb drive.
Please post in the comments below of your success, failures, and install time from thumb drive insert to tbaMUD login.
Download tbaMUD 3.61 or the patch at http://tbamud.com
More bug fixes, new areas command for players and an impoved idea, bug, typo command.
**WARNING** The new idea, bug, typo code will overwrite your existing idea, bug, and typo files. So make a backup or rename them if you add this code to your existing MUD.
The entire tbaMUD changelog: http://tbamud.com/files/changelog.txt
Changes this release:
tbaMUD 3.61
[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.
[Dec 28 2009] - Rumble
Fixed bug where an immortal could cast 'invis' and then be unable to break it with vis.
[Dec 25 2009] - Rumble
Added new mob flag NOKILL.
[Dec 23 2009] - Rumble
Added zone flags and min/max level to show zone # and stat zone #.
Changed show zone # reset mode from a # to the actual string description.
Changed Message Type on stat object from a number to the actual message type. i.e. pierce.
[Dec 22 2009] - Rumble
Made copyover save loadroom so players stay in the same room during copyover.
Added scan command.
[Dec 21 2009] - Rumble
Added identify command to shops.
Moved identify to an active spell. Cleric 11, Mage 20.
[Dec 17 2009] - Rumble
Standardized /n/r to /r/n in ibt.c
Removed ability to set a modifier to obj apply NONE.
[Dec 16 2009] - Rumble
Added some missing code for last_ibt in ibt.c.
Fixed typos in ibt.c (thanks Elervan)
[Dec 13 2009] - Rumble
Changed set password to level GRGOD.
[Dec 08 2009] - Rumble
Added "current zone:" to immortal score.
[Dec 07 2009] - Rumble
Renumbered UNUSED triggers to UNUSED# to make it easier to unset them when changing trig types. (thanks Parna)
Fixed double free in IBT that caused crash on /a or saving without any description.
[Dec 06 2009] - Rumble
Fixed medit autoroll so it would recognize a change and save.
[Dec 05 2009] - Rumble
Changed zedit clear level restrictions to dump you back at main zedit menu instead of in the level menu.
Changed zone level restriction to level recommendation so players would not be blocked from zones.
Changed Zcheck to level 31.
[Nov 19 2009] - Rumble
Fixed list obj max/min from room to obj. (thanks Elervan)
[Nov 13 2009] - Rumble
Removed con_app 2nd apply type shock that was for "resurrection?" This was not used in 3.1.
[Nov 12 2009] - Rumble
Fixed overflow in prefedit.c. (thanks Xiu)
[Nov 01 2009] - Rumble
Renamed autorun log entries from autoscript killed to terminated so it wouldn't go into the log/rip file.
[Nov 01 2009] - Fizban
Changed MOB_OR_IMPL macro to allow player-attachable scripts to work properly and edited the output of 'show zones'.
[Oct 27 2009] - Rumble
Added zone 555/556 qst files.
PLR_BUG/IDEA/TYPO flag cleanup.
[Oct 24 2009] - Jamdog
Added new IBT system for Ideas, Bugs and Typos (thanks Frenze)
**WARNING** This will create new files so backup your old ones first.
[Oct 23 2009] - Rumble
Fixed bug in renumbering zone table after deleting mobiles. (thanks Drefs)
Fixed bug in renumbering zone table after deleting objects. (thanks Drefs)
Upgraded column_list to use player screenwidth to determine how many columns to create (use column arg of 0). (thanks Maclir)
[Oct 20 2009] - Rumble
Corrected several PRF_FLAGGED checks that mobs were using.
Removed l-desc from stat player and title from stat mob. (thanks Xiu)
[Oct 19 2009] - Rumble
Added a note to spec_assign.c assign_mobiles that guildguard, snake, thief, magic user, puff, fido, janitor, and cityguards are now implemented via
triggers.
Fixed a bug where empty drink containers and fountains would show half empty. (thanks Parna)
[Oct 17 2009] - Rumble
Added Ultima Zone 555 and 556. Originally by Casret, rebuilt by Parna.
Made MEDIT column menu's consistent with other OLC menu's.
added TEDIT access to bugs, typos, and ideas file.
[Oct 09 2009] - Rumble
Added connected_type "Preference Edit" (thanks Maoliosa)
[Oct 08 2009] - Rumble
Fixed do_purge to allow targetting of multiple objects of the same name. i.e. 2.knife. (thanks Drefs)
[Oct 07 2009] - Rumble
Added CAN_SEE_IN_DARK and AFF_BLIND checks in do_automap. (thanks Frenze)
[Oct 06 2009] - Rumble
Fixed do_cast to allow targetting of multiple objects of the same name. i.e. 2.knife. (thanks Drefs)
[Oct 04 2009] - Rumble
Fixed a bug on filling unlimited containers where it doesn't update weight. (thanks Drefs)
[Oct 02 2009] - Rumble
Fixed buildwalk so it does not require the first room to exist. (thanks Zizazat)
Below is an example of how you can add/modify a mob flag. After initially creating a new NOKILL flag I changed my mind and instead renamed NOBASH to NOKILL and added checks to prevent attacking and spell casting. The only thing I left out was protection from triggers.
Index: fight.c
===================================================================
--- fight.c (revision 197)
+++ fight.c (working copy)
@@ -682,8 +682,9 @@
return (0);
}
- /* shopkeeper protection */
- if (!ok_damage_shopkeeper(ch, victim))
+ /* shopkeeper and MOB_NOKILL protection */
+ if (!ok_damage_shopkeeper(ch, victim) || MOB_FLAGGED(victim, MOB_NOKILL)) {
+ send_to_char(ch, "This mob is protected.\r\n");
return (0);
+ }
/* You can't damage an immortal! */
Index: structs.h
===================================================================
--- structs.h (revision 197)
+++ structs.h (working copy)
@@ -181,44 +181,44 @@
#define MOB_NOSUMMON 14 /**< Mob can't be summoned */
#define MOB_NOSLEEP 15 /**< Mob can't be slept */
-#define MOB_NOBASH 16 /**< Mob can't be bashed (e.g. trees) */
+#define MOB_NOKILL 16 /**< Mob can't be bashed (e.g. trees) */
#define MOB_NOBLIND 17 /**< Mob can't be blinded */
#define MOB_NOTDEADYET 18 /**< (R) Mob being extracted */
/** Total number of Mob Flags; it should be 1 less than MOB_NOT_DEADYET */
Index: constants.c
===================================================================
--- constants.c (revision 197)
+++ constants.c (working copy)
@@ -187,7 +187,7 @@
"NO_CHARM",
"NO_SUMMN",
"NO_SLEEP",
- "NO_BASH",
+ "NO_KILL",
"NO_BLIND",
"DEAD", /* You should never see this. */
"\n"
Index: spell_parser.c
===================================================================
--- spell_parser.c (revision 200)
+++ spell_parser.c (working copy)
@@ -212,6 +212,10 @@
act("White light from no particular source suddenly fills the room, then va
nishes.", FALSE, caster, 0, 0, TO_ROOM);
return (0);
}
+ if (cvict && MOB_FLAGGED(cvict, MOB_NOKILL)) {
+ send_to_char(caster, "This mob is protected.\r\n");
+ return (0);
+ }
/* determine the type of saving throw */
switch (casttype) {
case CAST_STAFF:
Index: act.offensive.c
===================================================================
--- act.offensive.c (revision 197)
+++ act.offensive.c (working copy)
@@ -294,12 +294,13 @@
send_to_char(ch, "Aren't we funny today...\r\n");
return;
}
+ if (MOB_FLAGGED(vict, MOB_NOKILL)) {
+ send_to_char(ch, "This mob is protected.\r\n");
+ return;
+ }
percent = rand_number(1, 101); /* 101% is a complete failure */
prob = GET_SKILL(ch, SKILL_BASH);
- if (MOB_FLAGGED(vict, MOB_NOBASH))
- percent = 101;
-
if (percent > prob) {
damage(ch, vict, 0, SKILL_BASH);
GET_POS(ch) = POS_SITTING; As discussed on the forums here is how to add identify:
Index: interpreter.c
===================================================================
--- interpreter.c (revision 197)
+++ interpreter.c (working copy)
@@ -171,6 +171,7 @@
{ "holylight", "holy" , POS_DEAD , do_gen_tog , LVL_IMMORT, SCMD_HOLYLIGHT },
{ "house" , "house" , POS_RESTING , do_house , 0, 0 },
+ { "identify" , "id" , POS_STANDING, do_not_here , 1, 0 },
{ "inventory", "i" , POS_DEAD , do_inventory, 0, 0 },
{ "idea" , "id" , POS_DEAD , do_ibt , 0, SCMD_IDEA },
{ "imotd" , "imo" , POS_DEAD , do_gen_ps , LVL_IMMORT, SCMD_IMOTD },
Index: spell_parser.c
===================================================================
--- spell_parser.c (revision 197)
+++ spell_parser.c (working copy)
@@ -916,7 +916,11 @@
TAR_CHAR_ROOM, FALSE, MAG_MANUAL,
NULL);
+ spello(SPELL_IDENTIFY, "identify", 50, 25, 5, POS_STANDING,
+ TAR_CHAR_ROOM | TAR_OBJ_INV | TAR_OBJ_ROOM, FALSE, MAG_MANUAL,
+ NULL);
+
/* NON-castable spells should appear below here. */
spello(SPELL_IDENTIFY, "identify", 0, 0, 0, 0,
TAR_CHAR_ROOM | TAR_OBJ_INV | TAR_OBJ_ROOM, FALSE, MAG_MANUAL,
Index: class.c
===================================================================
--- class.c (revision 197)
+++ class.c (working copy)
@@ -1586,6 +1586,7 @@
spell_level(SPELL_POISON, CLASS_MAGIC_USER, 14);
spell_level(SPELL_FIREBALL, CLASS_MAGIC_USER, 15);
spell_level(SPELL_CHARM, CLASS_MAGIC_USER, 16);
+ spell_level(SPELL_IDENTIFY, CLASS_MAGIC_USER, 20);
spell_level(SPELL_ENCHANT_WEAPON, CLASS_MAGIC_USER, 26);
spell_level(SPELL_CLONE, CLASS_MAGIC_USER, 30);
@@ -1607,6 +1608,7 @@
spell_level(SPELL_CURE_CRITIC, CLASS_CLERIC, 9);
spell_level(SPELL_SUMMON, CLASS_CLERIC, 10);
spell_level(SPELL_REMOVE_POISON, CLASS_CLERIC, 10);
+ spell_level(SPELL_IDENTIFY, CLASS_CLERIC, 11);
spell_level(SPELL_WORD_OF_RECALL, CLASS_CLERIC, 12);
spell_level(SPELL_EARTHQUAKE, CLASS_CLERIC, 12);
spell_level(SPELL_DISPEL_EVIL, CLASS_CLERIC, 14);
Index: shop.c
===================================================================
--- shop.c (revision 197)
+++ shop.c (working copy)
@@ -25,6 +25,7 @@
#include "constants.h"
#include "act.h"
#include "modify.h"
+#include "spells.h" /* for skill_name() */
/* Global variables definitions used externally */
/* Constant list for printing out who we sell to */
@@ -57,6 +58,7 @@
static int read_type_list(FILE *shop_f, struct shop_buy_data *list, int new_format, int max);
static int read_list(FILE *shop_f, struct shop_buy_data *list, int new_format, int max, int type);
static void shopping_list(char *arg, struct char_data *ch, struct char_data *keeper, int shop_nr);
+static bool shopping_identify(char *arg, struct char_data *ch, struct char_data *keeper, int shop_nr);
static void shopping_value(char *arg, struct char_data *ch, struct char_data *keeper, int shop_nr);
static void shopping_sell(char *arg, struct char_data *ch, struct char_data *keeper, int shop_nr);
static struct obj_data *get_selling_obj(struct char_data *ch, char *name, struct char_data *keeper, int shop_nr, int msg);
@@ -984,6 +986,8 @@
} else if (CMD_IS("list")) {
shopping_list(argument, ch, keeper, shop_nr);
return (TRUE);
+ } else if (CMD_IS("identify")) {
+ return (shopping_identify(argument, ch, keeper, shop_nr));
}
return (FALSE);
}
@@ -1543,3 +1547,114 @@
shop_index = NULL;
top_shop = -1;
}
+
+bool shopping_identify(char *arg, struct char_data *ch, struct char_data *keeper, int shop_nr)
+{
+ char buf[MAX_STRING_LENGTH];
+ struct obj_data *obj;
+ int i, found;
+
+ if (!is_ok(keeper, ch, shop_nr))
+ return FALSE;
+
+ if (SHOP_SORT(shop_nr) < IS_CARRYING_N(keeper))
+ sort_keeper_objs(keeper, shop_nr);
+
+ if (!*arg) {
+ snprintf(buf, sizeof(buf), "%s What do you want to identify??", GET_NAME(ch));
+ do_tell(keeper, buf, cmd_tell, 0);
+ return TRUE;
+ }
+ if (!(obj = get_purchase_obj(ch, arg, keeper, shop_nr, TRUE)))
+ return FALSE;
+
+ send_to_char(ch, "Name: %s\r\n", (obj->short_description) ? obj->short_description : "");
+ sprinttype(GET_OBJ_TYPE(obj), item_types, buf, sizeof(buf));
+ send_to_char(ch, "Type: %s\r\n", buf);
+ send_to_char(ch, "Weight: %d, Cost to Buy: @Y%d@n\r\n", GET_OBJ_WEIGHT(obj), sell_price(obj, shop_nr, keeper, ch));
+
+ sprintbitarray(GET_OBJ_WEAR(obj), wear_bits, TW_ARRAY_MAX, buf);
+ send_to_char(ch, "Can be worn on: %s\r\n", buf);
+
+ switch (GET_OBJ_TYPE(obj)) {
+ case ITEM_LIGHT:
+ if (GET_OBJ_VAL(obj, 2) == -1)
+ send_to_char(ch, "Hours Remaining: (Infinite)\r\n");
+ else if (GET_OBJ_VAL(obj, 2) == 0)
+ send_to_char(ch, "Hours Remaining: None!\r\n");
+ else
+ send_to_char(ch, "Hours Remaining: %d\r\n", GET_OBJ_VAL(obj, 2));
+ break;
+ case ITEM_SCROLL:
+ case ITEM_POTION:
+ send_to_char(ch, "Spells: %s, %s, %s\r\n",
+ skill_name(GET_OBJ_VAL(obj, 1)),
+ skill_name(GET_OBJ_VAL(obj, 2)),
+ skill_name(GET_OBJ_VAL(obj, 3)));
+ break;
+ case ITEM_WAND:
+ case ITEM_STAFF:
+ send_to_char(ch, "Spell: %s\r\n", skill_name(GET_OBJ_VAL(obj, 3)));
+ send_to_char(ch, "Charges: %d/%d\r\n", GET_OBJ_VAL(obj, 2), GET_OBJ_VAL(obj, 1));
+ break;
+ case ITEM_WEAPON:
+ send_to_char(ch, "Damage Dice is '%dD%d' for an average per-round damage of %.1f.\r\n",
+ GET_OBJ_VAL(obj, 1), GET_OBJ_VAL(obj, 2),
+ ((GET_OBJ_VAL(obj, 2) + 1) / 2.0) * GET_OBJ_VAL(obj, 1));
+ break;
+ case ITEM_ARMOR:
+ if(GET_OBJ_VAL(obj,1) == 0)
+ {
+ send_to_char(ch, "AC-apply: [%d]\r\n", GET_OBJ_VAL(obj, 0));
+ }
+ else
+ {
+ send_to_char(ch, "AC-apply: [%d] - This item has magical affects.\r\n", GET_OBJ_VAL(obj, 0));
+ }
+ break;
+ case ITEM_CONTAINER:
+ send_to_char(ch, "Capacity: %d/%d\r\n", GET_OBJ_WEIGHT(obj), GET_OBJ_VAL(obj, 0));
+ break;
+ case ITEM_DRINKCON:
+ case ITEM_FOUNTAIN:
+ send_to_char(ch, "Drinks: %d/%d\r\n", GET_OBJ_VAL(obj, 1), GET_OBJ_VAL(obj, 0));
+ break;
+ case ITEM_NOTE:
+ send_to_char(ch, "\r\n");
+ break;
+ case ITEM_KEY:
+ send_to_char(ch, "\r\n");
+ break;
+ case ITEM_FOOD:
+ send_to_char(ch, "\r\n");
+ break;
+ case ITEM_MONEY:
+ send_to_char(ch, "\r\n");
+ break;
+ case ITEM_WORN:
+ if(GET_OBJ_VAL(obj,1) > 0)
+ send_to_char(ch, "This item has magical affects.\r\n");
+ else
+ send_to_char(ch, "\r\n");
+ break;
+ default:
+ send_to_char(ch, "\r\n");
+ break;
+ }
+
+ found = 0;
+ send_to_char(ch, "Affections:");
+ for (i = 0; i < MAX_OBJ_AFFECT; i++)
+ if (obj->affected[i].modifier) {
+ sprinttype(obj->affected[i].location, apply_types, buf, sizeof(buf));
+ send_to_char(ch, "%s %+d to %s", found++ ? "," : "", obj->affected[i].modifier, buf);
+ }
+ if (!found)
+ send_to_char(ch, " None");
+
+ send_to_char(ch, "\r\nExtra Flags: ");
+ sprintbitarray(GET_OBJ_EXTRA(obj), extra_bits, EF_ARRAY_MAX, buf);
+ send_to_char(ch, "%s\r\n", buf);
+
+ return TRUE;
+}
Index: spells.h
===================================================================
--- spells.h (revision 197)
+++ spells.h (working copy)
@@ -89,6 +89,7 @@
#define SPELL_GROUP_RECALL 49 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_INFRAVISION 50 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_WATERWALK 51 /* Reserved Skill[] DO NOT CHANGE */
+#define SPELL_IDENTIFY 52 /* Reserved Skill[] DO NOT CHANGE */
/** Total Number of defined spells */
-#define NUM_SPELLS 51
+ #define NUM_SPELLS 52
@@ -113,7 +114,6 @@
* intended use is for spells and skills associated with objects (such as
* SPELL_IDENTIFY used with scrolls of identify) or non-players (such as NPC
* only spells). */
-#define SPELL_IDENTIFY 201
/* To make an affect induced by dg_affect look correct on 'stat' we need to
* define it with a 'spellname'. */ This question has come up a couple times lately so I decided to post it here to direct people as needed.
Players and mobs (NPC's) share many of the same data fields, but not all. So when a mob tries to access player data it gives a SYSERR like this:
SYSERR: Mob using >'((ch)-)player_specials-)saved.pref)' at act.wizard.c:127
The fix is actually a very easy one. All you have to do is make sure it only checks player data if it is a player. The way this is done in the code is with a non-player-character check. Now non-player-character (NPC) means it is a mob. So you need a double negative to make sure it is not a non-player-character. I know this is confusing, but just copy the example below.
- if (PRF_FLAGGED(ch, PRF_NOREPEAT)) // This line should be removed, hence the "-"
+ if (!IS_NPC(ch) && PRF_FLAGGED(ch, PRF_NOREPEAT)) // This line should be added without the "+"
The changed line now will not just check for a flag, instead it will check if it is a player (not an NPC) and it is flagged then continue.
I hope this helps and please report these (and any other bugs/SYSERR's) you find. I've corrected dozens of these and they keep cropping up, especially when you switch into a mob and try player commands.