public EditValues(ObjTemplate.ObjectType type, int[] values, String text, Area area) { _area = area; InitializeComponent(); _values = values; _type = type; lblObjectName.Text = text; RefreshWindowContents(); }
/// <summary> /// Copy constructor. /// </summary> /// <param name="obj"></param> public ObjTemplate(ObjTemplate obj) { this._affected = new List <Affect>(obj._affected); this._affectedBy = new int[obj._affectedBy.Length]; obj._affectedBy.CopyTo(_affectedBy, 0); this._antiFlags = new int[obj._antiFlags.Length]; obj._antiFlags.CopyTo(_antiFlags, 0); this._area = obj._area; this._condition = obj._condition; this._cost = obj._cost; this._count = 0; this._craftsmanshipLevel = obj._craftsmanshipLevel; this._customActions = new List <CustomAction>(obj._customActions); this._extraDescriptions = new List <ExtendedDescription>(obj._extraDescriptions); this._extraFlags = new int[obj._extraFlags.Length]; obj._extraFlags.CopyTo(_extraFlags, 0); this._fullDescription = obj._fullDescription; this._indexNumber = obj._area.HighObjIndexNumber + 1; this._itemType = obj._itemType; this._level = obj._level; this._material = obj._material; this._maxNumber = obj._maxNumber; this._name = obj._name; this._scarcity = obj._scarcity; this._shortDescription = obj._shortDescription; this._size = obj._size; this._specFun = new List <ObjSpecial>(obj._specFun); this._specFunName = obj._specFunName; this._spellEffects = new List <SpellEntry>(obj._spellEffects); this._trap = obj._trap; this._values = new int[obj._values.Length]; obj._values.CopyTo(_values, 0); this._volume = obj._volume; this._wearFlags = new int[obj._wearFlags.Length]; obj._wearFlags.CopyTo(_wearFlags, 0); this._weight = obj._weight; ++_numObjIndex; }
/// <summary> /// Retrieves a character's current damroll for a given weapon location. /// </summary> /// <param name="weapon"></param> /// <returns></returns> public int GetDamroll( ObjTemplate.WearLocation weapon ) { ObjTemplate.WearLocation otherWeapon; if( weapon == ObjTemplate.WearLocation.hand_one ) otherWeapon = ObjTemplate.WearLocation.hand_two; else if( weapon == ObjTemplate.WearLocation.hand_two ) otherWeapon = ObjTemplate.WearLocation.hand_one; else if( weapon == ObjTemplate.WearLocation.hand_three ) otherWeapon = ObjTemplate.WearLocation.hand_one; else if( weapon == ObjTemplate.WearLocation.hand_four ) otherWeapon = ObjTemplate.WearLocation.hand_one; else { string buf = "GetDamroll(): Invalid weapon location " + weapon + " on " + Name + "."; Log.Error( buf, 0 ); return 0; } int damr = Damroll + StrengthModifier.Table[ GetCurrStr() ].DamageModifier; if (CharacterClass.ClassNumber == CharClass.Names.monk || CharacterClass.ClassNumber == CharClass.Names.mystic) { int count; damr += MonkStance.GetMonkStance(((PC)this).Stance).DamrollModifier; for( count = 0; count < 5; ++count ) { if (Level >= MonkStance.GetMonkStance(((PC)this).Stance).DamPlus[count]) damr++; } } Object wield = Object.GetEquipmentOnCharacter( this, weapon ); // TODO: FIXME: BUG: Don't just cast the AttackType.DamageType. Fix the object format so this is valid info. if (CharacterClass.ClassNumber == CharClass.Names.antipaladin && wield && wield.ItemType == ObjTemplate.ObjectType.weapon && wield.HasFlag( ObjTemplate.ITEM_TWOHANDED ) && wield.Values[ 3 ] == (int)AttackType.DamageType.slash ) damr += Level / 9; // TODO: FIXME: BUG: Don't just cast the AttackType.DamageType. Fix the object format so this is valid info. if (CharacterClass.ClassNumber == CharClass.Names.paladin && wield && wield.ItemType == ObjTemplate.ObjectType.weapon && wield.HasFlag( ObjTemplate.ITEM_TWOHANDED ) && wield.Values[ 3 ] == (int)AttackType.DamageType.slash ) damr += Level / 9; Object otherWield = Object.GetEquipmentOnCharacter( this, otherWeapon ); if( !otherWield ) { return Math.Min( damr, Level ); } if( otherWield.ItemType != ObjTemplate.ObjectType.weapon ) { return Math.Min( damr, Level ); } foreach( Affect aff in otherWield.ObjIndexData.Affected ) { foreach (AffectApplyType apply in aff.Modifiers) { if (apply.Location == Affect.Apply.damroll) damr -= apply.Amount; } } foreach( Affect aff in otherWield.Affected) { foreach (AffectApplyType apply in aff.Modifiers) { if (apply.Location == Affect.Apply.damroll) damr -= apply.Amount; } } return Math.Min( damr, Level ); }
/// <summary> /// Equip a char with an object. /// </summary> /// <param name="obj">The object.</param> /// <param name="iWear">The equipment slot to place the object on.</param> public void EquipObject( ref Object obj, ObjTemplate.WearLocation iWear ) { int aff; if( obj == null ) return; if( Object.GetEquipmentOnCharacter( this, iWear ) ) { string buf = String.Format( "CharData.EquipObject(): {0} already equipped at slot {1}.", Name, iWear ); Log.Error( buf, 0 ); return; } if( ( obj.HasFlag( ObjTemplate.ITEM_ANTI_EVIL ) && IsEvil() ) || ( obj.HasFlag( ObjTemplate.ITEM_ANTI_GOOD ) && IsGood() ) || ( obj.HasFlag( ObjTemplate.ITEM_ANTI_NEUTRAL ) && IsNeutral() ) ) { SocketConnection.Act( "You are zapped by $p&n and drop it.", this, obj, null, SocketConnection.MessageTarget.character ); SocketConnection.Act( "$n&n is zapped by $p&n and drops it.", this, obj, null, SocketConnection.MessageTarget.room ); obj.RemoveFromChar(); obj.AddToRoom( InRoom ); return; } ArmorPoints -= Object.GetArmorClassModifer( obj, iWear ); obj.WearLocation = iWear; CarryNumber--; foreach (Affect affect in obj.ObjIndexData.Affected) { ApplyAffectModifiers(affect, true); } foreach (Affect affect in obj.Affected) { ApplyAffectModifiers(affect, true); } if (obj.ItemType == ObjTemplate.ObjectType.light && obj.Values[2] != 0 && InRoom) { ++InRoom.Light; } for( aff = 0; aff < Limits.NUM_AFFECT_VECTORS; aff++ ) AffectedBy[ aff ] = AffectedBy[ aff ] | obj.AffectedBy[ aff ]; if (!IsNPC() && Socket.Terminal == SocketConnection.TerminalType.TERMINAL_ENHANCED) { Command.Equipment(this, new string[] { "" } ); } return; }
public Target(ObjTemplate obj) { _objTemplate = obj; _type = TargetType.object_template; }
/// <summary> /// Copy constructor. /// </summary> /// <param name="obj"></param> public ObjTemplate(ObjTemplate obj) { this._affected = new List<Affect>(obj._affected); this._affectedBy = new int[obj._affectedBy.Length]; obj._affectedBy.CopyTo(_affectedBy, 0); this._antiFlags = new int[obj._antiFlags.Length]; obj._antiFlags.CopyTo(_antiFlags, 0); this._area = obj._area; this._condition = obj._condition; this._cost = obj._cost; this._count = 0; this._craftsmanshipLevel = obj._craftsmanshipLevel; this._customActions = new List<CustomAction>(obj._customActions); this._extraDescriptions = new List<ExtendedDescription>(obj._extraDescriptions); this._extraFlags = new int[obj._extraFlags.Length]; obj._extraFlags.CopyTo(_extraFlags, 0); this._fullDescription = obj._fullDescription; this._indexNumber = obj._area.HighObjIndexNumber + 1; this._itemType = obj._itemType; this._level = obj._level; this._material = obj._material; this._maxNumber = obj._maxNumber; this._name = obj._name; this._scarcity = obj._scarcity; this._shortDescription = obj._shortDescription; this._size = obj._size; this._specFun = new List<ObjSpecial>(obj._specFun); this._specFunName = obj._specFunName; this._spellEffects = new List<SpellEntry>(obj._spellEffects); this._trap = obj._trap; this._values = new int[obj._values.Length]; obj._values.CopyTo(_values, 0); this._volume = obj._volume; this._wearFlags = new int[obj._wearFlags.Length]; obj._wearFlags.CopyTo(_wearFlags, 0); this._weight = obj._weight; ++_numObjIndex; }
/// <summary> /// Sends a damage message to a player. /// </summary> /// <param name="ch"></param> /// <param name="victim"></param> /// <param name="damage"></param> /// <param name="skill"></param> /// <param name="weapon"></param> /// <param name="immune"></param> static void SendDamageMessage( CharData ch, CharData victim, int damage, string skill, ObjTemplate.WearLocation weapon, bool immune ) { if( victim.CurrentPosition == Position.sleeping ) { SocketConnection.Act( "$n&n has a rude awakening!", victim, null, null, SocketConnection.MessageTarget.room ); victim.CurrentPosition = Position.resting; SetFighting( victim, ch ); } if (skill == "bash") { SocketConnection.Act( "You send $N&n crashing to the &n&+yground&n with your powerful bash.", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "$n&n's powerful bash sends you sprawling!", ch, null, victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "$n&n sends $N&n sprawling with a powerful bash!", ch, null, victim, SocketConnection.MessageTarget.room_vict ); return; } if (skill == "headbutt") { if( ch != victim && damage > victim.Hitpoints + 10 ) { // a killing blow needs some nice verbage SocketConnection.Act( "You swiftly split $N&n's skull with your forehead, sending &+Rblood&n&+r and brains&n flying!", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "$n&n rears back and splits $N&n's skull with $s forehead!", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim ); SocketConnection.Act( "&+RBlood&n&+r and brains&n splatter everywhere as $N&n goes limp and collapses.", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim ); SocketConnection.Act( "$n&n's forehead smashes into you, crushing your skull and bringing on a wave of &+Lblackness&n.", ch, null, victim, SocketConnection.MessageTarget.victim ); //deal some decent damage } else if( ch != victim && damage > 100 ) { // a pretty nasty headbutt SocketConnection.Act( "Your headbutt smashes into $N&n's skull.", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "$n&n's headbutt smashes into $N&n's skull.", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim ); SocketConnection.Act( "$n&n's head smashes into you, sending you reeling.", ch, null, victim, SocketConnection.MessageTarget.victim ); //deal some decent damage } else if( ch != victim ) { SocketConnection.Act( "$n&n's headbutt leaves a red welt on $N&n's forehead.", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim ); SocketConnection.Act( "Your headbutt leaves a red welt on $N&n's forehead.", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "$n&n's headbutt leaves a red welt on your forehead.", ch, null, victim, SocketConnection.MessageTarget.victim ); } /* left the damage to self messages in the Command.Headbutt() function * because they are a little too tricky to implement through * this function */ return; } if (skill == "bodyslam" && ch != victim) { SocketConnection.Act( "You bodyslam $N&n!", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "$n&n bodyslams you!\r\nYou are stunned!", ch, null, victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "$n&n bodyslams $N&n.", ch, null, victim, SocketConnection.MessageTarget.room_vict ); } if (skill == "instant kill") { switch( MUDMath.NumberRange( 1, 3 ) ) { case 1: case 2: SocketConnection.Act( "You place your weapon in the back of $N&n, resulting in some strange noises, some blood, and a corpse!", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "You realize you should have kept your vital organs somewhere safe as $n&n stabs you to death.", ch, null, victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "$n&n places $s weapon in the back of $N&n, resulting in some strange noises, some blood, and a corpse!", ch, null, victim, SocketConnection.MessageTarget.room_vict ); break; case 3: SocketConnection.Act( "You place your weapon in the back of $N&n with such force that it comes out the other side!", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "$n&n ends your life with a well-placed backstab.", ch, null, victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "$n&n places $s weapon in the back of $N&n with such force that it comes out the other side!", ch, null, victim, SocketConnection.MessageTarget.room_vict ); break; } return; } // Multiple backstab messages, feel free to add more. if (skill == "backstab" && damage > 0) { switch( MUDMath.NumberRange( 1, 4 ) ) { case 1: SocketConnection.Act( "$N&n howls in agony as you pierce $S backbone!", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "You howl in agony as you feel &+rpain&n in your back!", ch, null, victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "$N&N howls in agony as $n&n pierces $S backbone!", ch, null, victim, SocketConnection.MessageTarget.room_vict ); break; case 2: SocketConnection.Act( "You place your $p&n silently and skillfully through the spine of $N&n.", ch, Object.GetEquipmentOnCharacter( ch, weapon ), victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "Your spine feels $p&n neatly slicing through it.", ch, Object.GetEquipmentOnCharacter( ch, weapon ), victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "$n&n places $s $p&n into $N&n's back!", ch, Object.GetEquipmentOnCharacter( ch, weapon ), victim, SocketConnection.MessageTarget.room_vict ); break; case 3: SocketConnection.Act( "Blood flies everywhere as you stab $N&n in the back!", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "You feel a sharp stabbing sensation in your back!", ch, null, victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "Blood flies everywhere as $n&n places $s $p&n into $N&n's back!", ch, Object.GetEquipmentOnCharacter( ch, weapon ), victim, SocketConnection.MessageTarget.room_vict ); break; case 4: SocketConnection.Act( "You smile with perverse pleasure as your $p&n plunges through $N&n's soft tissue, piercing vital organs.", ch, Object.GetEquipmentOnCharacter( ch, weapon ), victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "$n&n smiles with perverse pleasure as $s $p&n plunges through $N&n's soft tissue, piercing vital organs.", ch, Object.GetEquipmentOnCharacter( ch, weapon ), victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "$n&n smile with perverse pleasure as $s $p&n plunges through $N&n's soft tissue, piercing vital organs.", ch, Object.GetEquipmentOnCharacter( ch, weapon ), victim, SocketConnection.MessageTarget.room_vict ); break; } return; } if (skill == "poison weapon" || skill == "poison" || skill == "poison bite") return; string vp1; string attack; string buf1; string buf2; string buf3; string buf4; string buf5; // Adjectives based on amount of damage done. string adjective = String.Empty; if( damage > 100 ) { adjective = " godly"; } else if( damage > 75 ) { adjective = " devastating"; } else if( damage > 55 ) { adjective = " mighty"; } else if( damage > 40 ) { adjective = " awesome"; } else if( damage > 25 ) { adjective = " powerful"; } else if( damage > 4 ) { adjective = String.Empty; } // no message modifier for normal hits else if( damage > 2 ) { adjective = " mediocre"; } else if( damage > 0 ) { adjective = " feeble"; } string vp2 = String.Empty; if( damage == 0 ) { vp1 = "misses"; vp2 = String.Empty; } else { damage *= 100; if( victim.Hitpoints > 0 ) damage /= victim.Hitpoints; if( damage <= 1 ) { vp1 = "scratches"; } else if( damage <= 2 ) { vp1 = "grazes"; } else if( damage <= 3 ) { vp1 = "hits"; } else if( damage <= 4 ) { vp1 = "hits"; vp2 = " hard"; } else if( damage <= 5 ) { vp1 = "hits"; vp2 = " very hard"; } else if( damage <= 10 ) { vp1 = "mauls"; } else if( damage <= 15 ) { vp1 = "decimates"; } else if( damage <= 20 ) { vp1 = "makes"; vp2 = " stagger in pain"; } else if( damage <= 25 ) { vp1 = "maims"; } else if( damage <= 30 ) { vp1 = "mutilates"; } else if( damage <= 40 ) { vp1 = "disembowels"; } else if( damage <= 50 ) { vp1 = "eviscerates"; } else if( damage <= 75 ) { vp1 = "enshrouds"; vp2 = " in a mist of blood"; } else { vp1 = "beats the crap out of"; } } string punct = ( damage <= 40 ) ? "." : "!"; if (skill == "barehanded fighting") { if( ch.GetRace() >= Race.RaceList.Length ) { Log.Error( "SendDamageMessage: {0} invalid race", ch.GetRace() ); ch.SetPermRace( 0 ); } attack = Race.RaceList[ ch.GetRace() ].DamageMessage; buf1 = String.Format( "Your{0} {1} {2} $N&n{3}{4}", adjective, attack, vp1, vp2, punct ); buf2 = String.Format("$n&n's{0} {1} {2} you{3}{4}", adjective, attack, vp1, vp2, punct); buf3 = String.Format("$n&n's{0} {1} {2} $N&n{3}{4}", adjective, attack, vp1, vp2, punct); buf4 = String.Format("You{0} {1} {2} yourself{3}{4}", adjective, attack, vp1, vp2, punct); buf5 = String.Format("$n&n's{0} {1} {2} $m{3}{4}", adjective, attack, vp1, vp2, punct); } else { if (!String.IsNullOrEmpty(skill)) attack = Skill.SkillList[skill].DamageText; else { string buf = String.Format( "SendDamageMessage: bad damage type {0} for {1} damage caused by {2} to {3} with weapon {4}.", skill, damage, ch.Name, victim.Name, weapon ); Log.Error( buf, 0 ); skill = "barehanded fighting"; attack = AttackType.Table[ 0 ].Name; } if( immune ) { buf1 = String.Format("$N&n seems unaffected by your {0}!", attack); buf2 = String.Format("$n&n's {0} seems powerless against you.", attack); buf3 = String.Format("$N&n seems unaffected by $n&n's {0}!", attack); buf4 = String.Format("Luckily, you seem immune to {0}.", attack); buf5 = String.Format("$n&n seems unaffected by $s own {0}.", attack); } else { if (skill != "barehanded fighting" && IsWieldingPoisoned(ch, weapon)) { buf1 = String.Format("Your poisoned {0} {1} $N&n{2}{3}", attack, vp1, vp2, punct); buf2 = String.Format("$n&n's poisoned {0} {1} you{2}{3}", attack, vp1, vp2, punct); buf3 = String.Format("$n&n's poisoned {0} {1} $N&n{2}{3}", attack, vp1, vp2, punct); buf4 = String.Format("Your poisoned {0} {1} you{2}{3}", attack, vp1, vp2, punct); buf5 = String.Format("$n&n's poisoned {0} {1} $m{2}{3}", attack, vp1, vp2, punct); } else { buf1 = String.Format("Your{0} {1} {2} $N&n{3}{4}", adjective, attack, vp1, vp2, punct); buf2 = String.Format("$n&n's{0} {1} {2} you{3}{4}", adjective, attack, vp1, vp2, punct); buf3 = String.Format("$n&n's{0} {1} {2} $N&n{3}{4}", adjective, attack, vp1, vp2, punct); buf4 = String.Format("You{0} {1} {2} yourself{3}{4}", adjective, attack, vp1, vp2, punct); buf5 = String.Format("$n&n's{0} {1} {2} $m{3}{4}", adjective, attack, vp1, vp2, punct); } } } if( victim != ch ) { SocketConnection.Act( buf1, ch, null, victim, SocketConnection.MessageTarget.character, true ); SocketConnection.Act( buf2, ch, null, victim, SocketConnection.MessageTarget.victim, true ); SocketConnection.Act( buf3, ch, null, victim, SocketConnection.MessageTarget.room_vict, true ); } else { SocketConnection.Act( buf4, ch, null, victim, SocketConnection.MessageTarget.character, true ); SocketConnection.Act( buf5, ch, null, victim, SocketConnection.MessageTarget.room, true ); } return; }
/// <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); }
/// <summary> /// Remove an object from a wear location. /// </summary> /// <param name="iWear"></param> /// <param name="replaceExisting"></param> /// <returns></returns> public bool RemoveObject(ObjTemplate.WearLocation iWear, bool replaceExisting) { Object obj; if( ( obj = Object.GetEquipmentOnCharacter( this, iWear ) ) == null ) return true; if( !replaceExisting ) return false; if( obj.HasFlag( ObjTemplate.ITEM_NODROP ) ) { SocketConnection.Act( "Try as you might, you can't remove $p&n.", this, obj, null, SocketConnection.MessageTarget.character ); return false; } UnequipObject( obj ); SocketConnection.Act( "$n&n stops using $p&n.", this, obj, null, SocketConnection.MessageTarget.room ); SocketConnection.Act( "You remove $p&n.", this, obj, null, SocketConnection.MessageTarget.character ); return true; }
/// <summary> /// Places an object into a room. /// </summary> /// <param name="room"></param> /// <returns></returns> public bool AddToRoom( Room room ) { if( room == null ) { Log.Error( "Object.AddToRoom(): null room.", 0 ); return false; } if( _objIndexData == null ) { Log.Error( "Object.AddToRoom(): Object has null pIndexData.", 0 ); return false; } if( _objIndexData.IndexNumber == StaticObjects.OBJECT_NUMBER_MONEY_ONE || _objIndexData.IndexNumber == StaticObjects.OBJECT_NUMBER_MONEY_SOME ) { for( int i = (room.Contents.Count-1); i >= 0; i-- ) { Object obj2 = room.Contents[i]; switch( obj2._objIndexData.IndexNumber ) { case StaticObjects.OBJECT_NUMBER_MONEY_ONE: case StaticObjects.OBJECT_NUMBER_MONEY_SOME: _objIndexData = Database.GetObjTemplate( StaticObjects.OBJECT_NUMBER_MONEY_SOME ); _name = _objIndexData.Name; _shortDescription = _objIndexData.ShortDescription; _fullDescription = _objIndexData.FullDescription; _values[ 0 ] += obj2._values[ 0 ]; _values[ 1 ] += obj2._values[ 1 ]; _values[ 2 ] += obj2._values[ 2 ]; _values[ 3 ] += obj2._values[ 3 ]; obj2.RemoveFromWorld(); break; } } } room.Contents.Insert(0, this ); _inRoom = room; if( HasFlag( ObjTemplate.ITEM_LIT ) ) { _inRoom.Light++; } return true; }
private void UpdateWindowContents( ObjTemplate obj ) { txtCondition.Text = obj.Condition.ToString(); txtFullDescription.Text = obj.FullDescription; txtName.Text = obj.Name; txtShortDescription.Text = obj.ShortDescription; txtIndexNumber.Text = obj.IndexNumber.ToString(); txtWeight.Text = obj.Weight.ToString(); txtLevel.Text = obj.Level.ToString(); txtVolume.Text = obj.Volume.ToString(); cbCraftsmanship.SelectedIndex = (int)obj.CraftsmanshipLevel; int item = cbMaterial.Items.IndexOf(obj.Material.ToString()); cbMaterial.SelectedIndex = item; cbSize.SelectedIndex = (int)obj.Size; cbItemType.SelectedItem = obj.ItemType.ToString(); txtMaxInGame.Text = obj.MaxNumber.ToString(); txtExtraFlags.Text = obj.ExtraFlags[0].ToString(); txtExtraFlags2.Text = obj.ExtraFlags[1].ToString(); txtAffectFlags1.Text = obj.AffectedBy[0].ToString(); txtAffectFlags2.Text = obj.AffectedBy[1].ToString(); txtAffectFlags3.Text = obj.AffectedBy[2].ToString(); txtAffectFlags4.Text = obj.AffectedBy[3].ToString(); txtAffectFlags5.Text = obj.AffectedBy[4].ToString(); txtUseFlags.Text = obj.UseFlags[0].ToString(); txtUseFlags2.Text = obj.UseFlags[1].ToString(); txtWearFlags.Text = obj.WearFlags[0].ToString(); lstExtraDesc.Items.Clear(); foreach (ExtendedDescription desc in obj.ExtraDescriptions) { lstExtraDesc.Items.Add( desc ); } if (obj.SpecFun.Count > 0) { btnEditSpecials.ForeColor = System.Drawing.Color.Black; } else { btnEditSpecials.ForeColor = System.Drawing.Color.Gray; } }
private void btnNew_Click(object sender, EventArgs e) { ApplyWindowContents(); ObjTemplate obj = new ObjTemplate(); if( _area.HighObjIndexNumber >= 0 ) { obj.IndexNumber = _area.HighObjIndexNumber + 1; } _area.Objects.Add( obj ); _area.RebuildIndexes(); UpdateObjList(); SetControlAvailability(); objectList.SelectedIndex = objectList.Items.Count - 1; UpdateWindowContents(obj); _parent.UpdateStatusBar(); }
private void btnClone_Click(object sender, EventArgs e) { if (objectList.SelectedIndex == -1 || objectList.SelectedIndex > (objectList.Items.Count - 1)) { MessageBox.Show("Cannot clone an object without a valid object selected."); return; } ApplyWindowContents(); ObjTemplate obj = new ObjTemplate(_area.Objects[objectList.SelectedIndex]); if (_area.HighObjIndexNumber >= 0) { obj.IndexNumber = _area.HighObjIndexNumber + 1; } _area.Objects.Add(obj); _area.RebuildIndexes(); UpdateObjList(); SetControlAvailability(); objectList.SelectedIndex = objectList.Items.Count - 1; UpdateWindowContents(obj); _parent.UpdateStatusBar(); }
/// <summary> /// Parameterized constructor. /// </summary> /// <param name="name"></param> /// <param name="sfun"></param> public ObjSpecial(string name, ObjTemplate.ObjFun sfun) { _name = name; _function = sfun; }
/// <summary> /// Retrieves a character's current hitroll for a given weapon location. /// </summary> /// <param name="weapon"></param> /// <returns></returns> public int GetHitroll( ObjTemplate.WearLocation weapon ) { ObjTemplate.WearLocation otherWeapon; if( weapon == ObjTemplate.WearLocation.hand_one ) otherWeapon = ObjTemplate.WearLocation.hand_two; else if( weapon == ObjTemplate.WearLocation.hand_two ) otherWeapon = ObjTemplate.WearLocation.hand_one; else if( weapon == ObjTemplate.WearLocation.hand_three ) otherWeapon = ObjTemplate.WearLocation.hand_one; else if( weapon == ObjTemplate.WearLocation.hand_four ) otherWeapon = ObjTemplate.WearLocation.hand_one; else { string buf = "GetHitroll(): Invalid weapon location " + weapon + " on " + Name + "."; Log.Error( buf, 0 ); return 0; } int hitr = Hitroll + StrengthModifier.Table[ GetCurrStr() ].HitModifier; if (CharacterClass.ClassNumber == CharClass.Names.monk || CharacterClass.ClassNumber == CharClass.Names.mystic) { int count; hitr += MonkStance.GetMonkStance(( (PC)this ).Stance ).HitrollModifier; for( count = 0; count < 5; ++count ) { if (Level >= MonkStance.GetMonkStance(((PC)this).Stance).HitPlus[count]) hitr++; } } Object wield = Object.GetEquipmentOnCharacter( this, weapon ); Object otherWield = Object.GetEquipmentOnCharacter( this, otherWeapon ); if (CharacterClass.ClassNumber == CharClass.Names.antipaladin && wield && wield.ItemType == ObjTemplate.ObjectType.weapon && wield.HasFlag( ObjTemplate.ITEM_TWOHANDED ) ) hitr += Level / 9; if (CharacterClass.ClassNumber == CharClass.Names.paladin && wield && wield.ItemType == ObjTemplate.ObjectType.weapon && wield.HasFlag( ObjTemplate.ITEM_TWOHANDED ) ) hitr += Level / 9; if (CharacterClass.ClassNumber == CharClass.Names.ranger && wield && wield.ItemType == ObjTemplate.ObjectType.weapon && otherWield && otherWield.ItemType == ObjTemplate.ObjectType.weapon ) hitr += Level / 9; otherWield = Object.GetEquipmentOnCharacter( this, otherWeapon ); if( !otherWield ) { return Math.Min( hitr, Level ); } if( otherWield.ItemType != ObjTemplate.ObjectType.weapon ) { return Math.Min( hitr, Level ); } foreach( Affect aff in otherWield.ObjIndexData.Affected ) { foreach (AffectApplyType apply in aff.Modifiers) { if (apply.Location == Affect.Apply.hitroll) { hitr -= apply.Amount; } } } foreach( Affect aff in otherWield.Affected ) { foreach (AffectApplyType apply in aff.Modifiers) { if (apply.Location == Affect.Apply.hitroll) { hitr -= apply.Amount; } } } return Math.Min( hitr, Level ); }
/// <summary> /// Creates a level 1 object, flags it nosell, and gives it to the character. Used /// for newbie equipment. /// </summary> /// <param name="indexNumber"></param> /// <param name="wearLocation"></param> public void NewbieObjToChar( int indexNumber, ObjTemplate.WearLocation wearLocation ) { if( indexNumber == 0 ) { return; } ObjTemplate index = Database.GetObjTemplate( indexNumber ); if( index == null ) { return; } Object obj = Database.CreateObject( index, 1 ); if( obj != null ) { obj.AddFlag( ObjTemplate.ITEM_NOSELL ); obj.ObjToChar( this ); } if( wearLocation != ObjTemplate.WearLocation.none ) { EquipObject( ref obj, wearLocation ); } }
/// <summary> /// Counts the number of occurrences of an object inside an object list. /// </summary> /// <param name="objTemplate"></param> /// <param name="list"></param> /// <returns></returns> public static int CountObjectInList( ObjTemplate objTemplate, List<Object> list ) { int nMatch = 0; if( objTemplate == null ) { Log.Error("Object.CountObjectInList: called with null ObjIndex", 0); return 0; } if( list == null ) { Log.Error("Object.CountObjectInList: called with null Object list.", 0); return 0; } foreach( Object obj in list ) { if( obj._objIndexData == null ) { Log.Error("Object.CountObjectInList: Object has no pIndexData", 0); continue; } if( obj._objIndexData == objTemplate ) { nMatch++; } } return nMatch; }
/// <summary> /// Inflict damage from a single hit. This could use some cleanup since it's way too unwieldy at more than 600 lines. /// </summary> /// <param name="ch"></param> /// <param name="victim"></param> /// <param name="dam"></param> /// <param name="skill"></param> /// <param name="weapon"></param> /// <param name="damType"></param> /// <returns></returns> public static bool InflictDamage(CharData ch, CharData victim, int dam, string skill, ObjTemplate.WearLocation weapon, AttackType.DamageType damType) { if (ch == null || victim == null) { return false; } Object obj; bool critical = false; if( victim.CurrentPosition == Position.dead || victim.Hitpoints < -10 ) { return true; } /* * Stop up any residual loopholes. */ if( ( dam > 1276 ) && ch.Level < Limits.LEVEL_AVATAR ) { string text; if (ch.IsNPC() && ch.Socket) { text = String.Format("Damage: {0} from {1} by {2}: > 1276 points with {3} damage type!", dam, ch.Name, ch.Socket.Original.Name, skill); } else { text = String.Format("Damage: {0} from {1}: > 1276 points with {2} damage type!", dam, ch.IsNPC() ? ch.ShortDescription : ch.Name, skill); } Log.Error( text, 0 ); dam = 1276; } // Remove memorization and meditation bits - Xangis victim.BreakMeditate(); victim.BreakMemorization(); if (victim.IsAffected( Affect.AFFECT_MINOR_PARA)) { SocketConnection.Act( "$n&n disrupts the magic preventing $N&n from moving.", ch, null, victim, SocketConnection.MessageTarget.room_vict ); SocketConnection.Act( "You disrupt the magic preventing $N&n from moving.", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "&+YYou can move again.&n", ch, null, victim, SocketConnection.MessageTarget.victim ); victim.RemoveAffect( Affect.AFFECT_MINOR_PARA ); } bool immune = false; if( victim != ch ) { /* * Certain attacks are forbidden. * Most other attacks are returned. */ victim = CheckGuarding( ch, victim ); if( IsSafe( ch, victim ) ) return false; // is_safe could wipe out victim, as it calls procs if a boss // check and see that victim is still valid if( victim == null ) return true; Crime.CheckAttemptedMurder( ch, victim ); if( victim.CurrentPosition > Position.stunned ) { if( !victim.Fighting ) SetFighting( victim, ch ); // Can't have prone people automatically stand if( victim.CurrentPosition == Position.standing ) victim.CurrentPosition = Position.fighting; if( !ch.Fighting ) SetFighting( ch, victim ); /* * If NPC victim is following, ch might attack victim's master. * No charm check here because charm would be dispelled from * tanking mobile when combat ensues thus ensuring PC charmer is * not harmed. * Check for IsSameGroup wont work as following mobile is not * always grouped with PC charmer * * Added a check for whether ch has switch skill. If not, * much lower chancing of retargetting */ if( ch.IsNPC() && victim.IsNPC() && victim.Master && victim.Master.InRoom == ch.InRoom && MUDMath.NumberBits( 2 ) == 0 ) { StartGrudge( ch, victim.Master, false ); } } /* * More charm stuff. */ if( victim.Master == ch ) { StopFighting( victim, true ); } ch.BreakInvisibility(); /* * Hunting stuff... */ if( dam != 0 && victim.IsNPC() ) { /* StartGrudge is combined StartHating and StartHunting */ StartGrudge( victim, ch, false ); } /* * Damage modifiers. */ // Critical hits for double damage // Average of 5% for those that have average luck // Gnomes could concievably have 10% if( MUDMath.NumberPercent() < ( 2 + ( ch.GetCurrLuck() / 18 ) ) && dam > 0 ) { ch.SendText( "&+WYou score a CRITICAL HIT!&n\r\n" ); dam *= 2; critical = true; } if( victim.IsAffected( Affect.AFFECT_SANCTUARY ) ) dam /= 2; if( victim.IsAffected( Affect.AFFECT_PROTECT_EVIL ) && ch.IsEvil() ) dam -= dam / 8; else if( victim.IsAffected( Affect.AFFECT_PROTECT_GOOD ) && ch.IsGood() ) dam -= dam / 8; // Check stoneskin. People not affected by a stoneskin affect // cannot lose their stoneskin for any reason. This should mean // that mobs will keep their stoneskin and players should always // have a chance to lose it, since no player should ever be // setbit stoneskin. // // The bool value of found is used so that we can have them // take full damage when their stoneskin shatters, but get the // damage reduction if they are either a mob or their stoneskin // wears off that round. // /* Yeah, yeah.. so maybe backstabs shouldn't be aff'd. */ // Actually they should be affected, but they should have a much // higher chance of getting through (say 30-70%). // // Critical hits will now go through stoneskin // automatically if (!critical && victim.IsAffected( Affect.AFFECT_STONESKIN) && ( skill != "backstab" || MUDMath.NumberPercent() < ( 25 + ch.Level ) ) ) { bool found = false; for (int i = (victim.Affected.Count - 1); i >= 0; i--) { if( victim.Affected[i].HasBitvector( Affect.AFFECT_STONESKIN ) ) { // Small chance of shattering the stoneskin on a good hit. // Reduced chance by about 20% if( dam >= 25 && MUDMath.NumberPercent() <= ( dam / 12 ) ) { victim.SendText( "&+LYour stoneskin is shattered by the massive blow!&n\r\n" ); SocketConnection.Act( "$n&n's massive blow shatters $N&n's stoneskin!", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim ); SocketConnection.Act( "Your massive blow shatters $N&n's stoneskin!", ch, null, victim, SocketConnection.MessageTarget.character ); victim.RemoveAffect(victim.Affected[i]); found = true; } else if( dam > 0 ) // Added check for actual damage { for( int j = 0; j < victim.Affected[i].Modifiers.Count; j++ ) { victim.Affected[i].Modifiers[j].Amount--; if (victim.Affected[i].Modifiers[j].Amount < 1) { victim.RemoveAffect(victim.Affected[i]); victim.SendText("&+LYou feel your skin soften and return to normal.&n\r\n"); } dam /= 15; found = true; } } } } // This means they're Affect.AFFECT_STONESKIN as an innate/permenant. // We will still allow it to shatter, but it will refresh itself // upon a mob update. Because of this, we make it easier to shatter. // No damage reduction when it shatters. if( !found ) { if( dam >= 8 && MUDMath.NumberPercent() <= ( dam / 8 ) ) { victim.SendText( "&+LYour stoneskin is shattered by the massive blow!&n\r\n" ); SocketConnection.Act( "$n&n's massive blow shatters $N&n's stoneskin!", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim ); SocketConnection.Act( "Your massive blow shatters $N&n's stoneskin!", ch, null, victim, SocketConnection.MessageTarget.character ); victim.RemoveAffect( Affect.AFFECT_STONESKIN ); } else { dam = dam / 15 != 0 ? dam / 15 : 1; } } } if( dam < 0 ) dam = 0; /* * Check for disarm, trip, parry, dodge and shield block. */ if (skill != "barehanded fighting" || skill == "kick") { // Trip and disarm removed because those should be handled // by each individual mob's special function. if( ch.IsNPC() && ch.HasInnate( Race.RACE_WEAPON_WIELD ) && MUDMath.NumberPercent() < Math.Min( 25, Math.Max( 10, ch.Level ) ) && !victim.IsNPC() ) UseMagicalItem( ch ); } } switch( victim.CheckRIS( damType ) ) { case Race.ResistanceType.resistant: dam -= dam / 3; break; case Race.ResistanceType.immune: immune = true; dam = 0; break; case Race.ResistanceType.susceptible: dam += dam / 2; break; case Race.ResistanceType.vulnerable: dam *= 2; break; default: break; } if( ( damType == AttackType.DamageType.wind || damType == AttackType.DamageType.gas || damType == AttackType.DamageType.asphyxiation ) && victim.IsAffected(Affect.AFFECT_DENY_AIR)) { if( MUDMath.NumberPercent() < 50 ) { ch.SendText( "&+CYou deny the damage.&n\r\n" ); immune = true; dam = 0; } else dam -= dam / 5; } if (damType == AttackType.DamageType.fire && victim.IsAffected( Affect.AFFECT_DENY_FIRE)) { if( MUDMath.NumberPercent() < 50 ) { ch.SendText( "&+rYou deny the damage.&n\r\n" ); immune = true; dam = 0; } else dam -= dam / 5; } if( ( damType == AttackType.DamageType.earth || damType == AttackType.DamageType.crushing ) && victim.IsAffected( Affect.AFFECT_DENY_EARTH)) { if( MUDMath.NumberPercent() < 50 ) { ch.SendText( "&+yYou deny the damage.&n\r\n" ); immune = true; dam = 0; } else dam -= dam / 5; } if( ( damType == AttackType.DamageType.water || damType == AttackType.DamageType.acid || damType == AttackType.DamageType.drowning ) && victim.IsAffected( Affect.AFFECT_DENY_WATER)) { if( MUDMath.NumberPercent() < 50 ) { ch.SendText( "&+bYou deny the damage.&n\r\n" ); immune = true; dam = 0; } else dam -= dam / 5; } // Check for protection spells that give 25% damage reduction - Xangis if (damType == AttackType.DamageType.fire && victim.IsAffected( Affect.AFFECT_PROTECT_FIRE)) dam = ( dam * 3 ) / 4; else if (damType == AttackType.DamageType.cold && victim.IsAffected( Affect.AFFECT_PROTECT_COLD)) dam = ( dam * 3 ) / 4; else if (damType == AttackType.DamageType.acid && victim.IsAffected( Affect.AFFECT_PROTECT_ACID)) dam = ( dam * 3 ) / 4; else if (damType == AttackType.DamageType.gas && victim.IsAffected( Affect.AFFECT_PROTECT_GAS)) dam = ( dam * 3 ) / 4; else if (damType == AttackType.DamageType.electricity && victim.IsAffected( Affect.AFFECT_PROTECT_LIGHTNING)) dam = ( dam * 3 ) / 4; // Barkskin protects from 8% of slash and 12% of pierce damage. if (victim.IsAffected( Affect.AFFECT_BARKSKIN)) { if (skill == "1h slashing" || skill == "2h slashing") dam = dam * 11 / 12; else if (skill == "1h piercing" || skill == "2h piercing") dam = dam * 7 / 8; } // Check for vampiric touch for anti-paladins and vampires if( weapon == ObjTemplate.WearLocation.hand_one || weapon == ObjTemplate.WearLocation.hand_two || weapon == ObjTemplate.WearLocation.hand_three || weapon == ObjTemplate.WearLocation.hand_four ) { if( ( ( ch.IsClass(CharClass.Names.antipaladin) || ch.GetRace() == Race.RACE_VAMPIRE ) && skill == "barehanded fighting" && !Object.GetEquipmentOnCharacter(ch, weapon)) || (ch.IsAffected( Affect.AFFECT_VAMP_TOUCH) && ( !( obj = Object.GetEquipmentOnCharacter( ch, weapon ) ) || obj.HasAffect( Affect.AFFECT_VAMP_TOUCH ) ) ) ) { ch.Hitpoints += dam / 3; if( ch.Hitpoints > ( ch.GetMaxHit() + 50 + ch.Level * 5 ) ) { ch.Hitpoints = ch.GetMaxHit() + 50 + ch.Level * 5; } } } /* PC to PC damage quartered. * NPC to PC damage divided by 3. */ if( dam > 0 && !victim.IsNPC() && victim != ch ) { if( !ch.IsNPC() ) dam /= 4; else dam /= 3; } /* * Just a check for anything that is excessive damage * Send a log message, keeping the imms on their toes * Changed this from 300 to 250 'cause hitters get more than one * attack/round and w/haste that's 1000 possible in the time one fist * goes off. That's more than the fist might do and it has to be * memmed. */ if (dam > 250 && skill != "backstab" ) { string buf4; if (!string.IsNullOrEmpty(skill)) { buf4 = String.Format("Excessive damage: {0} attacking {1} for {2}, skill = {3}({4}).", ch.Name, victim.Name, dam, Skill.SkillList[skill].DamageText, skill); } else { buf4 = String.Format("Excessive damage: {0} attacking {1} for {2}, unknown damage type.", ch.Name, victim.Name, dam); } Log.Trace( buf4 ); } /* * We moved DamageMessage out of the victim != ch if above * so self damage would show. Other valid type_undefined * damage is ok to avoid like mortally wounded damage */ if (!String.IsNullOrEmpty(skill)) { SendDamageMessage(ch, victim, dam, skill, weapon, immune); } victim.Hitpoints -= dam; /* Check for HOLY_SACRFICE and BATTLE_ECSTASY */ if( dam > 0 && victim != ch ) { CharData groupChar; if (victim.IsAffected( Affect.AFFECT_HOLY_SACRIFICE) && victim.GroupLeader) { for( groupChar = victim.GroupLeader; groupChar; groupChar = groupChar.NextInGroup ) { if( groupChar == victim || groupChar.InRoom != ch.InRoom ) continue; groupChar.Hitpoints += dam / 5; if (groupChar.Hitpoints > groupChar.GetMaxHit() + 50 + groupChar.Level * 5) { groupChar.Hitpoints = groupChar.GetMaxHit() + 50 + groupChar.Level * 5; } } //end for loop } //end if holy sac if( ch.GroupLeader != null ) { for( groupChar = ch.GroupLeader; groupChar != null; groupChar = groupChar.NextInGroup ) { if( groupChar == victim || groupChar.InRoom != ch.InRoom ) continue; if( groupChar.IsAffected( Affect.AFFECT_BATTLE_ECSTASY ) ) { groupChar.Hitpoints += dam / 20; if( groupChar.Hitpoints > groupChar.GetMaxHit() + 50 + groupChar.Level * 5 ) groupChar.Hitpoints = groupChar.GetMaxHit() + 50 + groupChar.Level * 5; } // end if battle ecstasy } //end for loop } //end if grouped } //end if // Make sure if they got an instant kill roll that the victim dies. if (skill == "instant kill") { if( victim.GetRace() != Race.RACE_DEVIL && victim.GetRace() != Race.RACE_DEMON && victim.GetRace() != Race.RACE_GOD ) victim.Hitpoints = -20; } /* Added damage exp! */ // chance added because people level faster and faster as they get higher level... // to be worked out when exp is redone. // you can now only get damage exp on mobs that con easy or better // and there's only a 25% chance per hit of you evern being eligible for damage exp. if( MUDMath.NumberPercent() < 25 && victim.Level >= ( ch.Level - 3 ) ) ch.GainExperience( Math.Max( 1, dam / 20 ) ); if( !victim.IsNPC() && victim.Level >= Limits.LEVEL_AVATAR && victim.Hitpoints < 1 ) victim.Hitpoints = 1; /* * Magic shields that retaliate * * Apparently two people with the same sort of shield do not * take damage from each other */ if( ( dam > 1 ) && victim != ch ) { if( victim.IsAffected( Affect.AFFECT_FIRESHIELD ) && !ch.IsAffected( Affect.AFFECT_FIRESHIELD ) ) InflictSpellDamage( victim, ch, dam / 2, "fireshield", AttackType.DamageType.fire ); if (victim.IsAffected( Affect.AFFECT_COLDSHIELD) && !ch.IsAffected(Affect.AFFECT_COLDSHIELD)) InflictSpellDamage( victim, ch, dam / 2, "coldshield", AttackType.DamageType.cold ); if (victim.IsAffected(Affect.AFFECT_SHOCK_SHIELD) && !ch.IsAffected(Affect.AFFECT_SHOCK_SHIELD)) InflictSpellDamage( victim, ch, dam / 2, "shockshield", AttackType.DamageType.electricity ); /* Soulshield is a complex one. If the attacker and victim are of * opposite alignment, the shield retaliates with 1/2 damage just like * any other shield. If the victim is neutral and the attacker is * not, the shield retaliates with 1/4 damage. If the victim is good * or evil and the attacker is neutral, the shield retaliates with * 1/8 damage. If the attacker and victim are of same alignment, * the shield does nothing. */ if (victim.IsAffected(Affect.AFFECT_SOULSHIELD) && !ch.IsAffected(Affect.AFFECT_SOULSHIELD)) { if( victim.IsEvil() && ch.IsGood() ) InflictSpellDamage(victim, ch, dam / 2, "soulshield", AttackType.DamageType.harm); else if( victim.IsGood() && ch.IsEvil() ) InflictSpellDamage(victim, ch, dam / 2, "soulshield", AttackType.DamageType.harm); else if( victim.IsNeutral() && ( ch.IsEvil() || ch.IsGood() ) ) InflictSpellDamage(victim, ch, dam / 4, "soulshield", AttackType.DamageType.harm); else if( victim.IsGood() && ch.IsNeutral() ) InflictSpellDamage(victim, ch, dam / 8, "soulshield", AttackType.DamageType.harm); else if( victim.IsEvil() && ch.IsNeutral() ) InflictSpellDamage(victim, ch, dam / 8, "soulshield", AttackType.DamageType.harm); } } if (victim.IsAffected( Affect.AFFECT_BERZERK ) && victim.CurrentPosition <= Position.stunned ) victim.RemoveAffect(Affect.AFFECT_BERZERK); if (dam > 0 && skill != "barehanded fighting" && IsWieldingPoisoned( ch, weapon ) && !Magic.SpellSavingThrow( ch.Level, victim, AttackType.DamageType.poison ) ) { InflictPoison( "poison_weapon", ch.Level, IsWieldingPoisoned( ch, weapon ), ch, victim ); SocketConnection.Act( "$n&n suffers from the &+Gpoison&n inflicted upon $m.", victim, null, null, SocketConnection.MessageTarget.room, true ); Object.StripAffect( Object.GetEquipmentOnCharacter( ch, weapon ), Affect.AffectType.skill, "poison weapon" ); } victim.UpdatePosition(); switch( victim.CurrentPosition ) { case Position.mortally_wounded: victim.SendText( "&+LYou are &+Rmo&n&+rr&+Rt&n&+ral&+Rl&n&+ry&+L wounded, and will die soon, if not aided.&n\r\n" ); SocketConnection.Act( "$n&+L is &+Rmo&n&+rr&+Rt&n&+ral&+Rl&n&+ry&+L wounded, and will die soon, if not aided.&n", victim, null, null, SocketConnection.MessageTarget.room, true ); StopNotVicious( victim ); break; case Position.incapacitated: victim.SendText( "&+LYou are incapacitated and will &n&+rbl&+Re&n&+re&+Rd&+L to death, if not aided.&n\r\n" ); SocketConnection.Act( "$n&+L is incapacitated and will slowly &n&+rbl&+Re&n&+re&+Rd&+L to death, if not aided.&n", victim, null, null, SocketConnection.MessageTarget.room, true ); StopNotVicious( victim ); break; case Position.stunned: victim.SendText( "&+LYou are stunned, but will probably recover.&n\r\n" ); SocketConnection.Act( "$n&+L is stunned, but will probably recover.&n", victim, null, null, SocketConnection.MessageTarget.room, true ); break; case Position.dead: if( victim == ch ) { victim.SendText( "&+LYou have been &+Rsl&n&+ra&+Ri&n&+rn&+L!&n\r\n\r\n" ); } else { string buf = String.Format( "&+LYou have been &+Rsl&n&+ra&+Ri&n&+rn&+L by&n {0}&+L!&n\r\n\r\n", ch.ShowNameTo( victim, false ) ); victim.SendText( buf ); } /* Added this to stop a bug. */ Combat.StopFighting( victim, true ); SocketConnection.Act( "$n&+L is &n&+rdead&+L!&n", victim, null, null, SocketConnection.MessageTarget.room, true ); break; default: if( dam > victim.GetMaxHit() / 5 ) victim.SendText( "That really did &+RHURT&n!\r\n" ); if( victim.Hitpoints < victim.GetMaxHit() / 10 ) victim.SendText( "You sure are &n&+rBL&+RE&n&+rE&+RDI&n&+rN&+RG&n!\r\n" ); break; } // Check for weapon procs if( ( obj = Object.GetEquipmentOnCharacter( ch, weapon ) ) && Position.dead != victim.CurrentPosition ) { if( obj.SpecFun.Count > 0 ) obj.CheckSpecialFunction(true); } /* * Sleep spells and extremely wounded folks. */ if( !victim.IsAwake() ) /* lets make NPC's not slaughter PC's */ { if( victim.Fighting && victim.Fighting.Hunting && victim.Fighting.Hunting.Who == victim ) StopHunting( victim.Fighting ); if( victim.Fighting && !victim.IsNPC() && ch.IsNPC() ) StopFighting( victim, true ); else StopFighting( victim, false ); } /* * Payoff for killing things. */ if( victim.CurrentPosition == Position.dead ) { // Done in attempt to squelch the combat continuation bug StopFighting( victim, true ); if( !victim.HasActionBit(MobTemplate.ACT_NOEXP ) || !victim.IsNPC() ) GroupExperienceGain( ch, victim ); if( ch.IsNPC() ) { if( ch.Hunting ) { if( ch.Hunting.Who == victim ) StopHunting( ch ); } if( ch.IsHating(victim) ) { ch.StopHating( victim ); } } if( !victim.IsNPC() ) { if( ch.IsNPC() ) { ( (PC)victim ).MobDeaths++; if( victim.IsGuild() ) { ( (PC)victim ).GuildMembership.MonsterDeaths++; ( (PC)victim ).GuildMembership.Score += CalculateDeathScore( ch, victim ); } ( (PC)victim ).Score += CalculateDeathScore( ch, victim ); } else { ( (PC)ch ).PlayerKills++; ( (PC)victim ).PlayerDeaths++; ( (PC)victim ).Score += CalculateDeathScore( ch, victim ); ( (PC)ch ).Score += CalculateKillScore( ch, victim ); if( ch.IsGuild() && victim.IsGuild() && ( (PC)ch ).GuildMembership != ( (PC)victim ).GuildMembership ) { ( (PC)ch ).GuildMembership.PlayerKills++; ( (PC)victim ).GuildMembership.PlayerDeaths++; ( (PC)ch ).GuildMembership.Score += CalculateKillScore( ch, victim ); ( (PC)victim ).GuildMembership.Score += CalculateDeathScore( ch, victim ); } } string logBuf = String.Format( "{0}&n killed by {1}&n at {2}", victim.Name, ( ch.IsNPC() ? ch.ShortDescription : ch.Name ), victim.InRoom.IndexNumber ); Log.Trace( logBuf ); ImmortalChat.SendImmortalChat( ch, ImmortalChat.IMMTALK_DEATHS, Limits.LEVEL_AVATAR, logBuf ); /* * Dying penalty: * * At level 1 you lose 12.5% of a level. * At level 50 you lose 25% of a level. */ // Made it so people level 5 and under lose no exp from death. if( ch.Level > 5 ) victim.GainExperience( ( 0 - ( ( ( 50 + victim.Level ) * ExperienceTable.Table[ victim.Level ].LevelExperience ) / 400 ) ) ); if( victim.Level < 2 && victim.ExperiencePoints < 1 ) victim.ExperiencePoints = 1; } else { if( !ch.IsNPC() ) { ( (PC)ch ).MobKills++; if( ch.IsGuild() ) { ( (PC)ch ).GuildMembership.MonsterKills++; ( (PC)ch ).GuildMembership.Score += CalculateKillScore( ch, victim ); } ( (PC)ch ).Score += CalculateKillScore( ch, victim ); } } KillingBlow( ch, victim ); return true; } if( victim == ch ) { return false; } /* * Wimp out? */ if( victim.IsNPC() && dam > 0 ) { if( ( victim.HasActionBit(MobTemplate.ACT_WIMPY ) && MUDMath.NumberBits( 1 ) == 0 && victim.Hitpoints < victim.GetMaxHit() / 5 ) || (victim.IsAffected( Affect.AFFECT_CHARM) && victim.Master && victim.Master.InRoom != victim.InRoom ) ) { StartFearing( victim, ch ); StopHunting( victim ); CommandType.Interpret(victim, "flee"); } } if( !victim.IsNPC() && victim.Hitpoints > 0 && victim.Hitpoints <= victim.Wimpy ) { CommandType.Interpret(victim, "flee"); } return false; }
/// <summary> /// Find the armor class value of an object, including position effect. /// </summary> /// <param name="obj"></param> /// <param name="iWear"></param> /// <returns></returns> public static int GetArmorClassModifer( Object obj, ObjTemplate.WearLocation iWear ) { if( obj._itemType != ObjTemplate.ObjectType.armor ) return 0; if( obj._itemType == ObjTemplate.ObjectType.shield && ( ( iWear == ObjTemplate.WearLocation.hand_one ) || ( iWear == ObjTemplate.WearLocation.hand_two ) ) ) { return obj._values[ 0 ]; } switch( iWear ) { case ObjTemplate.WearLocation.body: return obj._values[ 0 ]; case ObjTemplate.WearLocation.head: return obj._values[ 0 ]; case ObjTemplate.WearLocation.legs: return obj._values[ 0 ]; case ObjTemplate.WearLocation.feet: return obj._values[ 0 ]; case ObjTemplate.WearLocation.hands: return obj._values[ 0 ]; case ObjTemplate.WearLocation.arms: return obj._values[ 0 ]; case ObjTemplate.WearLocation.finger_left: return obj._values[ 0 ]; case ObjTemplate.WearLocation.finger_right: return obj._values[ 0 ]; case ObjTemplate.WearLocation.neck_one: return obj._values[ 0 ]; case ObjTemplate.WearLocation.neck_two: return obj._values[ 0 ]; case ObjTemplate.WearLocation.about_body: return obj._values[ 0 ]; case ObjTemplate.WearLocation.waist: return obj._values[ 0 ]; case ObjTemplate.WearLocation.wrist_left: return obj._values[ 0 ]; case ObjTemplate.WearLocation.wrist_right: return obj._values[ 0 ]; case ObjTemplate.WearLocation.eyes: return obj._values[ 0 ]; case ObjTemplate.WearLocation.face: return obj._values[ 0 ]; case ObjTemplate.WearLocation.horns: return obj._values[ 0 ]; case ObjTemplate.WearLocation.tail: return obj._values[ 0 ]; case ObjTemplate.WearLocation.ear_left: return obj._values[ 0 ]; case ObjTemplate.WearLocation.ear_right: return obj._values[ 0 ]; case ObjTemplate.WearLocation.badge: return obj._values[ 0 ]; case ObjTemplate.WearLocation.quiver: return obj._values[ 0 ]; case ObjTemplate.WearLocation.on_back: return obj._values[ 0 ]; default: return 0; } }
/// <summary> /// Check to see if weapon is poisoned. /// </summary> /// <param name="ch"></param> /// <param name="weapon"></param> /// <returns></returns> static bool IsWieldingPoisoned(CharData ch, ObjTemplate.WearLocation weapon) { Object obj = Object.GetEquipmentOnCharacter( ch, weapon ); if( !obj ) return false; foreach (Affect aff in obj.Affected) { if( aff.Type == Affect.AffectType.skill && aff.Value == "poison weapon" ) return true; } return false; }
/// <summary> /// Find a piece of eq on a character. /// </summary> /// <param name="ch"></param> /// <param name="iWear"></param> /// <returns></returns> public static Object GetEquipmentOnCharacter( CharData ch, ObjTemplate.WearLocation iWear ) { if( ch == null ) return null; foreach( Object obj in ch.Carrying ) { if( obj._wearLocation == iWear ) return obj; } return null; }
/// <summary> /// Create an instance of an object. /// </summary> /// <param name="objTempalte"></param> /// <param name="level"></param> /// <returns></returns> public static Object CreateObject( ObjTemplate objTempalte, int level ) { if( level < 1 ) { level = 1; } if( !objTempalte ) { Log.Error("CreateObject: null ObjTemplate.", 0); return null; } Object obj = new Object( objTempalte ); if( !obj ) { Log.Error("Database.CreateObject: new Object(ObjIndex*) failed.", 0); return null; } obj.Level = level; return obj; }
/// <summary> /// Find some object with a given index data. Used by area-reset 'P' command. /// </summary> /// <param name="objTemplate"></param> /// <returns></returns> public static Object GetFirstObjectOfTemplateType( ObjTemplate objTemplate ) { foreach( Object obj in Database.ObjectList ) { if (obj._objIndexData == objTemplate) { return obj; } } return null; }
/// <summary> /// Parameterized constructor. /// </summary> /// <param name="loc"></param> /// <param name="bit"></param> public WearInfo(ObjTemplate.WearLocation loc, Bitvector bit) { WornLocation = loc; WearBit = bit; }
/// <summary> /// Creates an object and initializes it to the parent object values. /// </summary> /// <param name="indexData"></param> public Object( ObjTemplate indexData ) { if( indexData == null ) { Log.Error( "Object.Object(ObjIndex *) called with null ObjIndex.", 0 ); return; } ++_numObjects; _objIndexNumber = indexData.IndexNumber; _objIndexData = indexData; _wearLocation = ObjTemplate.WearLocation.none; _flyLevel = 0; _level = 1; _timer = -1; _createdBy = null; _extraDescription = indexData.ExtraDescriptions; _name = indexData.Name; _shortDescription = indexData.ShortDescription; _fullDescription = indexData.FullDescription; _specFun = indexData.SpecFun; _itemType = indexData.ItemType; int count; for( count = 0; count < Limits.NUM_ITEM_EXTRA_VECTORS; ++count ) _extraFlags[ count ] = indexData.ExtraFlags[ count ]; for( count = 0; count < Limits.NUM_AFFECT_VECTORS; ++count ) { _affectedBy[ count ] = indexData.AffectedBy[ count ]; } _wearFlags = indexData.WearFlags; _antiFlags = indexData.UseFlags; _material = indexData.Material; _size = indexData.Size; _volume = indexData.Volume; _craftsmanship = indexData.CraftsmanshipLevel; for( count = 0; count < 8; ++count ) _values[ count ] = indexData.Values[ count ]; _weight = indexData.Weight; _cost = indexData.Cost; _condition = indexData.Condition; _trap = indexData.Trap; // Create vehicle data for vehicles that are created. The // bulk of the data is stored in the object - Xangis if( _itemType == ObjTemplate.ObjectType.vehicle || _itemType == ObjTemplate.ObjectType.ship ) { Vehicle vehicle = new Vehicle(); if( _itemType == ObjTemplate.ObjectType.ship ) vehicle.Type = Vehicle.VehicleType.ship_any_water; else vehicle.Type = Vehicle.VehicleType.flat_land; // need to create virtual rooms for the rest of the data vehicle.HullPoints = _values[ 5 ]; vehicle.FlyLevel = 0; vehicle.Direction = 0; vehicle.Speed = 0; vehicle.Occupants = 0; vehicle.MovementTimer = 0; vehicle.MovementDelay = 0; vehicle.MovementPointer = 0; vehicle.MovementScript = String.Empty; vehicle.ParentObject = this; vehicle.EntryRoomTemplateNumber = _values[ 1 ]; vehicle.ControlPanelRoomTemplateNumber = _values[ 2 ]; } ++indexData.QuantityLoaded; --indexData.Scarcity; Database.ObjectList.Add( this ); return; }