/// <summary> /// Shows a character's attribute screen. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Attributes(CharData ch, string[] str) { if( ch == null ) return; string buf1 = String.Empty; if (ch.IsNPC()) { ch.SendText("&nYour attributes are as would be expected for an NPC.\r\n"); return; } if (ch.IsImmortal() && str.Length != 0) { CharData worldChar = ch.GetCharWorld(str[0]); if (!worldChar) { ch.SendText("No such person.\r\n"); return; } if (worldChar.IsNPC()) { ch.SendText("NPCs don't have skills!\r\n"); return; } } string buf = String.Format( "&+WName: &+G{0}&n &+WLevel: {1}&n\r\n", MUDString.PadStr(ch.Name, 17), ch.Level); buf1 += buf; buf = String.Format( "&+WRace:&n {0} &+WClass:&n {1} &n&+WSex:&n {2}\r\n", MUDString.PadStr(Race.RaceList[ch.GetRace()].ColorName, 16), MUDString.PadStr(ch.CharacterClass.WholistName, 16), System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(ch.GetSexString())); buf1 += buf; // Break a player's size into strings when we get around to it // -- Xangis if (!ch.IsNPC()) { buf = String.Format( "&+WHeight:&n {0} inches &+WWeight:&n {1} pounds &+WSize:&n {2}\r\n", MUDString.PadInt(((PC)ch).Height, 3), MUDString.PadInt(((PC)ch).Weight, 5), Race.SizeString(ch.CurrentSize)); } else { buf = String.Format("&+WSize:&n {0}\r\n", ch.CurrentSize); } buf1 += buf; TimeSpan time = TimeSpan.FromTicks(ch.TimePlayed.Ticks) + (DateTime.Now - ch.LogonTime); int days = (int)time.TotalHours / 24; time = (time - TimeSpan.FromDays(days)); int hours = (int)time.TotalHours; time = (time - TimeSpan.FromHours(hours)); int minutes = (int)time.TotalMinutes; // Age is a hack until we get it coded - Xangis buf = String.Format( "\r\n&+BAge:&n {0} years. &+BPlaying Time:&n {1} days {2} hours {3} minutes.\r\n", MUDString.PadInt(ch.GetAge(), 3), days, hours, minutes); buf1 += buf; // Need to create a function to display character status strings buf = String.Format("&+BStatus:&n {0}", System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(Position.PositionString(ch.CurrentPosition))); if (!ch.IsNPC() && ch.IsAffected(Affect.AFFECT_BERZERK)) { buf += ", &+Rberzerk&n"; } if (!ch.IsNPC() && ch.HasActionBit(PC.PLAYER_MEMORIZING)) { buf += ", Memorizing"; } if (ch.IsAffected(Affect.AFFECT_CASTING)) { buf += ", Casting"; } if (ch.IsAffected(Affect.AFFECT_SINGING)) { buf += ", Singing"; } if (!ch.IsNPC() && ch.HasActionBit(PC.PLAYER_MEDITATING)) { buf += ", Meditating"; } if (!ch.IsNPC() && ch.HasActionBit(PC.PLAYER_CAMPING)) { /* This is ugly and should be moved to its own function */ buf += ", Camping"; } buf += ".\r\n\r\n"; buf1 += buf; // We want players to see the same stats for levels 1-50. // Should create string converters so that we have no decimals displayed // below this point. buf = String.Format(" &+cSTR:&n {0} &+cArmor Class:&n {1}.\r\n", MUDString.PadStr(StringConversion.AbilityScoreString(ch.GetCurrStr()), 15), ch.GetAC()); buf1 += buf; buf = String.Format(" &+cAGI:&n {0} &+cHitroll:&n {1}\r\n", MUDString.PadStr(StringConversion.AbilityScoreString(ch.GetCurrAgi()), 15), StringConversion.BonusString(ch.GetHitroll(ObjTemplate.WearLocation.hand_one))); buf1 += buf; buf = String.Format(" &+cDEX:&n {0} &+cDamroll:&n {1}\r\n", MUDString.PadStr(StringConversion.AbilityScoreString(ch.GetCurrDex()), 15), StringConversion.BonusString(ch.GetDamroll(ObjTemplate.WearLocation.hand_one))); buf1 += buf; buf = String.Format(" &+cCON:&n {0} &+cAlignment:&n {1}\r\n", MUDString.PadStr(StringConversion.AbilityScoreString(ch.GetCurrCon()), 15), StringConversion.AlignmentString(ch)); buf1 += buf; buf = String.Format(" &n&+cINT:&n {0}\r\n", MUDString.PadStr(StringConversion.AbilityScoreString(ch.GetCurrInt()), 15)); buf1 += buf; buf = String.Format(" &+cWIS:&n {0} &+BSaving Throws&n\r\n", MUDString.PadStr(StringConversion.AbilityScoreString(ch.GetCurrWis()), 15)); buf1 += buf; buf = String.Format(" &+cPOW:&n {0} &+cParalysis:&n {1}\r\n", MUDString.PadStr(StringConversion.AbilityScoreString(ch.GetCurrPow()), 15), StringConversion.BonusString(-ch.SavingThrows[0])); buf1 += buf; buf = String.Format(" &+cCHA:&n {0} &+cRod:&n {1}\r\n", MUDString.PadStr(StringConversion.AbilityScoreString(ch.GetCurrCha()), 15), StringConversion.BonusString(-ch.SavingThrows[1])); buf1 += buf; buf = String.Format(" &+cLUK:&n {0} &+cPetrify:&n {1}\r\n", MUDString.PadStr(StringConversion.AbilityScoreString(ch.GetCurrLuck()), 15), StringConversion.BonusString(-ch.SavingThrows[2])); buf1 += buf; buf = String.Format(" &+cBreath:&n {0}\r\n", StringConversion.BonusString(-ch.SavingThrows[3])); buf1 += buf; buf = String.Format("&+BWimpy: &n{0} &+cSpell:&n {1}\r\n", MUDString.PadInt(ch.Wimpy, 4), StringConversion.BonusString(-ch.SavingThrows[4])); buf1 += buf; buf = String.Format("&+BLoad Carried: &n{0} pounds ({1})\r\n", MUDString.PadInt(ch.CarryWeight, 3), StringConversion.WeightString(ch)); buf1 += buf; ch.SendText(buf1); return; }
/// <summary> /// Bash. Usable to initiate combat and during combat. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Bash(CharData ch, string[] str) { if( ch == null ) return; int chance; /* Check player's level and class, mobs can use this skill */ if ((!ch.HasSkill("bash"))) { ch.SendText("You'd better leave that to those with more skills.\r\n"); return; } if (ch.IsBlind() && !ch.Fighting) { return; } /* Verify a target. */ CharData victim = ch.Fighting; if (str.Length != 0) { victim = ch.GetCharRoom(str[0]); if (!victim || victim.CurrentPosition == Position.dead) { ch.SendText("They aren't anywhere to be found.\r\n"); return; } } else { if (!victim || victim.CurrentPosition == Position.dead) { ch.SendText("You aren't fighting anyone.\r\n"); return; } } /* Bash self? Ok! */ // Toned down the damage cuz you can't really bash yourself // like you could with someone else. if (victim == ch) { ch.SendText("You throw yourself to the ground!\r\n"); SocketConnection.Act("$N&n knocks $mself to the ground.", ch, null, victim, SocketConnection.MessageTarget.room_vict); ch.CurrentPosition = Position.kneeling; ch.WaitState((Skill.SkillList["bash"].Delay * 8) / 10); Combat.InflictDamage(ch, ch, MUDMath.NumberRange(1, 3), "bash", ObjTemplate.WearLocation.none, AttackType.DamageType.bludgeon); return; } /* Check size of ch vs. victim. */ /* If ch is too small. */ if (ch.CurrentSize < victim.CurrentSize) { SocketConnection.Act("$N&n is too big for you to bash!", ch, null, victim, SocketConnection.MessageTarget.character); return; } /* Ch 2 or more sizes larger than victim => bad! */ if (ch.CurrentSize - 2 > victim.CurrentSize) { SocketConnection.Act("You nearly topple over as you try to bash $N&n.", ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n nearly topples over as $e attempts to bash you.", ch, null, victim, SocketConnection.MessageTarget.victim); SocketConnection.Act("$n&n nearly topples over as $e attempts to bash $N&n.", ch, null, victim, SocketConnection.MessageTarget.room_vict); ch.WaitState((Skill.SkillList["bash"].Delay)); ch.CurrentPosition = Position.kneeling; if (victim.Fighting == null) { Combat.SetFighting(victim, ch); } return; } /* Lag to basher from bash. Pets get more lag because pets are cheesy */ if (!ch.IsNPC()) { ch.WaitState(MUDMath.FuzzyNumber(Skill.SkillList["bash"].Delay)); } else { ch.WaitState((Skill.SkillList["bash"].Delay * 6 / 5)); } /* Base chance to bash, followed by chance modifications. */ if (ch.IsNPC()) { chance = (ch.Level * 3) / 2 + 15; } else { chance = ((PC)ch).SkillAptitude["bash"] - 5; } if (victim.CurrentPosition < Position.fighting) { chance /= 5; //used to be 0 } else { chance += ch.GetCurrAgi() - victim.GetCurrAgi(); } if (chance > 95) { chance = 95; } Object obj = Object.GetEquipmentOnCharacter(ch, ObjTemplate.WearLocation.hand_one); if (!obj) { /* No primary item. */ if (!(obj = Object.GetEquipmentOnCharacter(ch, ObjTemplate.WearLocation.hand_two))) { /* No items in hand. */ if (!ch.IsClass(CharClass.Names.paladin) && !ch.IsClass(CharClass.Names.antipaladin)) { if (!ch.IsClass(CharClass.Names.warrior)) { chance -= 25; } else { chance -= 20; } ch.SendText("You lower your shoulder and attempt to bash without a shield...\r\n"); } else { chance -= 3; // Hidden penalty for not having a shield } } else if (obj.ItemType != ObjTemplate.ObjectType.shield) { /* Secondary item isn't a shield, no primary. */ if (!ch.IsClass(CharClass.Names.paladin) && !ch.IsClass(CharClass.Names.antipaladin)) { if (!ch.IsClass(CharClass.Names.warrior)) { chance -= 25; } else { chance -= 20; } ch.SendText("Bashing without a shield is tough, but you try anyway...\r\n"); } else { chance -= 5; // Small hidden penalty for not having a shield } } /* Secondary item is a shield, no primary. */ else if (ch.IsClass(CharClass.Names.paladin) || ch.IsClass(CharClass.Names.antipaladin)) { chance += 3; // Small hidden bonus for having a shield } } else if (obj.ItemType != ObjTemplate.ObjectType.shield) { /* Primary item isn't a shield. */ if (!(obj = Object.GetEquipmentOnCharacter(ch, ObjTemplate.WearLocation.hand_two))) { /* No secondary. */ if (!ch.IsClass(CharClass.Names.paladin) && !ch.IsClass(CharClass.Names.antipaladin)) { if (!ch.IsClass(CharClass.Names.warrior)) { chance -= 25; } else { chance -= 20; } ch.SendText("Without a shield, bashing is a wistful thought, but you try anyway...\r\n"); } else { chance -= 5; // Hidden penalty for not having a shield } } else if (obj.ItemType != ObjTemplate.ObjectType.shield) { /* Secondary item is not a shield. */ if (!ch.IsClass(CharClass.Names.paladin) && !ch.IsClass(CharClass.Names.antipaladin)) { if (!ch.IsClass(CharClass.Names.warrior)) { chance -= 25; } else { chance -= 20; } ch.SendText("Without a shield, your shoulder bash is but wishful thinking...\r\n"); } else { chance -= 5; // Hidden penalty for not having a shield } } else if (ch.IsClass(CharClass.Names.paladin) || ch.IsClass(CharClass.Names.antipaladin)) { /* Secondary is a shield. */ chance += 3; // Small hidden bonus for having a shield } else if (ch.IsClass(CharClass.Names.ranger)) { chance -= 8; } else if (ch.IsClass(CharClass.Names.warrior)) { chance -= 5; } } // Centaurs are awful damned hard to bash -- Xangis if (victim.GetRace() == Race.RACE_CENTAUR) { chance -= 25; } // damned high penalty for bashing blind if (ch.IsAffected(Affect.AFFECT_BLIND) && !victim.IsAffected(Affect.AFFECT_BLIND)) { chance /= 10; } if (ch.IsAffected(Affect.AFFECT_BLIND) && victim.IsAffected(Affect.AFFECT_BLIND)) { chance /= 4; } ch.PracticeSkill("bash"); /* Start a fight if not already in one. */ if (ch != victim) { if (!ch.Fighting) { Combat.SetFighting(ch, victim); } if (!victim.Fighting) { Combat.SetFighting(victim, ch); } } string lbuf = "Bash: " + ch.Name + " bashing " + victim.Name + " with " + chance + " chance."; ImmortalChat.SendImmortalChat(null, ImmortalChat.IMMTALK_SPAM, 0, lbuf); /* Do the bash, deal the damage. */ if (MUDMath.NumberPercent() < chance) { /* Hit the bash. */ if (victim.IsAffected(Affect.AFFECT_SINGING)) { victim.RemoveAffect(Affect.AFFECT_SINGING); SocketConnection.Act("$n&n chokes on a note and falls silent as $e slams into the ground!", victim, null, null, SocketConnection.MessageTarget.room); victim.SendText("You abort your singing!\r\n"); } if (victim.IsAffected(Affect.AFFECT_CASTING)) { victim.RemoveAffect(Affect.AFFECT_CASTING); SocketConnection.Act("$n&n's eyes roll back in $s head and $e forgets all about $s spell.", victim, null, null, SocketConnection.MessageTarget.room); victim.SendText("Being knocked over so forcefully makes it hard to cast.\r\n"); } if (!Combat.CheckTumble(victim)) { victim.WaitState(((Skill.SkillList["bash"].Delay * 5) / 6)); if (victim.CurrentPosition > Position.kneeling) { victim.CurrentPosition = Position.kneeling; } victim.SendText("You are knocked to the ground!\r\n"); Combat.InflictDamage(ch, victim, MUDMath.NumberRange(1, ch.Level), "bash", ObjTemplate.WearLocation.none, AttackType.DamageType.bludgeon); } else { Combat.InflictDamage(ch, victim, MUDMath.NumberRange(1, (ch.Level / 3)), "bash", ObjTemplate.WearLocation.none, AttackType.DamageType.bludgeon); victim.SendText("You roll with the blow, finally landing on your feet.\r\n"); SocketConnection.Act("$n&n rolls with the blow, finally landing on $s feet.", victim, null, null, SocketConnection.MessageTarget.room); } } else { /* Miss the bash. */ SocketConnection.Act("As $N&n avoids your bash, you topple to the &n&+yground&n with a loud crash.", ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n crashes to the &n&+yground&n as you sidestep $s bash.", ch, null, victim, SocketConnection.MessageTarget.victim); SocketConnection.Act("$n&n misses $s bash at $N&n and is driven to the &n&+yground&n.", ch, null, victim, SocketConnection.MessageTarget.room_vict); ch.CurrentPosition = Position.kneeling; } return; }
/// <summary> /// Command to climb something. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Climb(CharData ch, string[] str) { if( ch == null ) return; int chance; if (!ch.HasSkill("climb")) { ch.SendText("You lack the skills to climb anything.\r\n"); return; } if (str.Length == 0) { ch.SendText("Climb what?\r\n"); return; } Object obj = ch.GetObjHere(str[0]); if (!obj) { ch.SendText("Uhh... what exactly did you want to climb!?\r\n"); return; } if (obj.ItemType != ObjTemplate.ObjectType.wall) { ch.SendText("That wasn't exactly designed for climbing.\r\n"); return; } if (ch.IsNPC()) { chance = ch.Level * 3 / 2 + 20; } else { chance = ((PC)ch).SkillAptitude["climb"]; } // Agility helps. chance += ch.GetCurrAgi() / 10; switch (obj.ObjIndexData.IndexNumber) { case StaticObjects.OBJECT_NUMBER_WALL_STONE: chance += 5; break; case StaticObjects.OBJECT_NUMBER_WALL_IRON: chance -= 15; break; default: ch.SendText("That wasn't exactly designed for climbing.\r\n"); return; } // Maximum chance of 98% if (chance > 98) { chance = 98; } if (MUDMath.NumberPercent() >= chance) { ch.SendText("You try to climb it, but you fall on your ass!\r\n"); ch.CurrentPosition = Position.sitting; ch.WaitState(5); return; } ch.SendText("With great skill, you scale the wall!\r\n"); // Value 0 of a wall object is the direction that has been walled... // This means that they should move in that direction. We leave it up to // move_char to make sure that there is actually an exit in that direction. // we use the climbing bit to allow them to pass the walls in move_char. ch.SetAffectBit(Affect.AFFECT_CLIMBING); ch.Move((Exit.Direction)obj.Values[0]); ch.RemoveAffect(Affect.AFFECT_CLIMBING); return; }
/// <summary> /// Ingest a liquid. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Drink(CharData ch, string[] str) { if( ch == null ) return; Object obj = null; if (ch.IsBlind()) { return; } if (ch.Fighting || ch.CurrentPosition == Position.fighting) { ch.SendText("You can't drink while you're fighting!\r\n"); return; } if (str.Length == 0 && ch.InRoom != null) { foreach (Object iobj in ch.InRoom.Contents) { if (iobj.ItemType == ObjTemplate.ObjectType.drink_container) { obj = iobj; break; } } if (!obj) { ch.SendText("Drink what?\r\n"); return; } } else { if (!(obj = ch.GetObjHere(str[0]))) { ch.SendText("You can't find it.\r\n"); return; } } // Allow bards to get twice as drunk as other classes - Xangis if (!ch.IsNPC() && !ch.IsImmortal() && ((PC)ch).Drunk > 15 && ch.IsClass(CharClass.Names.bard) && MUDMath.NumberPercent() < ch.GetCurrAgi() - ((PC)ch).Drunk) { ch.SendText("You fail to reach your mouth. *Hic*\r\n"); return; } if (!ch.IsNPC() && !ch.IsImmortal() && ((PC)ch).Drunk > 25 && ch.IsClass(CharClass.Names.bard) && MUDMath.NumberPercent() < ch.GetCurrAgi() - ((PC)ch).Drunk) { ch.SendText("You fail to reach your mouth. *Hic*\r\n"); return; } switch (obj.ItemType) { default: ch.SendText("You can't drink from that.\r\n"); break; case ObjTemplate.ObjectType.drink_container: // -1 Means a container never goes empty. if (obj.Values[1] <= 0 && obj.Values[1] != -1) { ch.SendText("It is already &+Lempty&n.\r\n"); return; } /* No drinking if you're full */ if ((!ch.IsImmortal()) && ( (!ch.IsNPC() && ((PC)ch).Thirst > 40) || (!ch.IsNPC() && ((PC)ch).Hunger > 50))) { ch.SendText("You couldn't possibly drink any more.\r\n"); return; } int liquid; if ((liquid = obj.Values[2]) >= Liquid.Table.Length) { Log.Error("Drink: bad liquid number {0}.", liquid); liquid = obj.Values[2] = 0; } SocketConnection.Act("You drink $T from $p&n.", ch, obj, Liquid.Table[liquid].Name, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n drinks $T from $p&n.", ch, obj, Liquid.Table[liquid].Name, SocketConnection.MessageTarget.room); int amount = MUDMath.NumberRange(3, 10); if (obj.Values[0] != -1) { amount = Math.Min(amount, obj.Values[1]); } ch.AdjustDrunk(amount * Liquid.Table[liquid].DrunkValue); if (!ch.IsUndead()) { ch.AdjustHunger(amount * Liquid.Table[liquid].HungerValue); if (ch.IsAffected(Affect.AFFECT_THIRST)) { ch.AdjustThirst((amount * Liquid.Table[liquid].ThirstValue) / 12); ch.SendText("That doesn't taste as &+bwet&n as it used to.\r\n"); } else { ch.AdjustThirst(amount * Liquid.Table[liquid].ThirstValue); } } else { /* If blood */ if (Liquid.Table[liquid].Name == "blood") { ch.AdjustHunger(amount * 2); ch.AdjustThirst(amount); } } if (!ch.IsNPC() && ((PC)ch).Drunk > 10) { ch.SendText("You feel &n&+gdrunk&n.\r\n"); } if (!ch.IsNPC() && ((PC)ch).Hunger > 20) { ch.SendText("You are &n&+yfull&n.\r\n"); } if (!ch.IsNPC() && ((PC)ch).Thirst > 20) { ch.SendText("You do not feel &n&+cth&+Ci&n&+cr&+Cst&n&+cy&n.\r\n"); } if (obj.Values[3] != 0 && !CharData.CheckImmune(ch, Race.DamageType.poison)) { /* The shit was poisoned ! */ Affect af = new Affect(); ch.SendText("You choke and gag.\r\n"); SocketConnection.Act("$n chokes and gags.", ch, null, null, SocketConnection.MessageTarget.room); af.Type = Affect.AffectType.spell; af.Value = "poison"; af.Duration = 3 * amount; af.AddModifier(Affect.Apply.strength, -(obj.Level / 7 + 1)); af.SetBitvector(Affect.AFFECT_POISON); ch.CombineAffect(af); } /* HOLY_WATER and UNHOLY_WATER effects */ if ((ch.IsGood() && obj.Values[2] == 27) || (ch.IsEvil() && obj.Values[2] == 28)) { int heal = MUDMath.Dice(1, 8); if (ch.Hitpoints < ch.GetMaxHit()) { ch.Hitpoints = Math.Min(ch.Hitpoints + heal, ch.GetMaxHit()); ch.UpdatePosition(); ch.SendText("You feel a little better!\r\n"); } } if ((ch.IsEvil() && obj.Values[2] == 27) || (ch.IsGood() && obj.Values[2] == 28)) { int harm = MUDMath.Dice(1, 10); ch.Hitpoints = Math.Max(ch.Hitpoints - harm, -10); ch.SendText("You choke and feel as if you'd swallowed boiling oil!\r\n"); ch.UpdatePosition(); } /* End (UN)HOLY_WATER effects */ // -1 Means a container never goes empty. if (obj.Values[1] != -1) { obj.Values[1] -= amount; if (obj.Values[1] <= 0) { ch.SendText("The container is now &+Lempty&n.\r\n"); obj.Values[1] = 0; } } break; } return; }
/// <summary> /// Bodyslam an opponent. Can only be used to initiate combat. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Bodyslam(CharData ch, string[] str) { if( ch == null ) return; CharData victim; /* Check player's level and class, mobs can use this skill */ if (!ch.HasInnate(Race.RACE_BODYSLAM)) { ch.SendText("You don't feel massive enough to manhandle that.\r\n"); return; } if (ch.IsBlind()) { return; } if (ch.Riding) { ch.SendText("You can't do that while mounted.\r\n"); return; } if (str.Length != 0) { if (!(victim = ch.GetCharRoom(str[0])) || victim.CurrentPosition == Position.dead) { ch.SendText("They aren't here.\r\n"); return; } } else { ch.SendText("Bodyslam who?\r\n"); return; } // Added size restrictions -- Xangis if (victim.CurrentSize > ch.CurrentSize) { if (ch.HasInnate(Race.RACE_SLAM_LARGER)) { // allowing centaurs to slam one size up if it's an ogre if (victim.CurrentSize > (ch.CurrentSize + 1)) { ch.SendText("You can't bodyslam something that much bigger than you.\r\n"); return; } } else { ch.SendText("You can't bodyslam something bigger than you.\r\n"); return; } } if ((ch.CurrentSize > victim.CurrentSize) && ((ch.CurrentSize - victim.CurrentSize) > 3)) { ch.SendText("They're too small to slam.\r\n"); return; } /* Bodyslam self? Ok! */ if (ch == victim) { ch.SendText("You slam yourself to the ground!\r\n"); SocketConnection.Act("$n&n throws $mself to the ground in a fit of clumsiness.", ch, null, victim, SocketConnection.MessageTarget.room_vict); ch.WaitState((Skill.SkillList["bodyslam"].Delay / 2)); ch.CurrentPosition = Position.reclining; Combat.InflictDamage(ch, ch, MUDMath.NumberRange(1, 6), "bodyslam", ObjTemplate.WearLocation.none, AttackType.DamageType.bludgeon); return; } ch.WaitState(Skill.SkillList["bodyslam"].Delay); ch.PracticeSkill("bodyslam"); int chance = (ch.Level * 3) / 2 + 15; chance += ch.GetCurrAgi() - victim.GetCurrAgi(); chance -= (victim.Level - ch.Level); switch (victim.CurrentPosition) { case Position.dead: return; case Position.mortally_wounded: chance += 15; break; case Position.incapacitated: chance += 10; break; case Position.unconscious: chance += 5; break; case Position.stunned: chance += 3; break; case Position.sleeping: chance += 2; break; case Position.reclining: chance -= 45; break; case Position.resting: chance -= 30; break; case Position.sitting: chance -= 20; break; case Position.kneeling: chance -= 15; break; case Position.fighting: case Position.standing: default: break; } // Small penalty for the small buggers -- Xangis if (victim.CurrentSize < (ch.CurrentSize - 1)) { chance -= 15; } if (chance > 90) { chance = 90; } // Shaman bodyslam penalty. if (ch.IsClass(CharClass.Names.shaman) || ch.IsClass(CharClass.Names.druid)) { chance = (chance * 2) / 3; } if (victim.IsAffected(Affect.AFFECT_AWARE)) { chance -= 15; } else if (victim.IsAffected( Affect.AFFECT_SKL_AWARE)) { if (ch.HasSkill("springleap")) { if (ch.IsNPC()) { if (MUDMath.NumberPercent() < ((ch.Level * 3) / 2 + 15)) { chance -= 15; } } else if (MUDMath.NumberPercent() < ((PC)ch).SkillAptitude["awareness"]) { ch.PracticeSkill("awareness"); chance -= 15; } else { ch.PracticeSkill("awareness"); } } } if (!ch.Fighting) { Combat.SetFighting(ch, victim); } if (victim.Fighting == null) { Combat.SetFighting(victim, ch); } if (ch.IsNPC() || MUDMath.NumberPercent() < chance) { if (victim.IsAffected( Affect.AFFECT_SINGING)) { victim.RemoveAffect(Affect.AFFECT_SINGING); SocketConnection.Act("$n&n suddenly loses track of the key $e was singing in.", victim, null, null, SocketConnection.MessageTarget.room); victim.SendText("You get the wind knocked out of you!\r\n"); } if (victim.IsAffected( Affect.AFFECT_CASTING)) { victim.RemoveAffect(Affect.AFFECT_CASTING); SocketConnection.Act("$n&n's thoughts of casting are scattered about as $e is slammed into the ground.", victim, null, null, SocketConnection.MessageTarget.room); victim.SendText("Your brain slamming against your skull disrupts your concentration.\r\n"); } // Moved damage to bottom because it would crash when a person died, because // it still tried to access them as a valid character. Also added tumble check for // thieves. if (!Combat.CheckTumble(victim)) { victim.WaitState(Skill.SkillList["bodyslam"].Delay); victim.CurrentPosition = Position.reclining; Combat.InflictDamage(ch, victim, MUDMath.NumberRange(1, ch.Level), "bodyslam", ObjTemplate.WearLocation.none, AttackType.DamageType.bludgeon); } else { if (!Combat.InflictDamage(ch, victim, MUDMath.NumberRange(1, (ch.Level / 3)), "bodyslam", ObjTemplate.WearLocation.none, AttackType.DamageType.bludgeon)) { ch.SendText("You roll with the blow, finally landing on your feet.\r\n"); SocketConnection.Act("$n&n rolls with the blow, finally landing on $s feet.", ch, null, null, SocketConnection.MessageTarget.room); } } } else { ch.Hitpoints -= MUDMath.NumberRange(1, 5); SocketConnection.Act("As $N&n avoids your slam, you smack headfirst into the &n&+yground&n.", ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n throws $mself to the &n&+yground&n in a fit of clumsiness.", ch, null, victim, SocketConnection.MessageTarget.victim); SocketConnection.Act("$n&n misses a bodyslam on $N&n and slams $s head into the &n&+yground&n.", ch, null, victim, SocketConnection.MessageTarget.room_vict); ch.CurrentPosition = Position.reclining; } return; }
/// <summary> /// Circle around behind someone and backstab them. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Circle(CharData ch, string[] str) { if( ch == null ) return; CharData victim; /* Verify that ch can circle. */ if (!ch.IsNPC() && !ch.HasSkill("circle")) { ch.SendText("You'd better leave the assassination trade to those more skilled.\r\n"); return; } /* No charmies or NPC's are allowed to circle. */ if (ch.IsNPC() && ch.IsAffected(Affect.AFFECT_CHARM)) { return; } /* Yeah, gallop around them without them noticing. */ if (ch.Riding) { ch.SendText("You can't circle while mounted.\r\n"); return; } /* Find the unlucky soul. */ if (str.Length == 0) { victim = ch.Fighting; } else { if (!(victim = ch.GetCharRoom(str[0]))) { ch.SendText("They aren't here.\r\n"); return; } } /* No target. */ if (!victim) { ch.SendText("Circle who?\r\n"); return; } /* Run around yourself? Ok. */ if (victim == ch) { ch.SendText("You spin around in a circle. Whee!\r\n"); return; } /* Check for protection of victim. */ victim = Combat.CheckGuarding(ch, victim); if (Combat.IsSafe(ch, victim)) { return; } // is_safe could wipe out victim, as it calls procs if a boss // check and see that victim is still valid if (!victim) { return; } /* Check if someone is attacking ch. */ CharData roomChar = null; foreach (CharData irch in ch.InRoom.People) { if (irch.Fighting == ch) { roomChar = irch; break; } } if (roomChar) { ch.SendText("You're too busy being hit right now.\r\n"); return; } Object obj = Object.GetEquipmentOnCharacter(ch, ObjTemplate.WearLocation.hand_one); if (!obj || obj.Values[3] != 11) { ch.SendText("You need to wield a piercing weapon.\r\n"); return; } SocketConnection.Act("You circle around behind $N&n...", ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n circles around behind $N&n...", ch, null, victim, SocketConnection.MessageTarget.room_vict); Crime.CheckAttemptedMurder(ch, victim); ch.WaitState(Skill.SkillList["circle"].Delay); if (ch.IsNPC() || MUDMath.NumberPercent() < ((PC)ch).SkillAptitude["circle"] + ch.GetCurrAgi() - victim.GetCurrAgi()) { /* Don't always switch. */ if (MUDMath.NumberPercent() < 40) { Combat.StopFighting(victim, false); } Combat.SingleAttack(ch, victim, "circle", ObjTemplate.WearLocation.hand_one); } else { SocketConnection.Act("You failed to get around $M!", ch, null, victim, SocketConnection.MessageTarget.character); } ch.PracticeSkill("circle"); return; }
/// <summary> /// Kick someone. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Kick(CharData ch, string[] str) { if( ch == null ) return; int chance; int wallkickchance; /* Check player's level and class, allow mobs to do this too */ if ((!ch.HasSkill("kick"))) { ch.SendText("You'd better leave the martial arts to fighters.\r\n"); return; } if (ch.IsBlind() && !ch.Fighting) { return; } CharData victim = ch.Fighting; if (str.Length != 0) { victim = ch.GetCharRoom(str[0]); if (!victim || victim.CurrentPosition == Position.dead) { ch.SendText("They aren't here.\r\n"); return; } } else { if (!victim || victim.CurrentPosition == Position.dead) { ch.SendText("You aren't fighting anyone.\r\n"); return; } } if (victim == ch) { ch.SendText("You kick yourself for being a dummy.\r\n"); return; } ch.WaitState(MUDMath.FuzzyNumber(Skill.SkillList["kick"].Delay)); if (!ch.IsNPC()) { chance = ((PC)ch).SkillAptitude["kick"]; ch.PracticeSkill("kick"); } else { chance = ch.Level * 3 / 2 + 20; } // It's much harder to kick really tiny things; imagine trying to kick // a fly. if (ch.CurrentSize > victim.CurrentSize + 5) { chance -= (ch.CurrentSize - victim.CurrentSize) * 5; } // It's harder to kick things that move faster than you. chance += ((ch.GetCurrAgi() - victim.GetCurrAgi()) / 5); // Huge bonus against incapacitated and mortally wounded foes. if (victim.CurrentPosition <= Position.incapacitated) { chance += 50; } // Damned high penalty for kicking blind if (ch.IsAffected(Affect.AFFECT_BLIND) && !victim.IsAffected(Affect.AFFECT_BLIND)) { chance /= 10; } if (ch.IsAffected(Affect.AFFECT_BLIND) && victim.IsAffected(Affect.AFFECT_BLIND)) { chance /= 4; } // If the victim is two or more sizes smaller than the kicker give them a chance // to be kicked into a wall or out of the room. // // Chance of 5% per size class difference, no maximum // (wall/room kick is 50% at a difference of 10 sizes) if (victim.CurrentSize - 1 >= ch.CurrentSize) { wallkickchance = 0; } else { wallkickchance = ((ch.CurrentSize - victim.CurrentSize) * 5) - 5; } // Check for kick success if (MUDMath.NumberPercent() < chance) { /* Check for wall kick. */ /* to be kicked out of the room (random direction). */ if (MUDMath.NumberPercent() < wallkickchance) { Exit.Direction door = Database.RandomDoor(); // Check for valid room stuff on victim Room kickedInto; Exit exit; if (victim && victim.InRoom && victim.InRoom.ExitData != null && (exit = victim.InRoom.GetExit(door)) && exit.TargetRoom && exit.ExitFlags != 0 && !exit.HasFlag(Exit.ExitFlag.secret) && !exit.HasFlag(Exit.ExitFlag.blocked) && !exit.HasFlag(Exit.ExitFlag.walled) && exit.HasFlag(Exit.ExitFlag.closed) && !Room.GetRoom(exit.IndexNumber).IsPrivate() && exit.TargetRoom.TerrainType != TerrainType.underground_impassable && (kickedInto = Room.GetRoom(exit.IndexNumber))) { Combat.StopFighting(victim, true); string buf = String.Format("$N&n is sent flying out of the room {0}ward by $n&n's mighty kick.", door.ToString()); SocketConnection.Act(buf, ch, null, victim, SocketConnection.MessageTarget.room_vict); buf = String.Format("$N&n is sent flying out of the room {0}ward by your mighty kick.", door.ToString()); SocketConnection.Act(buf, ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("You are sent flying out of the room by $n's mighty kick!", ch, null, victim, SocketConnection.MessageTarget.victim); victim.RemoveFromRoom(); victim.AddToRoom(kickedInto); SocketConnection.Act("$n&n is stunned!", victim, null, null, SocketConnection.MessageTarget.room); victim.SendText("You are stunned!\r\n"); victim.WaitState((Skill.SkillList["kick"].Delay * 9) / 10); if (victim.CurrentPosition > Position.resting) { victim.CurrentPosition = Position.resting; } } else { // If no exit in our chosen direction, must be a wall. SocketConnection.Act("$N&n is sent flying into the wall by $n&n's mighty kick.", ch, null, victim, SocketConnection.MessageTarget.room_vict); SocketConnection.Act("$N&n is sent flying into the wall by your mighty kick.", ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("You are smacked into the wall by $n's mighty kick!", ch, null, victim, SocketConnection.MessageTarget.victim); if (victim.CurrentPosition > Position.resting) { victim.CurrentPosition = Position.resting; } // At least a two second stun victim.WaitState(8); /* Check for stunning victim. */ if ((MUDMath.NumberPercent() * 2) > victim.GetCurrAgi()) { SocketConnection.Act("$N&n is stunned!", ch, null, victim, SocketConnection.MessageTarget.room_vict); victim.SendText("You are stunned!\r\n"); victim.WaitState((Skill.SkillList["kick"].Delay * 9) / 10); } else { victim.WaitState(1); } } // Do excessive damage compared to a normal kick Combat.InflictDamage(ch, victim, MUDMath.Dice(2, ch.Level), String.Empty, ObjTemplate.WearLocation.none, AttackType.DamageType.bludgeon); } // Check for wall kick (execute regular kick) else { Combat.InflictDamage(ch, victim, MUDMath.NumberRange(1, ch.Level), "kick", ObjTemplate.WearLocation.none, AttackType.DamageType.bludgeon); } } // Check for successful kick (missed kick) else { Combat.InflictDamage(ch, victim, 0, "kick", ObjTemplate.WearLocation.none, AttackType.DamageType.bludgeon); } return; }
/// <summary> /// Sends ability scores when rolling stats or applying bonuses. Used to avoid repetitive code. /// </summary> /// <param name="ch"></param> private void SendAbilityScores(CharData ch) { string text = String.Format( "\r\n\r\nStr: {0} Int: {1}\r\nDex: {2} Wis: {3}\r\nAgi: {4} Cha: {5}\r\nCon: {6}\r\nPow: {7}\r\n\r\n", MUDString.PadStr( StringConversion.AbilityScoreString( ch.GetCurrStr() ), 17 ), MUDString.PadStr( StringConversion.AbilityScoreString( ch.GetCurrInt() ), 17 ), MUDString.PadStr( StringConversion.AbilityScoreString( ch.GetCurrDex() ), 17 ), MUDString.PadStr( StringConversion.AbilityScoreString( ch.GetCurrWis() ), 17 ), MUDString.PadStr( StringConversion.AbilityScoreString( ch.GetCurrAgi() ), 17 ), MUDString.PadStr( StringConversion.AbilityScoreString( ch.GetCurrCha() ), 17 ), MUDString.PadStr( StringConversion.AbilityScoreString( ch.GetCurrCon() ), 17 ), MUDString.PadStr( StringConversion.AbilityScoreString( ch.GetCurrPow() ), 17 ) ); WriteToBuffer( text ); }
/// <summary> /// Checks whether a spell cast by the player is swallowed by one of the starshell types. /// </summary> /// <param name="ch">The caster</param> /// <returns>true if the spell was eaten.</returns> public bool CheckStarshell(CharData ch) { if( !ch.IsClass( CharClass.Names.bard ) ) { if( ch.InRoom.HasFlag( ROOM_EARTHEN_STARSHELL ) ) { ch.SendText( "You start casting...\r\n" ); ch.SendText( "&+lThe &+yearth&n &+lcomes up &+yand engulfs &+lyour spell.\r\n" ); Combat.InflictSpellDamage( ch, ch, 1, "earthen starshell", AttackType.DamageType.fire ); ch.WaitState( 6 ); return true; } if( ch.InRoom.HasFlag( ROOM_AIRY_STARSHELL ) ) { ch.SendText( "You start casting...\r\n" ); ch.SendText( "&+CAir swir&n&+cls a&+Cnd absorbs y&n&+cour spell.&n\r\n" ); ch.WaitState( 6 ); if( ch.CurrentPosition > Position.reclining && MUDMath.NumberPercent() + 50 > ch.GetCurrAgi() ) { ch.CurrentPosition = Position.reclining; ch.WaitState( 6 ); ch.SendText( "You are knocked over!\r\n" ); } return true; } if( ch.InRoom.HasFlag( ROOM_WATERY_STARSHELL ) ) { ch.SendText( "You start casting...\r\n" ); ch.SendText( "&+bWater b&+Bursts up a&n&+bnd absor&+Bbs your spell.&n\r\n" ); ch.WaitState( 6 ); ch.CurrentMoves -= 20; ch.SendText( "You feel tired!\r\n" ); return true; } if( ch.InRoom.HasFlag( ROOM_FIERY_STARSHELL ) ) { ch.SendText( "You start casting...\r\n" ); ch.SendText( "&+RFire&n&+r engu&+Rlfs y&n&+rour s&+Rpell.&n\r\n" ); Combat.InflictSpellDamage( ch, ch, 1, "fiery starshell", AttackType.DamageType.fire ); ch.WaitState( 6 ); return true; } } return false; }
/// <summary> /// Check tumble skill to see whether an attack is avoided. /// </summary> /// <param name="ch"></param> /// <returns></returns> public static bool CheckTumble( CharData ch ) { if (ch == null) return false; int chance; if( !ch.HasSkill( "tumble" ) ) return false; if( ch.IsNPC() ) chance = ch.Level / 2 + 8; else chance = ( (PC)ch ).SkillAptitude[ "tumble" ] / 3; chance += ch.GetCurrAgi() / 15; ch.PracticeSkill( "tumble" ); if( MUDMath.NumberPercent() >= chance ) return false; return true; }
/// <summary> /// Check for falling. Called from room update and movement. /// </summary> /// <param name="room"></param> /// <param name="target"></param> /// <param name="ch"></param> public static void CheckFall( Room room, Room target, CharData ch ) { int chance; if( !room || !target || !ch ) return; if( room.TerrainType != TerrainType.air && room.TerrainType != TerrainType.plane_of_air && room.TerrainType != TerrainType.underground_no_ground ) { if( MUDMath.NumberPercent() > room.FallChance ) return; } if( ch.CanFly() || ch.IsAffected( Affect.AFFECT_LEVITATE ) ) return; if( ch.InRoom.People != null ) { SocketConnection.Act( "You are falling down!", ch, null, null, SocketConnection.MessageTarget.character ); SocketConnection.Act( "$n&n falls away.", ch, null, null, SocketConnection.MessageTarget.room ); } ch.RemoveFromRoom(); ch.AddToRoom( target ); if( !ch.HasSkill( "safe_fall" ) ) chance = 0; else if( ch.IsNPC() ) chance = ( ( ch.Level * 3 ) / 2 ) + 15; else chance = ( (PC)ch ).SkillAptitude[ "safe fall" ]; // People with high agility have a small chance to safe fall, and those with // the skill already get a bonus. chance += ( ch.GetCurrAgi() / 20 ); // Minimum 1% chance of a bad fall. if( chance > 99 ) { chance = 99; } // Safe fall added by Xangis if( target.FallChance == 0 || !target.ExitData[ 5 ] || !target.ExitData[ 5 ].TargetRoom ) { if( MUDMath.NumberPercent() < chance ) { // Decent chance of skill increase - people don't fall very often. ch.PracticeSkill( "safe fall" ); ch.PracticeSkill( "safe fall" ); ch.PracticeSkill( "safe fall" ); ch.PracticeSkill( "safe fall" ); ch.SendText( "You fall to the ground!\r\n" ); if( MUDMath.NumberPercent() < chance ) { SocketConnection.Act( "$n&n falls from above and lands gracefully.", ch, null, null, SocketConnection.MessageTarget.room ); ch.SendText( "You land gracefully, avoiding any injury.\r\n" ); } else { SocketConnection.Act( "$n&n falls from above and lands on $s arse.", ch, null, null, SocketConnection.MessageTarget.room ); if( Race.MAX_SIZE > 0 && !ch.IsNPC() ) { Combat.InflictDamage(ch, ch, MUDMath.NumberRange(2, 4), String.Empty, ObjTemplate.WearLocation.none, AttackType.DamageType.none); ch.CurrentPosition = Position.sitting; ch.WaitState( 3 ); } } } else { ch.SendText( "You slam into the ground!\r\n" ); ch.CurrentPosition = Position.sitting; ch.WaitState( 8 ); SocketConnection.Act( "$n&n comes crashing in from above.", ch, null, null, SocketConnection.MessageTarget.room ); if( Race.MAX_SIZE > 0 && !ch.IsNPC() ) { Combat.InflictDamage( ch, ch, ( ( MUDMath.NumberPercent() * (int)ch.CurrentSize ) / Race.MAX_SIZE ), String.Empty, ObjTemplate.WearLocation.none, AttackType.DamageType.none); } } } else if( ch && ch.InRoom ) { if( ch.InRoom.People.Count > 0 ) { SocketConnection.Act( "$n&n falls by.", ch, null, null, SocketConnection.MessageTarget.room ); } } return; }