/// <summary> /// Checks whether the victim is able to dodge the attacker's swing. /// </summary> /// <param name="ch"></param> /// <param name="victim"></param> /// <returns></returns> public static bool CheckDodge( CharData ch, CharData victim ) { if( !victim.IsAwake() || victim.CurrentPosition < Position.reclining ) return false; if (ch.IsAffected(Affect.AFFECT_DAZZLE)) return false; if( !victim.HasSkill( "dodge" ) ) return false; int chance = victim.GetSkillChance("dodge"); // Size difference bonus for dodge for halflings - they get 2% dodge // bonus per size difference between them and the attacker. -- Xangis // Drow get a flat 15% bonus. if( victim.GetRace() == Race.RACE_HALFLING ) { if( ch.CurrentSize > victim.CurrentSize ) { chance += 3 * ( ch.CurrentSize - victim.CurrentSize ); } } else if( victim.HasInnate( Race.RACE_GOOD_DODGE ) ) { chance += 8; } else if( victim.HasInnate( Race.RACE_BAD_DODGE ) ) { chance -= 3; } // Bashed mobs/creatures have a hard time dodging if( victim.CurrentPosition < Position.fighting ) { chance -= 25; } // Leap is 16% max at level 50. Considering crappy thri hitpoints it's necessary. if( victim.GetRace() == Race.RACE_THRIKREEN && MUDMath.NumberPercent() <= ( victim.Level / 3 ) ) { SocketConnection.Act( "$N&n leaps over your attack.", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "You leap over $n&n's attack.", ch, null, victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "$N&n leaps over $n&n's attack.", ch, null, victim, SocketConnection.MessageTarget.room_vict ); return true; } victim.PracticeSkill( "dodge" ); if( MUDMath.NumberPercent() >= chance - ch.Level ) return false; switch( MUDMath.NumberRange( 1, 2 ) ) { case 1: SocketConnection.Act( "$N&n dodges your attack.", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "You dodge $n&n's attack.", ch, null, victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "$N&n dodges $n&n's attack.", ch, null, victim, SocketConnection.MessageTarget.room_vict ); break; case 2: SocketConnection.Act( "$N&n sidesteps your attack.", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "You narrowly dodge $n&n's attack.", ch, null, victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "$N&n avoids $n&n's attack.", ch, null, victim, SocketConnection.MessageTarget.room_vict ); break; default: break; } if( ch.Fighting == null ) SetFighting( ch, victim ); if( victim.Fighting == null ) SetFighting( victim, ch ); return true; }
/// <summary> /// Checks for block if holding shield. /// </summary> /// <param name="ch"></param> /// <param name="victim"></param> /// <returns></returns> static bool CheckShieldBlock( CharData ch, CharData victim ) { if( !victim.HasSkill( "shield block" ) ) return false; if( ch.IsAffected( Affect.AFFECT_DAZZLE ) ) return false; if( !victim.IsAwake() || victim.CurrentPosition < Position.reclining ) return false; Object obj = Object.GetEquipmentOnCharacter( victim, ObjTemplate.WearLocation.hand_one ); if( !obj || ( obj.ItemType != ObjTemplate.ObjectType.shield ) ) { if( !( obj = Object.GetEquipmentOnCharacter( victim, ObjTemplate.WearLocation.hand_two ) ) ) return false; if( obj.ItemType != ObjTemplate.ObjectType.shield ) return false; } if( obj.ItemType != ObjTemplate.ObjectType.shield ) { if( !( obj = Object.GetEquipmentOnCharacter( victim, ObjTemplate.WearLocation.hand_two ) ) ) return false; if( obj.ItemType != ObjTemplate.ObjectType.shield ) return false; } int chance = ch.GetSkillChance("shield block"); victim.PracticeSkill("shield block"); if (MUDMath.NumberPercent() >= ((chance - ch.Level) / 2)) { return false; } switch( MUDMath.NumberRange( 1, 5 ) ) { case 1: SocketConnection.Act( "You block $n&n's attack with your shield.", ch, null, victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "$N&n blocks your attack with a shield.", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "$N&n blocks $n&n's attack with a shield.", ch, null, victim, SocketConnection.MessageTarget.room_vict ); break; case 2: // If we were really smart we would check to see whether both the shield // and weapon were made of metal before we gave a sparks message... SocketConnection.Act( "&+CS&n&+cp&+Car&n&+ck&+Cs&n fly off your shield as you block $n&n's attack.", ch, null, victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "$N&n defends against your attack with a shield.", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "$N&n deflects $n&n's attack with a shield.", ch, null, victim, SocketConnection.MessageTarget.room_vict ); break; case 3: SocketConnection.Act("You bring up your shield to block $n&n's attack.", ch, null, victim, SocketConnection.MessageTarget.victim); SocketConnection.Act("$N&n brings up %s shield to block your attack.", ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("$N&n brings up %s shield to blocks $n&n's attack.", ch, null, victim, SocketConnection.MessageTarget.room_vict); break; case 4: SocketConnection.Act("You knock $n&n's attack aside with your shield.", ch, null, victim, SocketConnection.MessageTarget.victim); SocketConnection.Act("$N&n knocks your attack aside with $S shield.", ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("$N&n knocks $n&n's attack aside with $S shield.", ch, null, victim, SocketConnection.MessageTarget.room_vict); break; case 5: SocketConnection.Act("You hear a thud as $n&n's weapon smacks into your shield.", ch, null, victim, SocketConnection.MessageTarget.victim); SocketConnection.Act("Your weapon smacks into $N&n's shield with a thud.", ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n's weapon smacks into $N&'s shield with a thud.", ch, null, victim, SocketConnection.MessageTarget.room_vict); break; default: break; } if( ch.Fighting == null ) SetFighting( ch, victim ); if( victim.Fighting == null ) SetFighting( victim, ch ); return true; }
/// <summary> /// Checks whether the victim is able to parry a swing. /// </summary> /// <param name="ch"></param> /// <param name="victim"></param> /// <returns></returns> static bool CheckParry( CharData ch, CharData victim ) { if( !victim.IsAwake() || victim.CurrentPosition < Position.reclining ) return false; if (ch.IsAffected(Affect.AFFECT_DAZZLE)) return false; if( !victim.HasSkill( "parry" ) ) return false; int chance = ch.GetSkillChance("parry"); if (victim.IsNPC()) { // Mobs more often than not don't have weapons // so they should get bonuses for actually // having them if( !Object.GetEquipmentOnCharacter( victim, ObjTemplate.WearLocation.hand_one ) ) { if( !Object.GetEquipmentOnCharacter( victim, ObjTemplate.WearLocation.hand_two ) ) chance -= 5; } else chance += 5; } else { // No weapon means no parry. Only secondary weapon means 50% chance to parry. if( !Object.GetEquipmentOnCharacter( victim, ObjTemplate.WearLocation.hand_one ) ) { if( !Object.GetEquipmentOnCharacter( victim, ObjTemplate.WearLocation.hand_two ) ) return false; chance /= 2; } victim.PracticeSkill( "parry" ); } if( MUDMath.NumberPercent() >= ( chance - ch.Level ) / 2 ) return false; switch( MUDMath.NumberRange(1,3)) { case 1: SocketConnection.Act( "$N&n skillfully parries your attack.", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "You parry $n&n's fierce attack.", ch, null, victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "$N&n parries $n&n's attack.", ch, null, victim, SocketConnection.MessageTarget.room_vict ); break; case 2: SocketConnection.Act("$N&n knocks your blow aside with $S weapon.", ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("You knock $n&n's clumsy attack aside with your weapon.", ch, null, victim, SocketConnection.MessageTarget.victim); SocketConnection.Act("$N&n knocks $n&n's attack aside with $S weapon.", ch, null, victim, SocketConnection.MessageTarget.room_vict); break; case 3: SocketConnection.Act("$N&n deflects your attack with $S weapon.", ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("You deflect $n&n's attack with your weapon.", ch, null, victim, SocketConnection.MessageTarget.victim); SocketConnection.Act("$N&n deflects $n&n's attack aside with $S weapon.", ch, null, victim, SocketConnection.MessageTarget.room_vict); break; } if( ch.Fighting == null ) SetFighting( ch, victim ); if( victim.Fighting == null ) SetFighting( victim, ch ); return true; }
/// <summary> /// Checks whether the victim is able to riposte the attacker's swing. Returns false /// if failed, true if successful. /// </summary> /// <param name="ch"></param> /// <param name="victim"></param> /// <returns></returns> static bool CheckRiposte( CharData ch, CharData victim ) { if( !victim.IsAwake() || victim.CurrentPosition < Position.reclining ) return false; if( ch.IsAffected( Affect.AFFECT_DAZZLE ) ) return false; if( ch.IsAffected( Affect.AFFECT_BLIND ) ) return false; if( ch.IsAffected(Affect.AFFECT_CASTING ) ) return false; if( !victim.HasSkill( "riposte" ) ) return false; int chance = ch.GetSkillChance("riposte"); if (victim.IsNPC()) { // Mobs more often than not don't have weapons // so they should get bonuses for actually // having them if( Object.GetEquipmentOnCharacter( victim, ObjTemplate.WearLocation.hand_one ) ) { chance += 3; } } else { if( !Object.GetEquipmentOnCharacter( victim, ObjTemplate.WearLocation.hand_one ) ) { // Have to have a weapon to riposte. If only holding secondary weapon chances are lowered. if( !Object.GetEquipmentOnCharacter( victim, ObjTemplate.WearLocation.hand_two )) { return false; } chance /= 2; } victim.PracticeSkill( "riposte" ); } if( MUDMath.NumberPercent() >= (( chance - ch.Level ) / 3 ) ) return false; switch( MUDMath.NumberRange(1,3)) { case 1: SocketConnection.Act( "$N&n deflects your blow and strikes back at YOU!", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "You deflect $n&n's attack and strike back at $m.", ch, null, victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "$N&n deflects $n&n's attack and strikes back at $m.", ch, null, victim, SocketConnection.MessageTarget.room_vict ); break; case 2: SocketConnection.Act("$N&n knocks your swing aside and strikes back at YOU!", ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("You knock $n&n's attack aside and strikes back at $m.", ch, null, victim, SocketConnection.MessageTarget.victim); SocketConnection.Act("$N&n knocks $n&n's attack aside and strikes back at $m.", ch, null, victim, SocketConnection.MessageTarget.room_vict); break; case 3: SocketConnection.Act("$N&n blocks your strike and swings back at YOU!", ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("You block $n&n's strike aside and swing back at $m.", ch, null, victim, SocketConnection.MessageTarget.victim); SocketConnection.Act("$N&n block $n&n's strike and swings back at $m.", ch, null, victim, SocketConnection.MessageTarget.room_vict); break; } return true; }
/// <summary> /// Hit one guy once. /// /// Hitroll is now done on a 200-sided die rather than a 20-sided die /// This allows for more dynamic modifiers to hitroll. /// i.e. a couple extra points of strength and whatnot _may_ make the /// difference between a hit and a miss rather than incrementing something /// every 10-20 points of an ability we can modify it every 1-2 points. /// </summary> /// <param name="ch">attacker</param> /// <param name="victim">person being attacked</param> /// <param name="skill">damage type being used (skill)</param> /// <param name="weapon">wear location of weapon (usually primary or secondary hand)</param> /// <returns>true if victim is killed, otherwise false</returns> public static bool SingleAttack(CharData ch, CharData victim, string skill, ObjTemplate.WearLocation weapon) { string text; int dam; int chance; /* * Can't beat a dead char! * Guard against weird room-leavings. */ if( victim.CurrentPosition == Position.dead || victim.Hitpoints < -10 ) { text = String.Format("SingleAttack: ch {0} fighting dead victim {1}.", ch.Name, victim.Name ); Log.Error( text, 0 ); ch.Fighting = null; if( ch.CurrentPosition == Position.fighting ) ch.CurrentPosition = Position.standing; return true; } if( ch.InRoom != victim.InRoom ) { text = String.Format("SingleAttack: ch {0} not with victim {1}.", ch.Name, victim.Name ); Log.Error( text, 0 ); ch.Fighting = null; if( ch.CurrentPosition == Position.fighting ) ch.CurrentPosition = Position.standing; return false; } /* No casting/being para'd and hitting at the same time. */ if ((ch.IsAffected(Affect.AFFECT_CASTING)) || ch.IsAffected(Affect.AFFECT_MINOR_PARA) || ch.IsAffected(Affect.AFFECT_HOLD)) { return false; } // Inertial barrier will prevent some attacks. At the following levels a person // affected by inertial barrier will be able to avoid this percentage of attacks: // 1 = 7% 5 = 10% 10 = 13% 20 = 20% 30 = 27% 40 = 33% 50 = 39% 51 = 40% if (victim.IsAffected(Affect.AFFECT_INERTIAL_BARRIER) && MUDMath.NumberPercent() > (victim.Level * 2 / 3 + 7)) return false; // Keep in mind that CheckRiposte returns a boolean. if (skill != "kick" && skill != "backstab" && skill != "circle" && CheckRiposte( ch, victim ) ) { SingleAttack( victim, ch, String.Empty, ObjTemplate.WearLocation.hand_one ); return false; } if (CheckParry(ch, victim) && skill != "backstab" && skill != "circle") return false; if (CheckShieldBlock(ch, victim) && skill != "backstab" && skill != "circle") return false; if (CheckDodge(ch, victim) && skill != "backstab" && skill != "circle") return false; /* * Figure out the type of damage message if we don't have an associated attack skill. */ Object wield = Object.GetEquipmentOnCharacter( ch, weapon ); if (String.IsNullOrEmpty(skill)) { skill = "barehanded fighting"; if( wield && wield.ItemType == ObjTemplate.ObjectType.weapon ) skill = AttackType.Table[wield.Values[3]].SkillName; } /* * Weapon proficiencies. */ string weaponGsn = "barehanded fighting"; AttackType.DamageType damType = AttackType.DamageType.bludgeon; if( wield && wield.ItemType == ObjTemplate.ObjectType.weapon ) { if( wield.Values[ 3 ] >= 0 && wield.Values[ 3 ] < AttackType.Table.Length ) { weaponGsn = (AttackType.Table[wield.Values[3]].SkillName); damType = AttackType.Table[ wield.Values[ 3 ] ].DamageInflicted; } else { text = String.Format( "SingleAttack: bad weapon damage type {0} caused by {1} ({2}).", skill, wield.Name, wield.ObjIndexData ? wield.ObjIndexData.IndexNumber : -1 ); Log.Error( text, 0 ); wield.Values[ 3 ] = 0; } } /* * Calculate to-hit-armor-class-0 versus armor. */ int hitroll00 = ch.CharacterClass.HitrollLevel0; int hitroll40 = ch.CharacterClass.HitrollLevel40; /* Weapon-specific hitroll and damroll */ int hitroll = MUDMath.Interpolate( ch.Level, hitroll00, hitroll40 ) - ( ch.GetHitroll( weapon ) * 3 ); int victimAC = Math.Max( -100, victim.GetAC() ); // Added blindfighting skill - Xangis if( !CharData.CanSee( ch, victim ) ) { if( ch.CheckSkill( "blindfighting" ) ) { victimAC -= 5; } else { victimAC -= 40; } } /* Weapon proficiencies * * * The twohanded version of a weapon proficiency *MUST* follow the onehanded * version in the definitions. This is stupid. */ if( wield && wield.ItemType == ObjTemplate.ObjectType.weapon ) { if( !wield.HasFlag( ObjTemplate.ITEM_TWOHANDED ) ) { if( ch.CheckSkill( weaponGsn )) { victimAC += 20; ch.PracticeSkill( weaponGsn ); } } else { // This is not going to work. if (ch.CheckSkill(weaponGsn+1)) { victimAC += 20; ch.PracticeSkill(weaponGsn); } } } else if( ch.CheckSkill("barehanded fighting")) { victimAC += 20; } /* * The moment of excitement! */ int diceroll = MUDMath.NumberRange( 0, 199 ); // Give them a small bonus if they can make a successful luck check. if( MUDMath.NumberPercent() <= ch.GetCurrLuck() ) diceroll += 5; /* Made really lucky chars get saved by the godz. */ if( diceroll == 0 || ( diceroll <= 196 && diceroll < hitroll - victimAC ) || ( MUDMath.NumberPercent() < victim.GetCurrLuck() / 40 ) ) { /* Miss. */ return InflictDamage(ch, victim, 0, skill, weapon, damType); } /* * Hit. * Calc damage. * * NPCs are more badass barehanded than players. If they weren't * the game would be too damned easy since mobs almost never have * weapons. * * Increased mob damage by about 1/6 * It was previously level/2 to level*3/2 (25-75 at 50, average 50) * It is now level*3/5 to level*10/6 (30-87 at 50, average 59) * * Added the + ch.level - 1 'cause mobs still not hittin' hard enough */ if( ch.IsNPC() ) { dam = MUDMath.NumberRange( ( ch.Level * 3 / 5 ), ( ch.Level * 14 / 8 ) ) + ( ch.Level - 1 ); if (wield) { dam += MUDMath.Dice(wield.Values[1], wield.Values[2]); } else if (ch.CheckSkill("unarmed damage")) { dam += MUDMath.NumberRange(1, (ch.GetSkillChance("unarmed damage") / 12)); } } else { if (wield) { dam = MUDMath.Dice(wield.Values[1], wield.Values[2]); } else { if (!ch.IsClass(CharClass.Names.monk)) { dam = MUDMath.NumberRange(1, (2 + (int)ch.CurrentSize / 3)); if (ch.CheckSkill("unarmed damage")) { dam += MUDMath.NumberRange(1, (ch.GetSkillChance("unarmed damage") / 12)); } } else { int min; // monk barehanded damage - Xangis ch.PracticeSkill("unarmed damage"); chance = ch.GetSkillChance("unarmed damage"); if (chance < 13) { min = 1; } else { min = chance / 13; } // at max skill for barehanded and unarmed, a monk will get // a damage of 7-38, an average of 22.5 damage per hit before // modifiers. This is slightly better than a 6d6 weapon (average of 21 dmg) // this is slightly worse than a 6d7 weapon (average of 24 dmg) dam = MUDMath.NumberRange(min, ((chance / 3) + min)); } } if( ( wield && dam > 1000 ) && ch.Level < Limits.LEVEL_AVATAR ) { text = String.Format( "SingleAttack damage range > 1000 from {0} to {1}", wield.Values[ 1 ], wield.Values[ 2 ] ); Log.Error( text, 0 ); } } /* * Played a character with an armor class of 126 (awful agility). * Wasn't getting pounded much at all. Added a damage bonus applied * when the target's ac is worse than 100. * * This also means that someone who makes their weapon proficiency * check against someone with an ac of 81 or higher will also get a * damage bonus of 1% per ac point. * * This applies to mobs too, so if a mob has a terrible AC it will * get whacked harder. I call this the "soft as a pudding" code. * * This would also make AC debuffs stronger if they can make ac worse * than 100. */ if( victimAC > 100 ) { dam += ( (victimAC - 100) * dam) / 100; } /* * Bonuses. */ dam += ch.GetDamroll( weapon ); /* Weapon proficiencies, players only */ /* Up to 50% increase based on weapon skill */ if (wield && !ch.IsNPC()) { dam += dam * ch.GetSkillChance(weaponGsn) / 180; } /* Up to 33% for offense skill */ /* This means someone that has mastered a weapon and offense automatically does double damage in combat */ chance = ch.GetSkillChance("offense"); dam += dam * chance / 270; /* Bad idea to get caught napping in a fight */ if( !victim.IsAwake() ) dam *= 2; /* Backstab: 2 + one per 9 levels, 7x damage at 50 */ if (skill == "backstab") { // Cap was previously too low. It has been raised because a merc that was previously // stabbing for 180 now stabs for 64. Assassins will still be able to stab for // 175 and mercs for 116 with this revised cap. Keep in mind that a sorc can easily // fist for 250. int cap = 100 + 12 * ch.Level; if( ch.IsClass(CharClass.Names.mercenary) || ch.IsClass(CharClass.Names.bard )) cap = cap * 2 / 3; dam *= ( 2 + ( ch.Level / 9 ) ); /* damage cap applied here */ dam = Math.Min( dam, cap ); } else if (skill == "circle") /* 150% to 200% at lev. 50 */ dam += dam / 2 + ( dam * ch.Level ) / 100; if( dam <= 0 ) dam = 1; return InflictDamage(ch, victim, dam, skill, weapon, damType); }