.-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-. | | | | | | | 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!!!