.-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-.
| |
| |
| |
| ELEMENTAL ITEMS |
| |
! !
: A Snippet written by :
: Valcados :
. for SMAUG 1.02 .
. .
: :
: :
! !
| |
| |
| |
| |
| |
`-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-'
DESCRIPTION:
This code snippet adds support for elemental items in the
SMAUG codebase. This mainly effects weapons and armors.
When a fighter uses a non-elemental weapon, his attacks are
influenced by his opponent's resistance/susceptibility/immunity
just to the weapon's type: slash, pierce, etc.
If the weapon is elemental, the attacks are then also influenced
by his opponent's res/susc/immunity to the element.
For example, a fire elemental sword will do no damage to an
enemy who is immune to fire, even if the enemy is susceptible to
swords. Thus an elemental weapon can be stronger than a non-
elemental weapon against appropriate foes, but it is a very
specialized tool, and in most cases is actually weaker, since
enemies tend to have more resistances than susceptibility.
Elemental armor is more likely to be damaged and scrapped by
attacks from weapons of the opposite element, and less likely
to be damaged and scrapped by attacks from weapons of the same
element.
Additionally, fire elemental items can be instantly destroyed
by the "frost breath" spell, and cold elemental items can be
instantly destroyed by the "fire breath" spell. This depends
upon the victim's saving spell vs. breath. Fire items, cold
items, and acid items are immune to destruction from fire breath,
frost breath, and acid breath, respectively.
TESTING:
This code has been in use on the Lands of Aethar MUD
(www.aethar.com) for several years now and is thoroughly
assimilated into that MUD. However, in writing this
snippet, I've made some tiny changes to make things more
SMAUGlike, and those are untested.
Also, bear in mind that Lands of Aethar code in general
is thoroughly modified, and is built out of an earlier
code release than 1.4a.
KNOWN PROBLEMS/BUGS:
In stock SMAUG 1.02, backstab and circle, although requiring
stabbing weapons to be wielded, don't actually automatically
deal the damage with the stabbing weapon, for instance if you
wield a dagger and a club, and backstab, the club may actually
be the weapon which is used. This of course means if the club
was fire elemental, your backstab/circle may be fire elemental
even though your dagger is not.
It hardly needs to be said, but an area with elemental items
might not be loadable on a MUD where this snippet is uninstalled.
Overall problem/bug assessment: utterly negligible.
IN-GAME MECHANICS:
Elements are set on items using the oset or mposet command.
It is possible to have items with elements different than their
prototypes, allowing such things as granting elements as quest
rewards or programming mobs to spawn the item with a randomized
element.
Elements are visible to players with "identify" and with the
status screen on the "auction" command. Also, certain elemental
weapons have extra adjectives (Your blazing slash wounds the
goblin). They are also visible with ostat.
HOW TO INSTALL:
1. in act_obj.c, in do_auction, find the code for when a player
enters no argument and thus is shown the item's stats.
At an appropriate place (I like to put it right after the
wearlocation is displayed) add:
show_element_to_char( ch, obj );
2. in act_wiz.c, in do_ostat, wherever you think appropriate
(I like to put it right after the "Extra affects"), add
show_element_to_char( ch, obj );
3. in build.c, if it isn't already there (or in mud.h), add the global
function prototype (near the top, you can put it right after the
#includes)
int get_sdamage args( ( char *name ) );
4. in build.c, in do_oset, find the list of fields for when a builder
types oset with no arguments, and add "element" to the list.
5. in build.c, in do_oset, find the line "if ( !str_cmp( arg2, "short" ) )"
Right above this, add the new block
if ( !str_cmp( arg2, "element" ) )
{
sh_int new_element = get_sdamage( arg3 );
if ( new_element == -1 )
{
send_to_char( "That's not a spell damage type.\n\r", ch );
return;
}
if ( IS_OBJ_STAT( obj, ITEM_PROTOTYPE ) )
{
if ( !can_omodify( ch, obj ) )
{
send_to_char( "You can't edit this item.\n\r", ch );
return;
}
obj->pIndexData->element = new_element;
}
if ( obj->element != new_element )
switch ( new_element )
{
case SD_NONE:
set_char_color( AT_PLAIN, ch );
ch_printf( ch, "You drain the elemental power from %s...\n\r", obj->short_descr );
break;
case SD_FIRE:
set_char_color( AT_FIRE, ch );
ch_printf( ch, "With a crackle, bright flames burst forth from %s!\n\r", obj->short_descr );
break;
case SD_COLD:
set_char_color( AT_LBLUE, ch );
ch_printf( ch, "Swirls of cold coalesce into %s, leaving flecks of ice over its surface...\n\r", obj->short_descr );
break;
case SD_ELECTRICITY:
set_char_color( AT_YELLOW, ch );
ch_printf( ch, "Currents of electricity run along the surface of %s, crackling...\n\r", obj->short_descr );
break;
case SD_ENERGY:
set_char_color( AT_WHITE, ch );
ch_printf( ch, "As mysterious energies are drawn into %s, it begins trembling with raw power!\n\r", obj->short_descr );
break;
case SD_ACID:
set_char_color( AT_GREEN, ch );
ch_printf( ch, "Drops of deadly acid fall from %s and sizzle in a pool on the ground...\n\r", obj->short_descr );
break;
case SD_POISON:
set_char_color( AT_DGREEN, ch );
ch_printf( ch, "Drops of deadly poison fall from %s and gather in a pool on the ground...\n\r", obj->short_descr );
break;
case SD_DRAIN:
set_char_color( AT_BLOOD, ch );
ch_printf( ch, "Nearby life forces begin to slowly converge into %s ...\n\r", obj->short_descr );
break;
default:
break;
}
obj->element = new_element;
set_char_color( AT_PURPLE, ch );
send_to_char( "Done.\n\r", ch );
return;
}
6. in build.c, in fold_area, find this code (or something like it):
if ( pObjIndex->layers )
fprintf( fpout, "%d %s %d %d\n", pObjIndex->item_type,
print_bitvector(&pObjIndex->extra_flags),
pObjIndex->wear_flags,
pObjIndex->layers );
else
fprintf( fpout, "%d %s %d\n", pObjIndex->item_type,
print_bitvector(&pObjIndex->extra_flags),
pObjIndex->wear_flags );
Replace it with the following:
fprintf( fpout, "%d %s %d %d %d\n", pObjIndex->item_type,
print_bitvector(&pObjIndex->extra_flags),
pObjIndex->wear_flags,
pObjIndex->layers,
pObjIndex->element );
7. At the bottom of build.c, add the following function:
void show_element_to_char( CHAR_DATA *ch, OBJ_DATA *obj )
{
char *txt = NULL;
if ( obj->element == SD_NONE )
return;
switch ( obj->element )
{
case SD_FIRE:
set_char_color( AT_FIRE, ch );
txt = "fire";
break;
case SD_COLD:
set_char_color( AT_LBLUE, ch );
txt = "ice";
break;
case SD_ELECTRICITY:
set_char_color( AT_YELLOW, ch );
txt = "lightning";
break;
case SD_ENERGY:
set_char_color( AT_WHITE, ch );
txt = "energy";
break;
case SD_ACID:
set_char_color( AT_GREEN, ch );
txt = "acid";
break;
case SD_POISON:
set_char_color( AT_DGREEN, ch );
txt = "poison";
break;
case SD_DRAIN:
set_char_color( AT_BLOOD, ch );
txt = "life draining";
break;
default:
break;
}
if ( txt )
ch_printf( ch, "This item has been imbued with the element of %s.\n\r", txt );
set_char_color( AT_MAGIC, ch );
return;
}
8. In db.c, in load_objects, find the following code (or something similar):
x1=x2=0;
sscanf(ln, "%d %d",
&x1, &x2);
pObjIndex->wear_flags = x1;
pObjIndex->layers = x2;
Replace it with the following:
x1=x2=x3=0;
sscanf(ln, "%d %d %d",
&x1, &x2, &x3);
pObjIndex->wear_flags = x1;
pObjIndex->layers = x2;
pObjIndex->element = x3;
9. In db.c, in create_object, find the line "obj->cost = pObjIndex->cost;" (or similar)
and below it, add the line
obj->element = pObjIndex->element;
10. In db.c, in make_object, find the line "pObjIndex->cost = 0;" (or similar)
and below it, add the line
pObjIndex->element = SD_NONE;
11. In db.c, in make_object, find the line
"pObjIndex->short_descr = QUICKLINK(cObjIndex->short_descr);" (or similar)
and below it, add the line
pObjIndex->element = cObjIndex->element;
12. In fight.c, in the global function declarations, change the
new_dam_message declaration from
void new_dam_message args( ( CHAR_DATA *ch, CHAR_DATA *victim, int dam,
int dt, OBJ_DATA *obj ) );
to
void new_dam_message args( ( CHAR_DATA *ch, CHAR_DATA *victim, int dam,
int dt, OBJ_DATA *obj, sh_int elem ) );
13. In fight.c, just above where the is_attack_supressed function
starts, add the following.
const int ris_from_sd[] = { -1, RIS_FIRE, RIS_COLD, RIS_ELECTRICITY, RIS_ENERGY, RIS_ACID,
RIS_POISON, RIS_DRAIN };
const sh_int opposite_elements[] = { SD_NONE, SD_COLD, SD_FIRE, SD_ACID, SD_DRAIN, SD_ELECTRICITY,
SD_ENERGY, SD_ENERGY };
char * const attack_adjective[] = { "", "blazing ", "freezing ", "", "", "corrossive ", "", "" };
Incidentally, if you want to remove the elemental attack adjectives, or
add new ones, the attack_adjectives array above is the place to do it.
14. In fight.c, in one_hit, find the following code (or similar):
if ( (retcode = damage( ch, victim, dam, dt )) != rNONE )
return retcode;
replace it with this:
if ( wield )
retcode = specific_damage( ch, victim, dam, dt, wield->element );
else
retcode = damage( ch, victim, dam, dt );
if ( retcode != rNONE )
return retcode;
15. In fight.c, find the damage function. Change the top line
ch_ret damage(CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt )
to
ch_ret specific_damage(CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt, sh_int elem )
16. (fight.c) Now right above what was the damage function, and is now the
specific_damage function, add the following:
ch_ret damage( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt )
{
return specific_damage( ch, victim, dam, dt, SD_NONE );
}
17. (fight.c) Now in the specific_damage function, find the code
where damage is modified by the victim's res/susc/immunity to
the weapon. It might look something like this...
if ( dt == (TYPE_HIT + DAM_POUND) || dt == (TYPE_HIT + DAM_CRUSH)
|| dt == (TYPE_HIT + DAM_STONE) || dt == (TYPE_HIT + DAM_PEA) )
dam = ris_damage(victim, dam, RIS_BLUNT);
else
if ( dt == (TYPE_HIT + DAM_STAB) || dt == (TYPE_HIT + DAM_PIERCE)
|| dt == (TYPE_HIT + DAM_BITE) || dt == (TYPE_HIT + DAM_BOLT)
|| dt == (TYPE_HIT + DAM_DART) || dt == (TYPE_HIT + DAM_ARROW) )
dam = ris_damage(victim, dam, RIS_PIERCE);
else
if ( dt == (TYPE_HIT + DAM_SLICE) || dt == (TYPE_HIT + DAM_SLASH)
|| dt == (TYPE_HIT + DAM_WHIP) || dt == (TYPE_HIT + DAM_CLAW) )
dam = ris_damage(victim, dam, RIS_SLASH);
Right below it, add the following:
if ( dam > 0 && elem != SD_NONE )
dam = ris_damage( victim, dam, ris_from_sd[elem] );
18. Still in fight.c and still in specific_damage, find the block
similar to this:
if ( damobj )
{
if ( dam > get_obj_resistance(damobj)
&& number_bits(1) == 0 )
{
set_cur_obj(damobj);
damage_obj(damobj);
}
dam -= 5; /* add a bonus for having something to block the blow */
}
Change it to this:
if ( damobj && dam )
{
sh_int dam_mult = 1;
sh_int skin_bonus = 5;
/*
* Elemental items more resistant against same element,
* less resistant against opposite element.
*/
if ( damobj->element != SD_NONE && elem != SD_NONE )
{
if ( damobj->element == elem )
dam_mult += 2, skin_bonus -= 2;
else if ( damobj->element == opposite_elements[elem] )
dam_mult -= 2, skin_bonus += 2;
}
if ( dam > ( get_obj_resistance( damobj ) * dam_mult )
&& ( dam_mult > 2 || number_bits(1) == 0 ) )
{
set_cur_obj(damobj);
damage_obj(damobj);
}
dam -= skin_bonus; /* add a bonus for having something to block the blow */
}
19. Still in fight.c in specific_damage, find a line like
dam_message(ch, victim, dam, dt );
Replace it with
dam_message(ch, victim, dam, dt, elem);
20. In fight.c, find the dam_message function. Change the very top line
from something like
void new_dam_message( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt, OBJ_DATA *obj )
to
void new_dam_message( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt, OBJ_DATA *obj, sh_int elem )
21. In fight.c, in dam_message, find something like
sprintf( buf1, "$n's poisoned %s %s $N%c", attack, vp, punct );
sprintf( buf2, "Your poisoned %s %s $N%c", attack, vp, punct );
sprintf( buf3, "$n's poisoned %s %s you%c", attack, vp, punct );
Change it to
sprintf( buf1, "$n's %spoisoned %s %s $N%c", attack_adjective[elem], attack, vp, punct );
sprintf( buf2, "Your %spoisoned %s %s $N%c", attack_adjective[elem], attack, vp, punct );
sprintf( buf3, "$n's %spoisoned %s %s you%c", attack_adjective[elem], attack, vp, punct );
22. In fight.c, in dam_message, find something like
sprintf( buf1, "$n's %s %s $N%c", attack, vp, punct );
sprintf( buf2, "Your %s %s $N%c", attack, vp, punct );
sprintf( buf3, "$n's %s %s you%c", attack, vp, punct );
Change it to
sprintf( buf1, "$n's %s%s %s $N%c", attack_adjective[elem], attack, vp, punct );
sprintf( buf2, "Your %s%s %s $N%c", attack_adjective[elem], attack, vp, punct );
sprintf( buf3, "$n's %s%s %s you%c", attack_adjective[elem], attack, vp, punct );
23. In fight.c, where the local functions are declared (near the top),
find the declaration of dam_message, something like
void dam_message args( ( CHAR_DATA *ch, CHAR_DATA *victim, int dam,
int dt, OBJ_DATA *obj ) );
Change it to
void new_dam_message args( ( CHAR_DATA *ch, CHAR_DATA *victim, int dam,
int dt, OBJ_DATA *obj, sh_int elem ) );
24. In handler.c, in clone_object, right below a line like
clone->value[5] = obj->value[5];
insert the new line
clone->element = obj->element;
25. In handler.c, in group_object, right below a line like
&& obj1->item_type == obj2->item_type
insert the new line
&& obj1->element == obj2->element
26. In magic.c, in spell_identify, at an appropriate point in the
midst of the identify info (I like to put it right after
the "special properties" blurb), add
show_element_to_char( ch, obj );
27. In magic.c, in spell_acid_breath, right below a line like
obj_next = obj_lose->next_content;
add the lines
/*
* Acid-elemental items immune to acid breath destruction
*/
if ( obj_lose->element == SD_ACID )
continue;
28. In magic.c, in spell_fire_breath, right some lines like
if ( number_bits( 2 ) != 0 )
continue;
add the lines
/*
* Fire-elemental items immune to immolation
*/
if ( obj_lose->element == SD_FIRE )
continue;
29. In magic.c, in spell_fire_breath, find a line like
default: continue;
and replace it with this:
default:
/*
* Cold-elemental items susceptible to immolation,
* even if their item type is not usually burnable
*/
if ( obj_lose->element == SD_COLD )
{
msg = "$p flashes bright blue before disintegrating!";
break;
}
else
continue;
30. In magic.c, in spell_frost_breath, find a line like
obj_next = obj_lose->next_content;
below it, add this:
/*
* Cold-elemental items immune to frost breath destruction
*/
if ( obj_lose->element == SD_COLD )
continue;
31. In magic.c, in spell_frost_breath, find a line like
default: continue;
replace it with:
default:
/*
* Fire-elemental items susceptible to freezing,
* even if their item type is not usually freezable
*/
if ( obj_lose->element == SD_FIRE )
{
msg = "$p flares up one last time before succumbing to the cold!";
break;
}
else
continue;
32. In mpxset.c, if it isn't already declared there (or in mud.h),
declare the global function
int get_sdamage args( ( char *name ) );
You could maybe, for example, put this right below a line like
int get_trigflag args( ( char *flag ) );
33. In mpxset.c, in do_mposet, find a block of code something like
if ( !str_cmp( arg2, "name" ) )
{
STRFREE( obj->name );
obj->name = STRALLOC( arg3 );
return;
}
Below it, add the following:
if ( !str_cmp( arg2, "element" ) )
{
sh_int new_el = get_sdamage( arg3 );
if ( new_el == -1 )
{
progbug( "Mposet element: bad element", ch );
return;
}
obj->element = new_el;
return;
}
34. In save.c, in fwrite_obj, look for some lines like this:
if ( !xSAME_BITS(obj->extra_flags, obj->pIndexData->extra_flags) )
fprintf( fp, "ExtraFlags %s\n", print_bitvector(&obj->extra_flags) );
beneath them, add this:
if ( obj->element != obj->pIndexData->element )
fprintf( fp, "Element %d\n", obj->element );
35. In save.c, in fread_obj, in the variable declarations at the top of
the function, declare new variable
bool fElement = FALSE;
36. In save.c, in fread_obj, find a line similar to
KEY( "ExtraFlags", obj->extra_flags, fread_bitvector( fp ) );
Below it, add this:
if ( !strcmp( word, "Element" ) )
{
fMatch = TRUE;
fElement = TRUE;
obj->element = fread_number( fp );
break;
}
37. In save.c, in fread_obj, find some lines like
if ( !obj->name )
obj->name = QUICKLINK( obj->pIndexData->name );
Beneath them, add this:
if ( !fElement )
obj->element = obj->pIndexData->element;
38. In mud.h, find the obj_index_data structure. Give it a new
entry:
sh_int element;
39. Repeat step 38 but this time for the obj_data structure.
40. In mud.h, find the global function declarations from build.c.
The easiest way to do this is probably searching for
"build.c", if that doesnt work then search for
"get_cmdflag", if that still doesn't work, well, it
doesn't REALLY matter very much where this line goes.
Add the global function declaration:
void show_element_to_char args(( CHAR_DATA *ch, OBJ_DATA *obj ));
41. Similarly, in the section for global function declarations
from fight.c, declare the global function
ch_ret specific_damage args((CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt, sh_int elem ));
42. Recompile, reboot the mud, and have fun!!!