/* * Generic use ranged attack function -Thoric & Tricops */ ch_ret ranged_attack( char_data * ch, string argument, obj_data * weapon, obj_data * projectile, short dt, short range ) { string arg, arg1, temp; if( !argument.empty( ) && argument[0] == '\'' ) { one_argument( argument, temp ); argument = temp; } argument = one_argument( argument, arg ); argument = one_argument( argument, arg1 ); if( arg.empty( ) ) { ch->print( "Where? At who?\r\n" ); return rNONE; } /* * get an exit or a victim */ short dir = -1; exit_data *pexit; char_data *victim = nullptr; if( !( pexit = find_door( ch, arg, true ) ) ) { if( !( victim = ch->get_char_room( arg ) ) ) { ch->print( "Aim in what direction?\r\n" ); return rNONE; } else { if( ch->who_fighting( ) == victim ) { ch->print( "They are too close to release that type of attack!\r\n" ); return rNONE; } } } else dir = pexit->vdir; /* * check for ranged attacks from private rooms, etc */ if( !victim ) { if( ch->in_room->flags.test( ROOM_PRIVATE ) || ch->in_room->flags.test( ROOM_SOLITARY ) ) { ch->print( "You cannot perform a ranged attack from a private room.\r\n" ); return rNONE; } if( ch->in_room->tunnel > 0 ) { if( ( int )ch->in_room->people.size( ) >= ch->in_room->tunnel ) { ch->print( "This room is too cramped to perform such an attack.\r\n" ); return rNONE; } } } skill_type *skill = nullptr; if( IS_VALID_SN( dt ) ) skill = skill_table[dt]; if( pexit && !pexit->to_room ) { ch->print( "Are you expecting to fire through a wall!?\r\n" ); return rNONE; } /* * Check for obstruction */ if( pexit && IS_EXIT_FLAG( pexit, EX_CLOSED ) ) { if( IS_EXIT_FLAG( pexit, EX_SECRET ) || IS_EXIT_FLAG( pexit, EX_DIG ) ) ch->print( "Are you expecting to fire through a wall!?\r\n" ); else ch->print( "Are you expecting to fire through a door!?\r\n" ); return rNONE; } /* * Keeps em from firing through a wall but can still fire through an arrow slit or window, Marcus */ if( pexit ) { if( ( IS_EXIT_FLAG( pexit, EX_FORTIFIED ) || IS_EXIT_FLAG( pexit, EX_HEAVY ) || IS_EXIT_FLAG( pexit, EX_MEDIUM ) || IS_EXIT_FLAG( pexit, EX_LIGHT ) || IS_EXIT_FLAG( pexit, EX_CRUMBLING ) ) && !IS_EXIT_FLAG( pexit, EX_WINDOW ) && !IS_EXIT_FLAG( pexit, EX_ASLIT ) ) { ch->print( "Are you expecting to fire through a wall!?\r\n" ); return rNONE; } } char_data *vch = nullptr; if( pexit && !arg1.empty( ) ) { if( !( vch = scan_for_vic( ch, pexit, arg1 ) ) ) { ch->print( "You cannot see your target.\r\n" ); return rNONE; } /* * don't allow attacks on mobs that are in a no-missile room --Shaddai */ if( vch->in_room->flags.test( ROOM_NOMISSILE ) ) { ch->print( "You can't get a clean shot off.\r\n" ); return rNONE; } /* * can't properly target someone heavily in battle */ if( vch->num_fighting > MAX_FIGHT ) { ch->print( "There is too much activity there for you to get a clear shot.\r\n" ); return rNONE; } } if( vch ) { if( !vch->CAN_PKILL( ) || !ch->CAN_PKILL( ) ) { ch->print( "You can't do that!\r\n" ); return rNONE; } if( vch && is_safe( ch, vch ) ) return rNONE; } room_index *was_in_room = ch->in_room; const char *stxt = "burst of energy"; if( projectile ) { projectile->separate( ); if( pexit ) { if( weapon ) { act( AT_GREY, "You fire $p $T.", ch, projectile, dir_name[dir], TO_CHAR ); act( AT_GREY, "$n fires $p $T.", ch, projectile, dir_name[dir], TO_ROOM ); } else { act( AT_GREY, "You throw $p $T.", ch, projectile, dir_name[dir], TO_CHAR ); act( AT_GREY, "$n throw $p $T.", ch, projectile, dir_name[dir], TO_ROOM ); } } else { if( weapon ) { act( AT_GREY, "You fire $p at $N.", ch, projectile, victim, TO_CHAR ); act( AT_GREY, "$n fires $p at $N.", ch, projectile, victim, TO_NOTVICT ); act( AT_GREY, "$n fires $p at you!", ch, projectile, victim, TO_VICT ); } else { act( AT_GREY, "You throw $p at $N.", ch, projectile, victim, TO_CHAR ); act( AT_GREY, "$n throws $p at $N.", ch, projectile, victim, TO_NOTVICT ); act( AT_GREY, "$n throws $p at you!", ch, projectile, victim, TO_VICT ); } } } else if( skill ) { if( skill->noun_damage && skill->noun_damage[0] != '\0' ) stxt = skill->noun_damage; else stxt = skill->name; /* * a plain "spell" flying around seems boring */ if( !str_cmp( stxt, "spell" ) ) stxt = "magical burst of energy"; if( skill->type == SKILL_SPELL ) { if( pexit ) { act( AT_MAGIC, "You release $t $T.", ch, aoran( stxt ), dir_name[dir], TO_CHAR ); act( AT_MAGIC, "$n releases $s $t $T.", ch, stxt, dir_name[dir], TO_ROOM ); } else { act( AT_MAGIC, "You release $t at $N.", ch, aoran( stxt ), victim, TO_CHAR ); act( AT_MAGIC, "$n releases $s $t at $N.", ch, stxt, victim, TO_NOTVICT ); act( AT_MAGIC, "$n releases $s $t at you!", ch, stxt, victim, TO_VICT ); } } } else { bug( "%s: no projectile, no skill dt %d", __func__, dt ); return rNONE; } /* * victim in same room */ if( victim ) { check_illegal_pk( ch, victim ); check_attacker( ch, victim ); return ranged_got_target( ch, victim, weapon, projectile, 0, dt, stxt, AT_MAGIC ); } /* * assign scanned victim */ victim = vch; /* * reverse direction text from move_char */ const char *dtxt = rev_exit( pexit->vdir ); int dist = 0; while( dist <= range ) { ch->from_room( ); if( !ch->to_room( pexit->to_room ) ) log_printf( "char_to_room: %s:%s, line %d.", __FILE__, __func__, __LINE__ ); if( IS_EXIT_FLAG( pexit, EX_CLOSED ) ) { /* * whadoyahknow, the door's closed */ if( projectile ) ch->printf( "&wYou see your %s pierce a door in the distance to the %s.", projectile->myobj( ).c_str( ), dir_name[dir] ); else ch->printf( "&wYou see your %s hit a door in the distance to the %s.", stxt, dir_name[dir] ); if( projectile ) act_printf( AT_GREY, ch, projectile, nullptr, TO_ROOM, "$p flies in from %s and implants itself solidly in the %sern door.", dtxt, dir_name[dir] ); else act_printf( AT_GREY, ch, nullptr, nullptr, TO_ROOM, "%s flies in from %s and implants itself solidly in the %sern door.", aoran( stxt ), dtxt, dir_name[dir] ); break; } /* * no victim? pick a random one */ if( !victim ) { list < char_data * >::iterator ich; for( ich = ch->in_room->people.begin( ); ich != ch->in_room->people.end( ); ++ich ) { vch = *ich; if( ( ( ch->isnpc( ) && !vch->isnpc( ) ) || ( !ch->isnpc( ) && vch->isnpc( ) ) ) && number_bits( 1 ) == 0 ) { victim = vch; break; } } if( victim && is_safe( ch, victim ) ) { ch->from_room( ); if( !ch->to_room( was_in_room ) ) log_printf( "char_to_room: %s:%s, line %d.", __FILE__, __func__, __LINE__ ); return rNONE; } } /* * In the same room as our victim? */ if( victim && ch->in_room == victim->in_room ) { if( projectile ) act( AT_GREY, "$p flies in from $T.", ch, projectile, dtxt, TO_ROOM ); else act( AT_GREY, "$t flies in from $T.", ch, aoran( stxt ), dtxt, TO_ROOM ); /* * get back before the action starts */ ch->from_room( ); if( !ch->to_room( was_in_room ) ) log_printf( "char_to_room: %s:%s, line %d.", __FILE__, __func__, __LINE__ ); check_illegal_pk( ch, victim ); check_attacker( ch, victim ); return ranged_got_target( ch, victim, weapon, projectile, dist, dt, stxt, AT_GREY ); } if( dist == range ) { if( projectile ) { act( AT_GREY, "Your $t falls harmlessly to the ground to the $T.", ch, projectile->myobj( ).c_str( ), dir_name[dir], TO_CHAR ); act( AT_GREY, "$p flies in from $T and falls harmlessly to the ground here.", ch, projectile, dtxt, TO_ROOM ); if( projectile->in_obj ) projectile->from_obj( ); if( projectile->carried_by ) projectile->from_char( ); projectile->to_room( ch->in_room, ch ); } else { act( AT_MAGIC, "Your $t fizzles out harmlessly to the $T.", ch, stxt, dir_name[dir], TO_CHAR ); act( AT_MAGIC, "$t flies in from $T and fizzles out harmlessly.", ch, aoran( stxt ), dtxt, TO_ROOM ); } break; } if( !( pexit = ch->in_room->get_exit( dir ) ) ) { if( projectile ) { act( AT_GREY, "Your $t hits a wall and bounces harmlessly to the ground to the $T.", ch, projectile->myobj( ).c_str( ), dir_name[dir], TO_CHAR ); act( AT_GREY, "$p strikes the $Tsern wall and falls harmlessly to the ground.", ch, projectile, dir_name[dir], TO_ROOM ); if( projectile->in_obj ) projectile->from_obj( ); if( projectile->carried_by ) projectile->from_char( ); projectile->to_room( ch->in_room, ch ); } else { act( AT_MAGIC, "Your $t harmlessly hits a wall to the $T.", ch, stxt, dir_name[dir], TO_CHAR ); act( AT_MAGIC, "$t strikes the $Tsern wall and falls harmlessly to the ground.", ch, aoran( stxt ), dir_name[dir], TO_ROOM ); } break; } if( projectile ) act( AT_GREY, "$p flies in from $T.", ch, projectile, dtxt, TO_ROOM ); else act( AT_MAGIC, "$t flies in from $T.", ch, aoran( stxt ), dtxt, TO_ROOM ); ++dist; } ch->from_room( ); if( !ch->to_room( was_in_room ) ) log_printf( "char_to_room: %s:%s, line %d.", __FILE__, __func__, __LINE__ ); if( projectile->carried_by == ch ) projectile->extract( ); return rNONE; }
bool load_class_file( const char *fname ) { char buf[MSL]; const char *word; bool fMatch; struct class_type *Class; int cl = -1, tlev = 0; FILE *fp; snprintf( buf, MSL, "%s%s", CLASSDIR, fname ); if ( ( fp = FileOpen( buf, "r" ) ) == NULL ) { perror( buf ); return FALSE; } CREATE( Class, struct class_type, 1 ); /* * Setup defaults for additions to class structure */ Class->starting = FALSE; Class->attr_second = 0; Class->attr_deficient = 0; xCLEAR_BITS( Class->affected ); Class->resist = 0; Class->suscept = 0; for ( ;; ) { word = feof( fp ) ? "End" : fread_word( fp ); fMatch = FALSE; switch ( UPPER( word[0] ) ) { case '*': fMatch = TRUE; fread_to_eol( fp ); break; case 'A': KEY( "Affected", Class->affected, fread_bitvector( fp ) ); KEY( "AttrPrime", Class->attr_prime, fread_number( fp ) ); KEY( "AttrSecond", Class->attr_second, fread_number( fp ) ); KEY( "AttrDeficient", Class->attr_deficient, fread_number( fp ) ); break; case 'C': KEY( "Class", cl, fread_number( fp ) ); KEY( "Craftbase", Class->craft_base, fread_number( fp ) ); break; case 'E': if ( !str_cmp( word, "End" ) ) if ( !str_cmp( word, "End" ) ) { FileClose( fp ); if ( cl < 0 || cl >= MAX_CLASS ) { bug( "Load_class_file: Class (%s) bad/not found (%d)", Class->who_name ? Class->who_name : "name not found", cl ); if ( VLD_STR( Class->who_name ) ) STRFREE( Class->who_name ); if ( VLD_STR( Class->filename ) ) STRFREE( Class->filename ); DISPOSE( Class ); return FALSE; } if ( !Class->filename ) Class->filename = STRALLOC( Class->who_name ); class_table[cl] = Class; return TRUE; } KEY( "ExpBase", Class->exp_base, fread_number( fp ) ); break; case 'F': KEY( "Filename", Class->filename, fread_string( fp ) ); case 'H': KEY( "HpMax", Class->hp_max, fread_number( fp ) ); KEY( "HpMin", Class->hp_min, fread_number( fp ) ); break; case 'M': KEY( "ManaMax", Class->mana_max, fread_number( fp ) ); KEY( "ManaMin", Class->mana_min, fread_number( fp ) ); break; case 'N': KEY( "Nocombo", Class->combo_restriction, fread_number( fp ) ); KEY( "Name", Class->who_name, fread_string( fp ) ); break; case 'R': KEY( "Races", Class->race_restriction, fread_number( fp ) ); if ( !str_cmp( word, "Reclass" ) ) { Class->reclass1 = fread_number( fp ); Class->reclass2 = fread_number( fp ); Class->reclass3 = fread_number( fp ); fMatch = TRUE; break; } KEY( "Resist", Class->resist, fread_number( fp ) ); break; case 'S': KEY( "Starting", Class->starting, fread_number( fp ) ); if ( !str_cmp( word, "Skill" ) ) { int sn, lev, adp; word = fread_word( fp ); lev = fread_number( fp ); adp = fread_number( fp ); sn = skill_lookup( word ); if ( cl < 0 || cl >= MAX_CLASS ) bug( "load_class_file: Skill %s -- class bad/not found (%d)", word, cl ); else if ( !IS_VALID_SN( sn ) ) bug( "load_class_file: Skill %s unknown", word ); else { skill_table[sn]->skill_level[cl] = lev; skill_table[sn]->skill_adept[cl] = adp; } fMatch = TRUE; break; } KEY( "Skilladept", Class->skill_adept, fread_number( fp ) ); KEY( "Suscept", Class->suscept, fread_number( fp ) ); break; case 'T': if ( !str_cmp( word, "Title" ) ) { if ( cl < 0 || cl >= MAX_CLASS ) { char *tmp; bug( "load_class_file: Title -- class bad/not found (%d)", cl ); tmp = fread_string( fp ); DISPOSE( tmp ); tmp = fread_string( fp ); DISPOSE( tmp ); } else if ( tlev < MAX_LEVEL + 1 ) { title_table[cl][tlev][0] = fread_string( fp ); title_table[cl][tlev][1] = fread_string( fp ); ++tlev; } else bug( "%s", "load_class_file: Too many titles" ); fMatch = TRUE; break; } KEY( "Thac0", Class->thac0_00, fread_number( fp ) ); KEY( "Thac32", Class->thac0_32, fread_number( fp ) ); break; } if ( !fMatch ) { bug( "load_class_file: no match: %s", word ); fread_to_eol( fp ); } } return FALSE; }
/* * Hit one guy with a projectile. * Handles use of missile weapons (wield = missile weapon) * or thrown items/weapons */ ch_ret projectile_hit( char_data * ch, char_data * victim, obj_data * wield, obj_data * projectile, short dist ) { int vic_ac, thac0, plusris, diceroll, prof_bonus, prof_gsn = -1, proj_bonus, dt, pchance; double dam = 0; ch_ret retcode; if( !projectile ) return rNONE; if( projectile->item_type == ITEM_PROJECTILE || projectile->item_type == ITEM_WEAPON ) { dt = TYPE_HIT + projectile->value[3]; if( wield ) proj_bonus = number_range( wield->value[1], wield->value[2] ); else proj_bonus = 0; } else { dt = TYPE_UNDEFINED; proj_bonus = 0; } /* * Can't beat a dead char! */ if( victim->position == POS_DEAD || victim->char_died( ) ) { projectile->extract( ); return rVICT_DIED; } if( wield ) prof_bonus = weapon_prof_bonus_check( ch, wield, prof_gsn ); else prof_bonus = 0; if( dt == TYPE_UNDEFINED ) { dt = TYPE_HIT; if( wield && wield->item_type == ITEM_MISSILE_WEAPON ) dt += wield->value[3]; } thac0 = calc_thac0( ch, victim, dist ); vic_ac = umax( -19, ( victim->GET_AC( ) / 10 ) ); /* * if you can't see what's coming... */ if( !victim->can_see_obj( projectile, false ) ) vic_ac += 1; if( !ch->can_see( victim, false ) ) vic_ac -= 4; /* * Weapon proficiency bonus */ vic_ac += prof_bonus; /* * The moment of excitement! */ while( ( diceroll = number_bits( 5 ) ) >= 20 ) ; if( diceroll == 0 || ( diceroll != 19 && diceroll < thac0 - vic_ac ) ) { /* * Miss. */ if( prof_gsn != -1 ) ch->learn_from_failure( prof_gsn ); /* * Do something with the projectile */ if( number_percent( ) < 50 ) projectile->extract( ); else { if( projectile->carried_by ) projectile->from_char( ); projectile->to_room( victim->in_room, victim ); } damage( ch, victim, 0, dt ); return rNONE; } /* * Hit. * Calc damage. */ pchance = number_range( 1, 10 ); switch ( pchance ) { case 1: case 2: case 3: /* Hit in the arm */ dam = number_range( projectile->value[1], projectile->value[2] ) + proj_bonus; break; case 4: case 5: case 6: /* Hit in the leg */ dam = number_range( ( 2 * projectile->value[1] ), ( 2 * projectile->value[2] ) ) + proj_bonus; break; default: case 7: case 8: case 9: case 10: /* Hit in the chest */ dam = number_range( ( 3 * projectile->value[1] ), ( 3 * projectile->value[2] ) ) + proj_bonus; break; } /* * Bonuses. */ dam += ch->GET_DAMROLL( ); if( prof_bonus ) dam += prof_bonus / 4; /* * Calculate Damage Modifiers from Victim's Fighting Style */ if( victim->position == POS_BERSERK ) dam = 1.2 * dam; else if( victim->position == POS_AGGRESSIVE ) dam = 1.1 * dam; else if( victim->position == POS_DEFENSIVE ) dam = .85 * dam; else if( victim->position == POS_EVASIVE ) dam = .8 * dam; if( !ch->isnpc( ) && ch->pcdata->learned[gsn_enhanced_damage] > 0 && number_percent( ) < ch->pcdata->learned[gsn_enhanced_damage] ) dam += ( int )( dam * ch->LEARNED( gsn_enhanced_damage ) / 120 ); else ch->learn_from_failure( gsn_enhanced_damage ); if( !victim->IS_AWAKE( ) ) dam *= 2; if( dam <= 0 ) dam = 1; plusris = 0; if( projectile->extra_flags.test( ITEM_MAGIC ) ) dam = ris_damage( victim, dam, RIS_MAGIC ); else dam = ris_damage( victim, dam, RIS_NONMAGIC ); /* * Handle PLUS1 - PLUS6 ris bits vs. weapon hitroll -Thoric */ if( wield ) plusris = wield->hitroll( ); /* * check for RIS_PLUSx - Thoric */ if( dam ) { int x, res, imm, sus, mod; if( plusris ) plusris = RIS_PLUS1 << UMIN( plusris, 7 ); /* * initialize values to handle a zero plusris */ imm = res = -1; sus = 1; /* * find high ris *//* * FIXME: Absorb handling needs to be included */ for( x = RIS_PLUS1; x <= RIS_PLUS6; ++x ) { if( victim->has_immune( x ) ) imm = x; if( victim->has_resist( x ) ) res = x; if( victim->has_suscep( x ) ) sus = x; } mod = 10; if( imm >= plusris ) mod -= 10; if( res >= plusris ) mod -= 2; if( sus <= plusris ) mod += 2; /* * check if immune */ if( mod <= 0 ) dam = -1; if( mod != 10 ) dam = ( dam * mod ) / 10; } /* * immune to damage */ if( dam == -1 ) { if( dt >= 0 && dt < num_skills ) { skill_type *skill = skill_table[dt]; bool found = false; if( skill->imm_char && skill->imm_char[0] != '\0' ) { act( AT_HIT, skill->imm_char, ch, nullptr, victim, TO_CHAR ); found = true; } if( skill->imm_vict && skill->imm_vict[0] != '\0' ) { act( AT_HITME, skill->imm_vict, ch, nullptr, victim, TO_VICT ); found = true; } if( skill->imm_room && skill->imm_room[0] != '\0' ) { act( AT_ACTION, skill->imm_room, ch, nullptr, victim, TO_NOTVICT ); found = true; } if( found ) { if( number_percent( ) < 50 ) projectile->extract( ); else { if( projectile->carried_by ) projectile->from_char( ); projectile->to_room( victim->in_room, victim ); } return rNONE; } } dam = 0; } if( ( retcode = damage( ch, victim, dam, dt ) ) != rNONE ) { if( projectile->value[5] == PROJ_STONE ) projectile->extract( ); else { if( victim->char_died( ) ) { projectile->extract( ); return rVICT_DIED; } projectile->from_char( ); projectile->to_char( victim ); projectile->extra_flags.set( ITEM_LODGED ); switch ( pchance ) { case 1: case 2: case 3: /* Hit in the arm */ projectile->wear_flags.set( ITEM_LODGE_ARM ); wear_obj( victim, projectile, true, get_wflag( "lodge_arm" ) ); break; case 4: case 5: case 6: /* Hit in the leg */ projectile->wear_flags.set( ITEM_LODGE_LEG ); wear_obj( victim, projectile, true, get_wflag( "lodge_leg" ) ); break; default: case 7: case 8: case 9: case 10: /* Hit in the chest */ projectile->wear_flags.set( ITEM_LODGE_RIB ); wear_obj( victim, projectile, true, get_wflag( "lodge_rib" ) ); break; } } return retcode; } if( ch->char_died( ) ) { projectile->extract( ); return rCHAR_DIED; } if( victim->char_died( ) ) { projectile->extract( ); return rVICT_DIED; } retcode = rNONE; if( dam == 0 ) { if( number_percent( ) < 50 ) projectile->extract( ); else { if( projectile->carried_by ) projectile->from_char( ); projectile->to_room( victim->in_room, victim ); } return retcode; } /* * weapon spells -Thoric */ if( wield && !victim->has_immune( RIS_MAGIC ) && !victim->in_room->flags.test( ROOM_NO_MAGIC ) ) { list < affect_data * >::iterator paf; for( paf = wield->pIndexData->affects.begin( ); paf != wield->pIndexData->affects.end( ); ++paf ) { affect_data *af = *paf; if( af->location == APPLY_WEAPONSPELL && IS_VALID_SN( af->modifier ) && skill_table[af->modifier]->spell_fun ) retcode = ( *skill_table[af->modifier]->spell_fun ) ( af->modifier, 7, ch, victim ); } if( retcode != rNONE || ch->char_died( ) || victim->char_died( ) ) { projectile->extract( ); return retcode; } for( paf = wield->affects.begin( ); paf != wield->affects.end( ); ++paf ) { affect_data *af = *paf; if( af->location == APPLY_WEAPONSPELL && IS_VALID_SN( af->modifier ) && skill_table[af->modifier]->spell_fun ) retcode = ( *skill_table[af->modifier]->spell_fun ) ( af->modifier, 7, ch, victim ); } if( retcode != rNONE || ch->char_died( ) || victim->char_died( ) ) { projectile->extract( ); return retcode; } } projectile->extract( ); return retcode; }