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
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.
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.
Download tbaMUD 3.6 or the patch at http://tbamud.com
More bug fixes, new prefedit and all the auto options including the new autodoor. Thanks again to Jamdog for the majority of the work this release! For the full tbaMUD changelog check out: http://www.tbamud.com/files/changelog.txt
I am feeling a bit old. My first website host is shutting down. Geocities.com (bought out by Yahoo last century! yeah 1999) has been hosting TBA's builder application for almost 10 years. Alas, we must move on. Besides, geocities has always been the Fiat of the web hosting services anyways. But it was one of the first major hurdles to user generated content that setup the future of Web 2.0. I digress. So TBA needs a new builder application. After a bit of thought I decided to try Survey Monkey. They seem to be the standard these days and I liked their interface. Feel free to test it out at the redirected URL:
http://tbamud.com/builderapplication.
Thanks to sedition for contributing this common addition:
http://www.tbamud.com/Oasis_DG_pages/downloads/patches/contrib/3.59-ordi...