04 May, 2014, jeli wrote in the 1st comment:
Votes: 0
Hey everyone,

Longtime lurker, first time poster, but I felt a need to poll the community because in my experience you guys/gals know what you're talking about. I'm looking to start building a new mud that is part personal indulgence part hope this goes somewhere. I've been mudding since about 1999 and I've played across a variety of codebases, from Diku and its derivatives to CoffeeMud and even logged into an LPMud once. Once. In my time I've also tried a dozen times to start up my own mud building on a copy of someones released project and tried to tweak it to make it my own, but it always felt like something was missing, or something just didn't work the way I envisioned.

I want to start creating a game that is going to be Role Play Intensive but still maintain a hack-n-slash element that keeps you busy when people aren't around to help things progress. Deep levels of crafting (Everquest II style or even similar to WoW) are going to be a primary focus and a driving force behind the economy. Theme wise, I've been in love with Dragonlance since the late 80's and I want to emulate that as specifically as possible. Now I'm not jumping into this blindly. I've run muds built on TBA/Circlemud/D20/Luminari and even extended them to suit my needs where I could. I've even recently (past two years) been running a CoffeeMud server on my own Linode just to get some experience doing things but it feels that all of the game systems are kind of forced? I don't know, it feels like it assumes too much. So I went looking at what else was out there.

I've looked at Dawn of Time, SMAUG Fuss, LOP (smaug derivative, cleaned up) & Dead Souls. Dawn makes sense to me as its built on ROM and has some powerful OLC editors. SMAUG is foreign to me but everytime I login, I just like the "feel" of the place. Same with LOP, it seems as if some great attention to detail went into these codebases. Then theres Dead Souls. The only LPMud I've ever logged into and through my limited research it seems like its the one that should be the best bet, but is it? Of these codebases, which should I choose? Which shouldn't I choose and why?

Thanks for reading this long post, but I'm having a terrible time making a decision.

Jeli
04 May, 2014, quixadhal wrote in the 2nd comment:
Votes: 0
I'll try to enumerate some of the strengths and weaknesses of various MUD drivers, and maybe that will help you decide what to use. :)

An LPMUD (Dead Souls is the mudlib, there are others) is strongest in how flexible it can be. If your game is going to focus on complex quests, or features that are generally "unusual" for a MUD, it's likely to be your best bet. In an LPMUD, everything is coded in LPC, and it can all be changed on the fly without needing to reboot or recompile the driver. The driver basically handles networking, file I/O, and running LPC code. The other advantage to this is that builders can code their own systems without needing admin-level access to the "source".

The downside is that you have to learn LPC, and most mudlibs that have a decent feature set are also pretty complex and have a steep learning curve. If you don't mind getting your hands dirty and experimenting though, LPMUD is the way to go. No other MUD driver makes it as easy to just try coding something to see if it works without fear of crashing or breaking things for players.

SmaugFUSS, LOP, and Dawn of Time are all Dikurivatives. As such, they treat everything as templated data with hard-coded routines that manipulate that data. They all have OLC, because all things in the game share common templates, and the only code you can write without needing to recompile and reboot are trigger-based programs in their own mini-language. They excel as hack-and-slash, and it's pretty trivial to add more emotes and whatnot, but changing any basic behavior will require source code access, and a recompile/reboot.

You didn't mention them, but there's also the TinyMUSH/MUCK/MOO family of drivers. These systems are very much oriented towards socializing and role-play, often not even including combat at all. They allow you to create objects on the fly and write code for them, but unlike an LPMUD, that is all done via online commands, and objects are persistent.

The diffrerence here is, on an LPMUD, I'd typically write a new NPC as a file on disk and then "update file.c" inside the MUD to load it. Then I can "clone file" to make instances of them. While LPMUD's have a simple line editor, nobody likes using it these days. :) In a TinyMUSH, you'd do something like "create #81" to make a new object, and then use a bunch more commands to associate properties and MUSH code (forth?) with it. There isn't any (easy) way to do this on disk, because the game saves state in a non-text fashion.

So, which kind of driver you pick really depends on your long term goals. ANY of them can be "role play intensive", but unless you really just mean lots of chatting and emotes, you'll wand to consider how "different" or "clever" you want your game to be. If you like building quests and having interesting systems (minigames, areas with different physical rules, classes or jobs that are radically different from each other - not just different descriptions to dice rolls), I would pick an LPMUD. If you want a solid combat system and good OLC, and can live with fudging quests and sticking inside the box for other things, I would use either SmaugFUSS or LOP. If you're looking for pure role play with most stuff happening in chat, then a TinyMU* is likely the best thing to use.
04 May, 2014, Idealiad wrote in the 3rd comment:
Votes: 0
While people might start coding a mush with their client's line editor, many mush coders these days will code in a local file and then quote that file to the mush via their client. So it's basically the same thing as LP coding.

An LP is definitely a better bet than a mush if you want the kind of systems we associate with muds (combat, NPCs, etc.) because there are no game libs like that for mushes (there are some ad hoc systems you can add but nothing like you'd find in an LP lib).
06 May, 2014, jeli wrote in the 4th comment:
Votes: 0
Appreciate all of the feedback. I think I'm going to move forward with SMAUG and on that note 4Liberty/6Dragons released their codebases recently so I went to check them out and 4 Liberty has everything I'm looking for, mostly. I am having a problem getting their crafting system to work though. When you create an item it has you repeat the crafting command 4 times to take you through different steps of the crafting process, but it always seems to get stuck on the 2nd step and never moving forward. Heres what the function looks like if anyone is interested in helping me figure this out:

void do_bake( CHAR_DATA *ch, char *argument )
{
OBJ_DATA *bakedfood;
char arg1[MIL];
char arg2[MIL];
char arg3[MIL];
char arg4[MIL];
char name_buf[MSL];
char short_buf[MSL];
char long_buf[MSL];
char extra_buf[MSL];
const char *adj;
bool itm = FALSE;
int i = 0,
x = 0;
struct skill_type *skill = NULL;

name_buf[0] = '\0';
short_buf[0] = '\0';
long_buf[0] = '\0';
extra_buf[0] = '\0';

argument = one_argument( argument, arg1 );
argument = one_argument( argument, arg2 );
argument = one_argument( argument, arg3 );
argument = one_argument( argument, arg4 );

if ( IS_NPC( ch ) || ch->desc == NULL )
return;

if ( ch->Class == CLASS_MERCHANT ) {
if ( ch->pcdata->tradeclass == 21 )
ch->pcdata->whichtrade = 1;
else if ( ch->pcdata->tradeclass2 == 21 )
ch->pcdata->whichtrade = 2;
else if ( ch->pcdata->tradeclass3 == 21 )
ch->pcdata->whichtrade = 3;
}

if ( arg1[0] == '\0' ) {
send_bake_syntax( ch );
return;
}

if ( ch->position != POS_STANDING ) {
send_to_char("You should be standing to attempt this.\r\n", ch );
}


if ( !str_cmp( arg1, "list" ) ) {
int y;
int count = 0;

send_to_char( "\t\t\t&CThe Baked Food Listing\r\n\r\n", ch );

if ( !str_cmp( arg2, "all" ) ) {
for ( y = 0; y < MAX_FOOD; y++ ) {
count++;
ch_printf( ch, "&c%-15s %-3d ", food_one[y].name ? food_one[y].name : "null name",
food_one[y].level );

if ( count == 3 ) {
count = 0;
send_to_char( "\r\n", ch );
}
}
if ( count != 0 )
send_to_char( "\r\n", ch );
send_bake_syntax( ch );
return;
}

for ( y = 0; y < MAX_FOOD; y++ ) {
if ( mayi_baker(ch) < food_one[y].level )
continue;
count++;
ch_printf( ch, "&c%-15s ", food_one[y].name ? food_one[y].name : "null name" );
if ( count == 3 ) {
count = 0;
send_to_char( "\r\n", ch );
}
}

if ( count != 0 )
send_to_char( "\r\n", ch );
send_bake_syntax( ch );
return;
}

if ( ch->move < 1 )
{
send_to_char("You don't have enough move points to perform that.\r\n", ch );
return;
}
else
{
ch->move -= 1;
}


if ( !str_cmp( arg1, "rename" ) || !str_cmp( arg1, "rekey" ) || !str_cmp( arg1, "salve" ) ) {
for ( bakedfood = ch->first_carrying; bakedfood; bakedfood = bakedfood->next_content ) {
if ( bakedfood->pIndexData->vnum == OBJ_VNUM_CRAFTFOOD )
break;
}
if ( !bakedfood ) {
send_to_char( "You do not have any crafted foodstuff to rename or rekey.\r\n", ch );
return;
}
}

if ( mayi_baker(ch) > 4 && ( !str_cmp( arg1, "salve" ) && !str_cmp( arg2, "heal" ) ) ) {
act( AT_CYAN, "$n grinds the final baked product into a healing herbed salve.", ch, NULL,
NULL, TO_ROOM );
act( AT_CYAN, "You grind the final baked product into a healing herbed salve.", ch, NULL,
NULL, TO_CHAR );
bakedfood->item_type = ITEM_SALVE;
bakedfood->value[0] = mayi_baker(ch) * 5; // spell level
bakedfood->value[1] = mayi_baker(ch) / 2; // charges
bakedfood->value[2] = mayi_baker(ch) / 2; // max charges
bakedfood->value[3] = 12; // affect delay
bakedfood->value[4] = 58; // affect 134 or 58 disinfect or
// medicene

// salve naming

sprintf( name_buf, "%s salve", bakedfood->name );
sprintf( short_buf, "%s salve", bakedfood->short_descr );
if ( bakedfood->name )
STRFREE( bakedfood->name );
if ( bakedfood->short_descr )
STRFREE( bakedfood->short_descr );
bakedfood->name = STRALLOC( name_buf );
bakedfood->short_descr = STRALLOC( short_buf );
return;
}

if ( mayi_baker(ch) > 4
&& ( !str_cmp( arg1, "salve" ) && !str_cmp( arg2, "antidote" ) ) ) {
act( AT_CYAN, "$n grinds the final baked product into a antidote herbed salve.", ch, NULL,
NULL, TO_ROOM );
act( AT_CYAN, "You grind the final baked product into a antidote herbed salve.", ch, NULL,
NULL, TO_CHAR );
bakedfood->item_type = ITEM_SALVE;
bakedfood->value[0] = mayi_baker(ch) * 5;
bakedfood->value[1] = mayi_baker(ch) / 2;
bakedfood->value[2] = mayi_baker(ch) / 2;
bakedfood->value[3] = 12; // affect delay
bakedfood->value[4] = 42; // affect cure affliction

// salve naming

sprintf( name_buf, "%s salve", bakedfood->name );
sprintf( short_buf, "%s salve", bakedfood->short_descr );
if ( bakedfood->name )
STRFREE( bakedfood->name );
if ( bakedfood->short_descr )
STRFREE( bakedfood->short_descr );
bakedfood->name = STRALLOC( name_buf );
bakedfood->short_descr = STRALLOC( short_buf );
return;
}

if ( !str_cmp( arg1, "rename" ) && mayi_baker(ch) > 14
&& !xIS_SET( ch->act, PLR_NORENAME ) ) {
if ( !arg3 || arg3[0] == '\0' ) {
send_to_char
( "You must specify the new keywords, ie 'rename item new name' or 'rename ring toering'.\r\n",
ch );
return;
}

ch_printf( ch, "&cChanging item's description from &C%s&c to&C %s&c..\r\n",
bakedfood->short_descr, arg3 );
STRFREE( bakedfood->short_descr );
bakedfood->short_descr = STRALLOC( arg3 );
sprintf( long_buf, "A %s has been left here along the ground.", bakedfood->short_descr );
STRFREE( bakedfood->description );
bakedfood->description = STRALLOC( long_buf );
send_to_char
( "&RDone!&c Please note, the KEYWORDS used to drop/equip/etc remain the same.\r\n",
ch );
save_char_obj( ch );
return;
}
else if ( !str_cmp( arg1, "rename" ) && ( mayi_baker(ch) < 15
|| xIS_SET( ch->act, PLR_NORENAME ) ) ) {
send_to_char
( "You must specify the new keywords, ie 'rename item new name' or 'rename ring toering'.\r\n",
ch );
return;
}

if ( !str_cmp( arg1, "rekey" ) && mayi_baker(ch) > 14
&& !xIS_SET( ch->act, PLR_NORENAME ) ) {
ch_printf( ch, "&cChanging item's keywords from &C%s&c to &C%s&c..\r\n", bakedfood->name,
arg3 );
STRFREE( bakedfood->name );
bakedfood->name = STRALLOC( arg3 );
send_to_char
( "&RDone!&c Please note, the DESCRIPTION when looking at the item remains the same.\r\n",
ch );
save_char_obj( ch );
return;
}
else if ( !str_cmp( arg1, "rekey" ) && ( mayi_baker(ch) < 15
|| xIS_SET( ch->act, PLR_NORENAME ) ) ) {
send_to_char
( "You must specify the new keywords, ie 'bake rekey whatever new name' or 'bake rekey sword toothpick'.\r\n",
ch );
return;
}

if ( !str_cmp( arg1, "fire" ) ) {
OBJ_DATA *stove;
bool found;

found = FALSE;

for ( stove = ch->in_room->first_content; stove; stove = stove->next_content ) {
if ( stove->item_type == ITEM_STOVE ) {
found = TRUE;
break;
}
}

if ( !found ) {
send_to_char( "There must be an oven to fire it.\r\n", ch );
return;
}

if ( stove->value[0] == 1 ) {
send_to_char( "There is no need to fire the oven, it is already.\r\n", ch );
return;
}

OBJ_DATA *coal;

for ( coal = ch->first_carrying; coal; coal = coal->next_content ) {
if ( coal->item_type == ITEM_COAL )
break;
}

if ( !coal ) {
send_to_char( "You do not have any coal to fire the oven.\r\n", ch );
return;
}

separate_obj( coal );
obj_from_char( coal );

act( AT_CYAN, "$n fires up the oven lighting the coal within it.", ch, NULL, NULL,
TO_ROOM );
act( AT_CYAN, "You fire the oven lighting the coal within it.", ch, NULL, NULL, TO_CHAR );
act( AT_YELLOW, "\r\nA flame flickers within the oven.", ch, NULL, NULL, TO_ROOM );
act( AT_YELLOW, "\r\nA flame flickers within the oven.", ch, NULL, NULL, TO_CHAR );
extract_obj( coal );
stove->value[0] = 1;
return;
}

OBJ_DATA *bag,
*obj;

for ( bag = ch->first_carrying; bag; bag = bag->next_content ) {
if ( bag->item_type == ITEM_GATHER_BAG )
break;
}

if ( !bag ) {
send_to_char( "You do not have a baker's bag to bake with.\r\n", ch );
return;
}

if ( ( bag->craft1 < 1 && !str_cmp( arg1, "strawberries" ) ) ||
( bag->craft2 < 1 && !str_cmp( arg1, "watermelons" ) ) ||
( bag->craft3 < 1 && !str_cmp( arg1, "lettuce" ) ) ||
( bag->craft4 < 1 && !str_cmp( arg1, "pickles" ) ) ||
( bag->craft5 < 1 && !str_cmp( arg1, "wheat" ) ) ||
( bag->craft6 < 1 && !str_cmp( arg1, "oranges" ) ) ||
( bag->craft7 < 1 && !str_cmp( arg1, "peaches" ) ) ||
( bag->craft8 < 1 && !str_cmp( arg1, "carrots" ) ) ||
( bag->craft9 < 1 && !str_cmp( arg1, "cinammon" ) ) ||
( bag->craft10 < 1 && !str_cmp( arg1, "fig" ) ) ||
( bag->craft11 < 1 && !str_cmp( arg1, "blueberries" ) ) ||
( bag->craft12 < 1 && !str_cmp( arg1, "pears" ) ) ||
( bag->craft13 < 1 && !str_cmp( arg1, "onions" ) ) ||
( bag->craft14 < 1 && !str_cmp( arg1, "sugar-cane" ) ) ||
( bag->craft15 < 1 && !str_cmp( arg1, "ginger" ) ) ||
( bag->craft16 < 1 && !str_cmp( arg1, "raspberries" ) ) ||
( bag->craft17 < 1 && !str_cmp( arg1, "corn" ) ) ||
( bag->craft18 < 1 && !str_cmp( arg1, "radishes" ) ) ||
( bag->craft19 < 1 && !str_cmp( arg1, "garlic" ) ) ||
( bag->craft20 < 1 && !str_cmp( arg1, "pepper" ) )
) {
send_to_char( "You would need to gather more in your baker's bag first.\r\n", ch );
return;
}

if ( !str_cmp( arg1, "strawberries" ) ) {
bag->craft1 -= 1;
}
else if ( !str_cmp( arg1, "watermelons" ) ) {
bag->craft2 -= 1;
}
else if ( !str_cmp( arg1, "lettuce" ) ) {
bag->craft3 -= 1;
}
else if ( !str_cmp( arg1, "pickles" ) ) {
bag->craft4 -= 1;
}
else if ( !str_cmp( arg1, "wheat" ) ) {
bag->craft5 -= 1;
}
else if ( !str_cmp( arg1, "oranges" ) ) {
bag->craft6 -= 1;
}
else if ( !str_cmp( arg1, "peaches" ) ) {
bag->craft7 -= 1;
}
else if ( !str_cmp( arg1, "carrots" ) ) {
bag->craft8 -= 1;
}
else if ( !str_cmp( arg1, "cinammon" ) ) {
bag->craft9 -= 1;
}
else if ( !str_cmp( arg1, "fig" ) ) {
bag->craft10 -= 1;
}
else if ( !str_cmp( arg1, "blueberries" ) ) {
bag->craft11 -= 1;
}
else if ( !str_cmp( arg1, "pears" ) ) {
bag->craft12 -= 1;
}
else if ( !str_cmp( arg1, "onions" ) ) {
bag->craft13 -= 1;
}
else if ( !str_cmp( arg1, "sugar-cane" ) ) {
bag->craft14 -= 1;
}
else if ( !str_cmp( arg1, "ginger" ) ) {
bag->craft15 -= 1;
}
else if ( !str_cmp( arg1, "raspberries" ) ) {
bag->craft16 -= 1;
}
else if ( !str_cmp( arg1, "corn" ) ) {
bag->craft17 -= 1;
}
else if ( !str_cmp( arg1, "radishes" ) ) {
bag->craft18 -= 1;
}
else if ( !str_cmp( arg1, "garlic" ) ) {
bag->craft19 -= 1;
}
else if ( !str_cmp( arg1, "pepper" ) ) {
bag->craft20 -= 1;
}

if ( arg2[0] == '\0' || arg3[0] == '\0' ) {
send_bake_syntax( ch );
return;

}
if ( str_cmp( arg2, "into" ) ) {
send_bake_syntax( ch );
return;
}

if ( !IS_SET( ch->in_room->room_flags, ROOM_TRADESKILLS ) ) {
send_to_char( "You must be in a tradeskills building to do this.\r\n", ch );
return;
}

{
OBJ_DATA *stove;
bool found;

found = FALSE;

for ( stove = ch->in_room->first_content; stove; stove = stove->next_content ) {
if ( stove->item_type == ITEM_STOVE ) {
found = TRUE;
break;
}
}

if ( !found ) {
send_to_char( "There must be a oven in the room in order to do that.\r\n", ch );
return;
}

if ( stove->value[0] != 1 ) {
send_to_char( "&cYou have to fire the oven first.\r\n", ch );
send_to_char( "&cSyntax: bake fire\r\n", ch );
return;
}

// This is the nitty gritty of checking tier output
for ( i = 0; i < MAX_FOOD; i++ ) {
if ( mayi_baker(ch) < food_one[i].level )
continue;
if ( !str_cmp( arg3, food_one[i].name ) ) {
itm = TRUE;
break;
}
}

if ( itm == FALSE ) {
send_to_char( "This is not a valid food type.\r\n", ch );
return;
}

SKILLTYPE *skill = get_skilltype( gsn_bake );

if ( mayi_baker(ch) <= 1 )
adj = "poorly";
else if ( mayi_baker(ch) <= 5 )
adj = "simply";
else if ( mayi_baker(ch) <= 8 )
adj = "properly";
else if ( mayi_baker(ch) <= 10 )
adj = "well";
else if ( mayi_baker(ch) <= 15 )
adj = "finely";
else if ( mayi_baker(ch) <= 19 )
adj = "masterfully";
else
adj = "legendary";

WAIT_STATE( ch, 15 );

if ( bag->value[6] == 0 ) {
if ( ch->pcdata->learned[gsn_bake] <= number_range( 1, 5 ) || number_percent( ) <= 5 ) {
act( AT_CYAN,
"$n prepares the ingredient by placing it in the oven, but the ingredient is left too long and burns.",
ch, NULL, NULL, TO_ROOM );
act( AT_CYAN,
"You prepare the ingredient by placing it in the oven, but the ingredient is left too long and burns.",
ch, NULL, NULL, TO_CHAR );
learn_from_failure( ch, gsn_bake );
return;
}

act( AT_CYAN, "$n prepares the ingredient by placing it in the oven.", ch, NULL, NULL,
TO_ROOM );
act( AT_CYAN, "You prepare the ingredient by placing it in the oven.", ch, NULL, NULL,
TO_CHAR );
obj = create_object( get_obj_index( OBJ_VNUM_CRAFTFOOD ), 0 );
if ( obj->name )
STRFREE( obj->name );
if ( obj->short_descr )
STRFREE( obj->short_descr );
if ( obj->description )
STRFREE( obj->description );
obj->name = STRALLOC( "dish" );
obj->description = STRALLOC( "A cooked dish has been left here." );
obj->short_descr = STRALLOC( "a cooked dish" );
obj_to_char( obj, ch );
bag->value[6] = 1;
return;
}

if ( bag->value[6] == 1 ) {
if ( ch->pcdata->learned[gsn_bake] <= number_range( 1, 10 )
|| number_percent( ) <= 25 ) {

for ( obj = ch->first_carrying; obj; obj = obj->next_content ) {
if ( obj->item_type == ITEM_RAW )
break;
}

if ( !obj ) {
send_to_char( "You do not have your dish.\r\n", ch );
bag->value[6] = 0;
return;
}

act( AT_CYAN,
"$n places the cooked dish on the table and begins to add garnishments but gets distracted and ruins the dish by adding too much.",
ch, NULL, NULL, TO_ROOM );
act( AT_CYAN,
"You place the cooked dish on the table and begin to add garnishments, but get distracted and ruin the dish by adding too much.",
ch, NULL, NULL, TO_CHAR );
separate_obj( obj );
obj_from_char( obj );
extract_obj( obj );
bag->value[6] = 0;
learn_from_failure( ch, gsn_bake );
return;
}

act( AT_CYAN, "$n places the cooked dish on the table and begins to add garnishments.",
ch, NULL, NULL, TO_ROOM );
act( AT_CYAN, "You place the cooked dish on the table and begin to add garnishments.",
ch, NULL, NULL, TO_CHAR );
bag->value[6] += 1;
for ( obj = ch->first_carrying; obj; obj = obj->next_content ) {
if ( obj->item_type == ITEM_RAW )
break;
}

if ( !obj ) {
send_to_char( "You do not have your dish.\r\n", ch );
bag->value[6] = 0;
return;
}

obj->short_descr = STRALLOC( "a prepped dish" );
return;
}

if ( bag->value[6] == 2 ) {
if ( !can_use_skill( ch, number_percent( ), gsn_bake ) || number_percent( ) <= 25 ) {
act( AT_CYAN, "$n tastes the dish to see if it is right, then keeps adding to it.",
ch, NULL, NULL, TO_ROOM );
act( AT_CYAN,
"You taste the dish to see if it is right, then continue adding to it.", ch,
NULL, NULL, TO_CHAR );
learn_from_failure( ch, gsn_bake );
return;
}

if ( number_percent( ) <= 25 ) {
act( AT_CYAN,
"$n begins to stir the prepped dish, but gets distracted and ruins the dish by over stirring it.",
ch, NULL, NULL, TO_ROOM );
act( AT_CYAN,
"You begin to stir the prepped dish, but get distracted and ruin the dish by over stirring it.",
ch, NULL, NULL, TO_CHAR );
for ( obj = ch->first_carrying; obj; obj = obj->next_content ) {
if ( obj->item_type == ITEM_RAW )
break;
}

if ( !obj ) {
send_to_char( "You do not have your dish.\r\n", ch );
bag->value[6] = 0;
return;
}

separate_obj( obj );
obj_from_char( obj );
extract_obj( obj );
bag->value[6] = 0;
learn_from_failure( ch, gsn_bake );
return;
}
act( AT_CYAN, "$n begins to stir the prepped dish.", ch, NULL, NULL, TO_ROOM );
act( AT_CYAN, "You begin to stir the prepped dish.", ch, NULL, NULL, TO_CHAR );
bag->value[6] += 1;
for ( obj = ch->first_carrying; obj; obj = obj->next_content ) {
if ( obj->item_type == ITEM_RAW )
break;
}

if ( !obj ) {
send_to_char( "You do not have your dish.\r\n", ch );
bag->value[6] = 0;
return;
}

obj->short_descr = STRALLOC( "a prepared dish" );
return;
}
if ( number_percent( ) <= 25 ) {
act( AT_CYAN,
"$n takes out some spices, but gets distracted and ruins the dish by adding too much.",
ch, NULL, NULL, TO_ROOM );
act( AT_CYAN,
"You take out some spices, but get distracted and ruin the dish by adding too much.",
ch, NULL, NULL, TO_CHAR );
for ( obj = ch->first_carrying; obj; obj = obj->next_content ) {
if ( obj->item_type == ITEM_RAW )
break;
}

if ( !obj ) {
send_to_char( "You do not have your dish.\r\n", ch );
bag->value[6] = 0;
return;
}

separate_obj( obj );
obj_from_char( obj );
extract_obj( obj );
bag->value[6] = 0;
learn_from_failure( ch, gsn_bake );
return;
}

act( AT_CYAN, "$n takes out some spices, and adds the finishing touches.", ch, NULL, NULL,
TO_ROOM );
act( AT_CYAN, "You take out some spices, and add the finishing touches.", ch, NULL, NULL,
TO_CHAR );
bag->value[6] = 0;
learn_from_craft( ch, gsn_bake );
bakedfood = create_object( get_obj_index( OBJ_VNUM_CRAFTFOOD ), 1 );
if ( arg4[0] == '\0' )
bakedfood->level = ch->level;
else
bakedfood->level = atoi( arg4 );

found = FALSE;

for ( i = 0; i < MAX_FOOD; i++ ) {
if ( mayi_baker(ch) < food_one[i].level )
continue;
if ( !str_cmp( arg3, food_one[i].name ) ) {
found = TRUE;
break;
}
}

if ( !found ) {
send_to_char( "Can't create it for some odd reason, inform a STAFF member.\r\n", ch );
return;
}

sprintf( name_buf, "%s", food_one[i].name );
sprintf( short_buf, "a %s, %s cooked from %s", food_one[i].name, adj, STRALLOC( arg1 ) );
sprintf( long_buf, "Here lies a %s, %s cooked from %s.", food_one[i].name, adj,
STRALLOC( arg1 ) );
bakedfood->item_type = food_one[i].item_type;
bakedfood->wear_flags += food_one[i].wear_flags;
bakedfood->weight = ( food_one[i].weight );
bakedfood->cost = food_one[i].weight;
bakedfood->value[6] = 1;
bakedfood->value[0] = 5;
bakedfood->pIndexData->value[6] = 1;
if ( mayi_baker(ch) >= 5 )
bakedfood->value[6] = 2;
if ( mayi_baker(ch) >= 10 )
bakedfood->value[6] = 3;

if ( mayi_baker(ch) >= 20 ) {
GET_VALUE( bakedfood, type ) = CURR_GOLD;
bakedfood->cost = 30;
}
else if ( mayi_baker(ch) >= 15 ) {
GET_VALUE( bakedfood, type ) = CURR_GOLD;
bakedfood->cost = 25;
}
else if ( mayi_baker(ch) >= 10 ) {
GET_VALUE( bakedfood, type ) = CURR_GOLD;
bakedfood->cost = 15;
}
else if ( mayi_baker(ch) >= 5 ) {
GET_VALUE( bakedfood, type ) = CURR_SILVER;
bakedfood->cost = 50;
}
else {
GET_VALUE( bakedfood, type ) = CURR_COPPER;
bakedfood->cost = 25;
}

sprintf( extra_buf,
"\r\n&CThis crafted dish bears the seal of %s, the %s baker.\r\n",
ch->name,
mayi_baker(ch) <=
5 ? "apprentice" : mayi_baker(ch) <= 10 ? "journeyman" :
mayi_baker(ch) ? "expert" : mayi_baker(ch) ==
20 ? "master" : "reknowned" );

if ( bakedfood->name )
STRFREE( bakedfood->name );
if ( bakedfood->short_descr )
STRFREE( bakedfood->short_descr );
if ( bakedfood->description )
STRFREE( bakedfood->description );

bakedfood->name = STRALLOC( name_buf );
bakedfood->short_descr = STRALLOC( short_buf );
bakedfood->description = STRALLOC( long_buf );

EXTRA_DESCR_DATA *ed;

CREATE( ed, EXTRA_DESCR_DATA, 1 );

LINK( ed, bakedfood->first_extradesc, bakedfood->last_extradesc, next, prev );
ed->keyword = STRALLOC( bakedfood->name );
ed->description = STRALLOC( extra_buf );

if ( mayi_baker(ch) <= 1 )
bakedfood->value[1] = 1;
else if ( mayi_baker(ch) <= 5 )
bakedfood->value[1] = 3;
else if ( mayi_baker(ch) <= 8 )
bakedfood->value[1] = 5;
else if ( mayi_baker(ch) <= 10 )
bakedfood->value[1] = 8;
else if ( mayi_baker(ch) <= 15 )
bakedfood->value[1] = 10;
else if ( mayi_baker(ch) <= 19 )
bakedfood->value[1] = 12;
else
bakedfood->value[1] = 15;
for ( obj = ch->first_carrying; obj; obj = obj->next_content ) {
if ( obj->item_type == ITEM_RAW )
break;
}

if ( !obj ) {
send_to_char( "You do not have your dish.\r\n", ch );
bag->value[6] = 0;
return;
}

bakedfood->color = obj->color;
separate_obj( obj );
obj_from_char( obj );
extract_obj( obj );

if ( ch->carry_number + get_obj_number( bakedfood ) > can_carry_n( ch ) ) {
send_to_char( "You can't carry that many items, and drop the food on the floor.\r\n",
ch );
separate_obj( bakedfood );
obj_from_char( bakedfood );
extract_obj( bakedfood );
return;
}

if ( ch->carry_weight + ( get_obj_weight( bakedfood, FALSE ) * 1 ) + ( 1 > 1 ? 2 : 0 ) >
can_carry_w( ch ) ) {
send_to_char( "You can't carry that much weight, and drop the food on the floor.\r\n",
ch );
separate_obj( bakedfood );
obj_from_char( bakedfood );
extract_obj( bakedfood );
return;
}

obj_to_char( bakedfood, ch );
short extinguish;

extinguish = number_chance( 1, 8 );
if ( extinguish == 8 ) {
send_to_char
( "\r\n&wThe oven burns the last of the coal and the flame is extinguished.\r\n",
ch );
stove->value[0] = 0;
}
CLAN_DATA *clan;

if ( IS_CLANNED( ch ) ) {
clan = ch->pcdata->clan;
ch->pcdata->clanpoints += 1;
clan->totalpoints += 1;
ch_printf( ch,
"\r\n&G%s clan has gained a status point from your craftsmanship, now totaling %d clan status points!\r\n",
clan->name, clan->totalpoints );
save_char_obj( ch );
save_clan( clan );
}
return;
tail_chain( );
}
}
06 May, 2014, quixadhal wrote in the 5th comment:
Votes: 0
Ouch! Seeing ingredients hard-coded makes me cry.
06 May, 2014, jeli wrote in the 6th comment:
Votes: 0
quixadhal said:
Ouch! Seeing ingredients hard-coded makes me cry.


How else could it be done using SMAUG? Stop tempting me to learn LPC. *mutter*

But any thoughts on how I can make this work? I don't even care about the 4 step process, would be satisfied if the crafting step just had a chance to fail or succeed.
06 May, 2014, plamzi wrote in the 7th comment:
Votes: 0
jeli said:
quixadhal said:
Ouch! Seeing ingredients hard-coded makes me cry.


How else could it be done using SMAUG? Stop tempting me to learn LPC. *mutter*

But any thoughts on how I can make this work? I don't even care about the 4 step process, would be satisfied if the crafting step just had a chance to fail or succeed.


You don't need LPC to organize your static strings in arrays and structs, and do your lookups using utility functions that traverse the static data. It's just best practice. I'm guessing it will cut the code you're showing by about two-thirds.
06 May, 2014, jeli wrote in the 8th comment:
Votes: 0
True I don't need LPC, but it's just inspiring me to take on an LP mud and learn it so I can finally do what I want in a mud without having to sort through code that I don't understand.

That being said, I've installed the latest version of Dead Souls and I'm wondering if there is a way to import any area files from other mud codebases? I have an entire world of Dragonlance areas already built in a slightly modified ROM form and in CoffeeMUD form. Would save me a lot of building as they're already over 8000+ rooms.
07 May, 2014, quixadhal wrote in the 9th comment:
Votes: 0
You'd do it the way other systems in Smaug do it… IE: the liquids system comes to mind. At boot time, you read in a file with all the known ingredients and recipies, and as the player tries to use them, you walk the list (tree, array, whatever) to see if what they typed matches one.

There isn't any "import" feature, but…. you could write a converter without too much trouble.

I have a stand-alone program that reads in my own (custom DikuMUD) file format and spits out several different formats, some more complete than others. There was a time I planned to convert my game directly from DikuMUD to *some LPMUD mudlib we hadn't decided on yet*.

If you want to see the conversion code I have, it's in https://github.com/quixadhal/wileymud/tr...

My suggestion is to not use that, but instead take the idea of generating LPC files from the data, and add it to your existing codebase, so when you do a "savearea" or whatever, it not only writes to the normal native format, but also builds a tree of LPC files the way my converter does. Then the only tricky part is digging through your target MUD's code/documentation and trying to find equivalents to the native flags/properties.

For example, the ROOM_INDOORS flag on DikuMUD translates to two things in the Dead Souls mudlib.. SetAmbientLight() to a level appropriate for an indoor room (80 seems to be the usual), and SetClimate("indoors") which prevents weather from affecting the room.
10 May, 2014, Nathan wrote in the 10th comment:
Votes: 0
quixadhal said:
Ouch! Seeing ingredients hard-coded makes me cry.


Really? Stuff doesn't need to all be loaded from a file, especially if you don't plan on changing anything. I do have to wonder if defining all those strings in a separate file would make sense, though. You know, just to make it easier if you want to rewrite them or fix spelling errors.


( bag->craft20 < 1 && !str_cmp( arg1, "pepper" ) )


This does look like an array might help… You know like bag->craft[20]

@plamzi +1
10 May, 2014, quixadhal wrote in the 11th comment:
Votes: 0
Why in the world would you EVER be naive enough to think you'll NEVER want to change the list of ingredients your crafting system uses? That's just too silly to say anything else about.

Might as well just hard-code all your weapons and NPC's too, since that'll save you from loading them from a file.
10 May, 2014, Scandum wrote in the 12th comment:
Votes: 0
You could build a MUD with TinTin++, it'd be pure awesome sauce. You can run any existing *nix MUD within tt++ and add hooks where needed to modify its behavior. You automatically gain safe threaded behavior and it'd be highly modular. It'd also be the greatest hack-job in MUD history, and in some instances be the most elegant solution to many development issues.
10 May, 2014, hitsuzen wrote in the 13th comment:
Votes: 0
That code quoted is spaghetti code really. I get you can return whenever you want, but you shouldn't. It makes reading the flow extremely difficult when you have even 60 lines of code. That entire function probably needs to be rewritten – zero return points since the return type is void. If you reach an end point you need to terminate on, use more conditional logic or something. Also, the least you can do is replace the hardcoded recipe shit with macro directives.
0.0/13