/// <summary> /// Hide yourself or an object. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Hide(CharData ch, string[] str) { if( ch == null ) return; /* Check player's skill */ if (!ch.HasSkill("hide")) { if(str.Length != 0) { HideItem(ch, new[] { str[0] }); return; } ch.SendText("You're far too obvious to hide anywhere.\r\n"); return; } if (ch.Riding) { ch.SendText("You can't do that while mounted.\r\n"); return; } if (ch.CurrentPosition <= Position.sleeping) { return; } ch.SendText("You attempt to hide.\r\n"); if (ch.IsAffected(Affect.AFFECT_HIDE)) { ch.RemoveAffect(Affect.AFFECT_HIDE); } if (ch.CheckSkill("hide")) { ch.SetAffectBit(Affect.AFFECT_HIDE); } ch.WaitState(12); return; }
/// <summary> /// Get down from a mount. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Dismount(CharData ch, string[] str) { if( ch == null ) return; CharData victim = ch.Riding; if (!victim) { ch.SendText("You're not mounted.\r\n"); return; } if (ch.Riding.InRoom != ch.InRoom) { ch.SendText("Weird! You dismount, but your mount isn't here."); if (ch.Riding.Rider && ch.Riding.Rider == ch) { ch.Riding.Rider = null; } ch.Riding = null; return; } ch.WaitState(Skill.SkillList["mount"].Delay); if (ch.CheckSkill("mount", PracticeType.easy)) { SocketConnection.Act("You dismount $N&n.", ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n skillfully dismounts $N&n.", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim); SocketConnection.Act("$n&n dismounts you. Whew!", ch, null, victim, SocketConnection.MessageTarget.victim); victim.Rider = null; ch.Riding = null; ch.CurrentPosition = Position.standing; } else { SocketConnection.Act("You fall off while dismounting $N&n. Ouch!", ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n falls off of $N&n while dismounting.", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim); SocketConnection.Act("$n&n falls off your back.", ch, null, victim, SocketConnection.MessageTarget.victim); victim.Rider = null; ch.Riding = null; ch.CurrentPosition = Position.resting; Combat.InflictDamage(ch, ch, 1, String.Empty, ObjTemplate.WearLocation.none, AttackType.DamageType.bludgeon); } return; }
public static void Heighten(CharData ch, string[] str) { if( ch == null ) return; Affect af = new Affect(); if (!ch.IsNPC() && !ch.HasSkill("heighten senses")) { ch.SendText("Your senses are as heightened as they're going to get.\r\n"); return; } if (ch.HasAffect( Affect.AffectType.skill, "heighten senses")) return; if (ch.CheckSkill("heighten senses")) { af.Value = "heighten senses"; af.Type = Affect.AffectType.skill; af.Duration = 24 + ch.Level; af.SetBitvector(Affect.AFFECT_DETECT_INVIS); ch.AddAffect(af); af.SetBitvector(Affect.AFFECT_SENSE_LIFE); ch.AddAffect(af); af.SetBitvector(Affect.AFFECT_INFRAVISION); ch.AddAffect(af); ch.SendText("Your senses are heightened.\r\n"); } return; }
/// <summary> /// Command for corpse carving. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Carve(CharData ch, string[] str) { if( ch == null ) return; Object weap; Race.Parts value; if (str.Length == 0) { ch.SendText("What do you want to carve?\r\n"); return; } Object corpse = ch.GetObjHere(str[0]); if (!corpse) { ch.SendText("I don't see that around here.\r\n"); return; } if (corpse.ItemType != ObjTemplate.ObjectType.pc_corpse) { ch.SendText("That's not a player corpse!\r\n"); return; } if (str.Length < 2) { ch.SendText("Parts of that corpse that are left:\r\n"); if (corpse.Values[0] == 0) { ch.SendText("None.\r\n"); } else { ch.SendText(StringConversion.PartsBitString((Race.Parts)corpse.Values[0])); } return; } // 11 is for piercing eapons if (!(weap = Object.GetEquipmentOnCharacter(ch, ObjTemplate.WearLocation.hand_one)) || weap.Values[3] != 11) { ch.SendText("You need some sort of stabbing weapon for carving.\r\n"); return; } if (!MUDString.StringsNotEqual(str[1], "head")) value = Race.Parts.skull; else if (!MUDString.StringsNotEqual(str[1], "arms")) value = Race.Parts.arms; else if (!MUDString.StringsNotEqual(str[1], "legs")) value = Race.Parts.legs; else if (!MUDString.StringsNotEqual(str[1], "heart")) value = Race.Parts.heart; else if (!MUDString.StringsNotEqual(str[1], "brains")) value = Race.Parts.brains; else if (!MUDString.StringsNotEqual(str[1], "guts")) value = Race.Parts.guts; else if (!MUDString.StringsNotEqual(str[1], "hands")) value = Race.Parts.hands; else if (!MUDString.StringsNotEqual(str[1], "feet")) value = Race.Parts.feet; else if (!MUDString.StringsNotEqual(str[1], "fingers")) value = Race.Parts.fingers; else if (!MUDString.StringsNotEqual(str[1], "ears")) value = Race.Parts.ears; else if (!MUDString.StringsNotEqual(str[1], "eyes")) value = Race.Parts.eyes; else if (!MUDString.StringsNotEqual(str[1], "tongue")) value = Race.Parts.tongue; else if (!MUDString.StringsNotEqual(str[1], "eyestalks")) value = Race.Parts.eyestalks; else if (!MUDString.StringsNotEqual(str[1], "tentacles")) value = Race.Parts.tentacles; else if (!MUDString.StringsNotEqual(str[1], "fins")) value = Race.Parts.fins; else if (!MUDString.StringsNotEqual(str[1], "wings")) value = Race.Parts.wings; else if (!MUDString.StringsNotEqual(str[1], "tail")) value = Race.Parts.tail; else if (!MUDString.StringsNotEqual(str[1], "claws")) value = Race.Parts.claws; else if (!MUDString.StringsNotEqual(str[1], "fangs")) value = Race.Parts.fangs; else if (!MUDString.StringsNotEqual(str[1], "scales")) value = Race.Parts.scales; else if (!MUDString.StringsNotEqual(str[1], "horns")) value = Race.Parts.horns; else if (!MUDString.StringsNotEqual(str[1], "tusks")) value = Race.Parts.tusks; else { ch.SendText("No such part.\r\n"); return; } if (!Macros.IsSet(corpse.Values[0], (int)value)) { ch.SendText("That part has either already been removed or never existed.\r\n"); return; } Macros.RemoveBit(ref corpse.Values[0], (int)value); if (ch.CheckSkill("carve")) { ch.SendText("You managed to pry it loose!\r\n"); } else { ch.SendText("You mangled that part badly.\r\n"); return; } /* corpse of {0} */ string name = MUDString.LastArgument(corpse.Name); // This is where we would normally create the object... Object part; switch (value) { default: part = Database.CreateObject(Database.GetObjTemplate(StaticObjects.OBJECT_NUMBER_SEVERED_SKULL), 0); break; case Race.Parts.arms: // Arms are wieldable as a club. part = Database.CreateObject(Database.GetObjTemplate(StaticObjects.OBJECT_NUMBER_SLICED_ARM), 0); break; case Race.Parts.legs: // Legs are wieldable as a club, more damage than arms. part = Database.CreateObject(Database.GetObjTemplate(StaticObjects.OBJECT_NUMBER_SLICED_LEG), 0); break; case Race.Parts.scalp: // Scalp can be attached to belt. part = Database.CreateObject(Database.GetObjTemplate(StaticObjects.OBJECT_NUMBER_SEVERED_SCALP), 0); break; case Race.Parts.heart: // Heart can be eaten as food. part = Database.CreateObject(Database.GetObjTemplate(StaticObjects.OBJECT_NUMBER_TORN_HEART), 0); break; } string buf = String.Format("{0} {1}", StringConversion.PartsBitString(value), name); part.Name = buf; part.ShortDescription = String.Empty; buf = String.Format("the{0} of {1}", StringConversion.PartsBitString(value), name); part.ShortDescription = buf; buf = String.Format("The{0} of {1}&n {2} lying here.", StringConversion.PartsBitString(value), name, MUDString.IsAre(StringConversion.PartsBitString(value))); part.FullDescription = buf; part.Cost = 0; part.Level = corpse.Level; if (value == Race.Parts.skull) // Should be skull. part.Timer = -1; else part.Timer = MUDMath.Dice(5, 20); part.AddFlag(ObjTemplate.ITEM_NOSELL); corpse.AddToObject(part); }
public static void Chameleon(CharData ch, string[] str) { if( ch == null ) return; if (!ch.IsNPC() && !ch.HasSkill("chameleon power")) { ch.SendText("You don't know how to act like a chameleon.\r\n"); return; } ch.SendText("You attempt to blend in with your surroundings.\r\n"); if (ch.IsAffected(Affect.AFFECT_HIDE)) { ch.RemoveAffect(Affect.AFFECT_HIDE); } if (ch.CheckSkill("chameleon power")) { ch.SetAffectBit(Affect.AFFECT_HIDE); } return; }
/// <summary> /// Rescue command. Interpose yourself between the target and their opponent so you become /// the one taking damage. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Rescue(CharData ch, string[] str) { if( ch == null ) return; CharData victim; /* Don't allow the unskilled to rescue */ if ((!ch.HasSkill("rescue"))) { ch.SendText("You'd better leave the heroic acts to warriors.\r\n"); return; } if (ch.IsAffected(Affect.AFFECT_BERZERK)) { ch.SendText("You can't rescue anyone, you're in a &+RBl&n&+ro&+Ro&n&+rd&+L Rage&n!!\r\n"); return; } if (ch.Riding) { ch.SendText("You can't do that while mounted.\r\n"); return; } if (str.Length == 0) { ch.SendText("Rescue whom?\r\n"); return; } if (!(victim = ch.GetCharRoom(str[0]))) { ch.SendText("They aren't here.\r\n"); return; } if (victim == ch) { ch.SendText("What about fleeing instead?\r\n"); return; } if (ch.Fighting == victim) { ch.SendText("Too late.\r\n"); return; } if (!victim.Fighting) { ch.SendText("That person is not fighting right now.\r\n"); return; } if (ch.IsBlind()) { return; } ch.WaitState(Skill.SkillList["rescue"].Delay); int count = 0; CharData fighter = null; foreach (CharData ifch in ch.InRoom.People) { if (ifch.Fighting == victim) { if (MUDMath.NumberRange(0, count) == 0) { fighter = ifch; break; } ++count; } } if (!fighter || !ch.CheckSkill("rescue")) { SocketConnection.Act("$n&n fails miserably in $s attempt to rescue $N&n.", ch, null, victim, SocketConnection.MessageTarget.room_vict); SocketConnection.Act("$n&n fails miserably in $s attempt to rescue you.", ch, null, victim, SocketConnection.MessageTarget.victim); SocketConnection.Act("You fail in your attempt to rescue $N&n.", ch, null, victim, SocketConnection.MessageTarget.character); return; } SocketConnection.Act("&+WYou leap in front of $N&n!", ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("&+W$n&+W rescues you!", ch, null, victim, SocketConnection.MessageTarget.victim); SocketConnection.Act("&+w$n&+w leaps in front of $N&+w taking his place in battle!&n", ch, null, victim, SocketConnection.MessageTarget.room_vict); Combat.StopFighting(fighter, false); Combat.StopFighting(victim, false); Combat.SetFighting(fighter, ch); if (!ch.Fighting) { Combat.SetFighting(ch, fighter); } return; }
public static void Shadow(CharData ch, string[] str) { if( ch == null ) return; Affect af = new Affect(); if (!ch.IsNPC() && !ch.HasSkill("shadow form")) { ch.SendText("You don't know how to take shadow form.\r\n"); return; } ch.SendText("You attempt to move in the shadows.\r\n"); ch.AffectStrip( Affect.AffectType.skill, "shadow form"); if (ch.CheckSkill("shadow form")) { af.Value = "shadow form"; af.Type = Affect.AffectType.skill; af.Duration = ch.Level; af.SetBitvector(Affect.AFFECT_SNEAK); ch.AddAffect(af); } ch.WaitState(10); return; }
/// <summary> /// Apply poison to a weapon. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void PoisonWeapon(CharData ch, string[] str) { if (ch == null) { return; } Object obj; Object pobj = null; Affect af = new Affect(); /* Don't allow mobs or unskilled pcs to do this */ if (ch.IsNPC() || (!ch.IsNPC() && !ch.HasSkill("poison weapon"))) { ch.SendText("What do you think you are, a thief?\r\n"); return; } if (str.Length == 0) { ch.SendText("What are you trying to poison?\r\n"); return; } if (ch.Fighting != null) { ch.SendText("While you're fighting? Nice try.\r\n"); return; } if (!(obj = ch.GetObjCarrying(str[0]))) { ch.SendText("You do not have that weapon.\r\n"); return; } if (obj.ItemType != ObjTemplate.ObjectType.weapon) { ch.SendText("That item is not a weapon.\r\n"); return; } if (obj.HasFlag(ObjTemplate.ITEM_POISONED)) { ch.SendText("That weapon is already poisoned.\r\n"); return; } if (obj.Values[0] != 2) { ch.SendText("You don't have enough poison to cover that!\r\n"); return; } /* Now we have a valid weapon...check to see if we have the poison. */ foreach (Object iobj in ch.Carrying) { // here is where we should check to see if they have poison if (iobj.ItemType == ObjTemplate.ObjectType.drink_container && iobj.Values[2] == 27) { pobj = iobj; break; } } if (!pobj) { ch.SendText("You do not have any poison.\r\n"); return; } if (pobj.Values[1] <= 0 && pobj.Values[1] != -1) { SocketConnection.Act("Sorry, $p&n seems to be empty.", ch, pobj, null, SocketConnection.MessageTarget.character); return; } ch.WaitState(Skill.SkillList["poison weapon"].Delay); /* Check the skill percentage */ if (!ch.CheckSkill("poison weapon")) { ch.SendText("You failed and spill some on yourself. &+ROuch!&n\r\n"); Combat.InflictDamage(ch, ch, ch.Level, "poison weapon", ObjTemplate.WearLocation.none, AttackType.DamageType.poison); SocketConnection.Act("$n spills the &+Gpoison&n all over!", ch, null, null, SocketConnection.MessageTarget.room); pobj.Values[1] -= 2; return; } /* Can't have people smearing gunk on artifacts */ if (obj.InsultArtifact(ch)) { pobj.Values[1]--; return; } SocketConnection.Act("You apply the &+Gpoison&n to $p&n, which glistens wickedly!", ch, obj, null, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n pours the &+Gli&n&+gq&+Gu&n&+gid&n over $p&n, which glistens wickedly!", ch, obj, null, SocketConnection.MessageTarget.room); af.Value = "poison weapon"; af.Type = Affect.AffectType.skill; af.Duration = ch.Level + MUDMath.Dice(4, ch.Level / 2); af.AddModifier(Affect.Apply.none, pobj.Values[3]); af.Level = ch.Level; af.SetBitvector(Affect.AFFECT_POISON); obj.AddAffect(af); // Consume one unit of the poison source. pobj.Values[1]--; return; }
/// <summary> /// Use a scroll to invoke its magical spells. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Recite(CharData ch, string[] str) { if( ch == null ) return; Object scroll; Object obj = null; CharData victim; string arg1 = String.Empty; string arg2 = String.Empty; if (!(scroll = ch.GetObjCarrying(arg1))) { ch.SendText("You do not have that &+Wscroll&n.\r\n"); return; } if (scroll.ItemType != ObjTemplate.ObjectType.scroll) { ch.SendText("You can recite only &+Wscrolls&n.\r\n"); return; } if (String.IsNullOrEmpty(arg2)) { victim = ch; if (ch.Fighting != null) { victim = ch.Fighting; } } else { if (((victim = ch.GetCharRoom(arg2)) == null) && !(obj = ch.GetObjHere(arg2))) { ch.SendText("You can't find it.\r\n"); return; } } if (!ch.CanSpeak()) { ch.SendText("Your lips move but no sound comes out.\r\n"); return; } if (ch.IsNPC() && !ch.IsFreewilled()) { SocketConnection.Act("You try to recite $p&n, but you have no free will.", ch, scroll, null, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n tries to recite $p&n, but has no free will.", ch, scroll, null, SocketConnection.MessageTarget.room); return; } ch.WaitState(2 * Event.TICK_COMBAT); SocketConnection.Act("You recite $p&n.", ch, scroll, null, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n recites $p&n.", ch, scroll, null, SocketConnection.MessageTarget.room); if (ch.CheckSkill("scrolls")) { switch (MUDMath.NumberBits(3)) { case 0: case 1: case 2: case 3: SocketConnection.Act("You can't understand $p&n at all.", ch, scroll, null, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n can't understand $p&n at all.", ch, scroll, null, SocketConnection.MessageTarget.room); return; case 4: case 5: case 6: ch.SendText("You must have said something incorrectly.\r\n"); SocketConnection.Act("$n&n must have said something incorrectly.", ch, null, null, SocketConnection.MessageTarget.room); SocketConnection.Act("$p&n blazes brightly, then is gone.", ch, scroll, null, SocketConnection.MessageTarget.character); SocketConnection.Act("$p&n blazes brightly and disappears.", ch, scroll, null, SocketConnection.MessageTarget.room); scroll.RemoveFromWorld(); ; return; case 7: SocketConnection.Act( "You completely botch the recitation, and $p&n bursts into &+Rflames&n!!", ch, scroll, null, SocketConnection.MessageTarget.character); SocketConnection.Act("$p&n &+rglows&n and then bursts into &+Rflame&n!", ch, scroll, null, SocketConnection.MessageTarget.room); /* * Command.damage( ) call after Object.extract_obj in case the damage would * have extracted ch. This is okay because we merely mark * obj.deleted; it still retains all values until list_update. * Sloppy? Okay, create another integer variable. */ scroll.RemoveFromWorld(); Combat.InflictDamage(ch, ch, scroll.Level, "scrolls", ObjTemplate.WearLocation.none, AttackType.DamageType.fire); return; } } if (scroll.Level > ch.Level) { SocketConnection.Act("$p&n is too high level for you.", ch, scroll, null, SocketConnection.MessageTarget.character); } else { /* scroll.Values[0] is not used for scrolls */ for (int i = 1; i <= 4; i++) { String spellName = SpellNumberToTextMap.GetSpellNameFromNumber(scroll.Values[i]); if (String.IsNullOrEmpty(spellName)) { Log.Error("Recite: Spell number " + obj.Values[i] + " not found for object " + scroll.ObjIndexNumber + ". Make sure it's in the SpellNumberToTextMap."); } Spell spell = StringLookup.SpellLookup(spellName); if (!spell) { Log.Error("Recite: Spell '" + spellName + "' not found for object " + scroll.ObjIndexNumber + ". Make sure it's in the spells file."); } else { spell.Invoke(ch, scroll.Level, ch); } } } if (!ch.IsNPC() || (ch.IsNPC() && ch.IsAffected(Affect.AFFECT_CHARM))) { scroll.RemoveFromWorld(); } return; }
/// <summary> /// Climb on and ride a mount (horse, griffon, etc.) /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Mount(CharData ch, string[] str) { if (!ch) { Log.Error("Commandmount: no ch!", 0); return; } if (ch.Riding) { ch.SendText("You're already mounted!\r\n"); return; } if (str.Length == 0) { ch.SendText("Mount what?\r\n"); return; } CharData victim = ch.GetCharRoom(str[0]); if (victim == null) { ch.SendText("You can't find that here.\r\n"); return; } if (!victim.IsNPC() || !victim.HasActionBit(MobTemplate.ACT_MOUNT)) { ch.SendText("You can't mount that!\r\n"); return; } if (ch.Rider) { ch.SendText("You are being ridden by someone else!\r\n"); return; } if (victim.Rider) { ch.SendText("That mount already has a rider.\r\n"); return; } if (victim.CurrentPosition < Position.standing) { ch.SendText("Your mount must be standing.\r\n"); return; } if (victim.CurrentPosition == Position.fighting || victim.Fighting) { ch.SendText("Your mount is moving around too much.\r\n"); return; } ch.WaitState(Skill.SkillList["mount"].Delay); // NPCs and Immortals automatically succeed. The former is partly because the mount command // can be issued by a zone reset. We wouldn't want a mob to fail a mount when the area declares // that they should be mounted. if (ch.IsNPC() || ch.IsImmortal() || ch.CheckSkill("mount", PracticeType.easy )) { victim.Rider = ch; ch.Riding = victim; SocketConnection.Act("You mount $N&n.", ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n skillfully mounts $N&n.", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim); SocketConnection.Act("$n&n mounts you.", ch, null, victim, SocketConnection.MessageTarget.victim); } else { SocketConnection.Act("You unsuccessfully try to mount $N&n.", ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n unsuccessfully attempts to mount $N&n.", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim); SocketConnection.Act("$n&n tries to mount you.", ch, null, victim, SocketConnection.MessageTarget.victim); } return; }
public static void Berzerk(CharData ch, string[] str) { if( ch == null ) return; Affect af = new Affect(); /* Don't allow charmed mobs to do this, check player's level */ if ((ch.IsNPC() && ch.IsAffected( Affect.AFFECT_CHARM)) || (!ch.IsNPC() && !ch.HasSkill("berzerk"))) { ch.SendText("You're not enough of a warrior to enter a &+RBl&n&+ro&+Ro&n&+rd&+L Rage&n.\r\n"); return; } if (ch.IsAffected(Affect.AFFECT_BERZERK)) { if (MUDMath.NumberPercent() + 10 > ((PC)ch).SkillAptitude["berzerk"]) { ch.SendText("You failed to calm yourself down!\r\n"); ch.WaitState(Skill.SkillList["berzerk"].Delay); return; } ch.SendText("You no longer see targets everywhere.\r\n"); ch.RemoveAffect(Affect.AFFECT_BERZERK); ch.WaitState(Skill.SkillList["berzerk"].Delay); return; } ch.SendText("Your slam your weapon into yourself and &+Rbl&n&+ro&+Ro&n&+rd&n splatters all over!\r\n"); ch.SendText("The sight of &+Rbl&n&+ro&+Ro&n&+rd&n begins to drive you crazy!\r\n"); if (ch.CheckSkill("berzerk")) { af.Value = "berzerk"; af.Type = Affect.AffectType.skill; af.Duration = MUDMath.Dice(1, 2); af.AddModifier( Affect.Apply.hitroll, Math.Max(ch.Level / 6, 2)); af.AddModifier( Affect.Apply.damroll, Math.Max(ch.Level / 6, 2)); af.AddModifier( Affect.Apply.ac, (ch.Level / 2)); af.AddModifier( Affect.Apply.max_constitution, MUDMath.Dice(5, 9)); af.AddModifier( Affect.Apply.agility, 0 - MUDMath.Dice(5, 9)); af.AddModifier( Affect.Apply.max_strength, MUDMath.Dice(5, 9)); af.SetBitvector(Affect.AFFECT_BERZERK); ch.AddAffect(af); ch.SendText("You are overcome by &+RBl&n&+ro&+Ro&n&+rd&+L Rage&n!!\r\n"); SocketConnection.Act("$n has slipped into a &+RBl&n&+ro&+Ro&n&+rd&+L Rage&n!!", ch, null, null, SocketConnection.MessageTarget.room); return; } ch.SendText("You get a little angry, but fail to call up a &+Rblood rage&n.\r\n"); 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> /// Regeneration and natural healing. /// </summary> /// <param name="ch"></param> /// <returns></returns> public static int HitGain( CharData ch ) { int gain; int percent = 0; if( ch == null ) { Log.Error( "HitGain(): null ch", 0 ); return 0; } // They aren't going to gain hits in a no heal room... if (ch.InRoom.HasFlag(RoomTemplate.ROOM_NO_HEAL)) { return 0; } gain = 1; percent = 100; if (ch.CheckSkill("fast healing", PracticeType.none)) { gain += 1; } if (MUDMath.NumberPercent() < 3) { ch.PracticeSkill("fast healing"); } switch( ch.CurrentPosition ) { case Position.sleeping: gain += 3; break; case Position.reclining: gain += 2; break; case Position.resting: gain += 1; break; case Position.fighting: gain = 0; break; } if( ch.HasInnate( Race.RACE_REGENERATE ) ) { // Automatically one extra hp, two at level 30. gain += 1 + (ch.Level / 30); // One percent chance of gaining another per level. percent += (ch.Level); } // Hunger and thirst for PCs. if (!ch.IsNPC()) { if (((PC)ch).Hunger == 0) { gain /= 2; gain -= 1; percent -= 25; ch.SendText("&nYou double over from &+Rhunger pains&n!\r\n"); } if (((PC)ch).Thirst == 0) { gain /= 2; gain -= 1; percent -= 25; ch.SendText("&nYou suffer from severe &+cth&+Ci&n&+cr&+Cst&n!\r\n"); } } if( ch.IsAffected( Affect.AFFECT_POISON ) ) { gain /= 4; percent /= 2; } if( gain == 0 ) if( MUDMath.NumberPercent() < percent ) gain = 1; // Heal rooms heal you a little quicker if (ch.InRoom.HasFlag(RoomTemplate.ROOM_HEAL)) { gain += Math.Max(1, gain / 2); } if( ( ch.InRoom.TerrainType != TerrainType.underwater_has_ground && ch.InRoom.TerrainType != TerrainType.unswimmable_water && ch.InRoom.TerrainType != TerrainType.swimmable_water && ch.HasInnate( Race.RACE_WATERBREATH ) && MUDString.StringsNotEqual( Race.RaceList[ ch.GetRace() ].Name, "Object" ) && ch.GetRace() != Race.RACE_GOD ) || ( ch.InRoom.TerrainType == TerrainType.underwater_has_ground && ( !ch.IsImmortal() && !ch.IsAffected( Affect.AFFECT_BREATHE_UNDERWATER ) && !ch.HasInnate( Race.RACE_WATERBREATH ) ) ) ) gain = 0; return Math.Min( gain, ch.GetMaxHit() - ch.Hitpoints ); }
/// <summary> /// Called by the cast function to process a spell's targets and react accordingly. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="argument"></param> private static void ProcessSpellTargets(CharData ch, Spell spell, string argument) { Object obj = null; Target target = null; CharData victim = null; if (!ch.IsClass(CharClass.Names.bard)) switch (spell.ValidTargets) { default: Log.Error("Magic.Cast: bad target type for spell {0}. Type is: {1}", spell.Name, spell.ValidTargets.ToString()); return; case TargetType.none: target = new Target(argument); break; case TargetType.singleCharacterWorld: victim = ch.GetCharWorld(argument); if (victim == null) { ch.SendText("Cast the spell on whom?\r\n"); return; } target = new Target(victim); break; case TargetType.trap: ch.SendText("You cannot cast a trap!\r\n"); return; case TargetType.singleCharacterOffensive: if (String.IsNullOrEmpty(argument)) { victim = ch.Fighting; if (victim == null) { if (ch.IsClass(CharClass.Names.psionicist)) ch.SendText("Will the spell upon whom?\r\n"); else ch.SendText("Cast the spell upon whom?\r\n"); return; } } else { victim = ch.GetCharRoom(argument); if (!victim) { ch.SendText("They aren't here.\r\n"); return; } } if (ch.IsAffected( Affect.AFFECT_CHARM) && ch.Master == victim) { ch.SendText("You can't do that to your master!.\r\n"); return; } if (Combat.IsSafe(ch, victim)) return; // Combat.IsSafe could wipe out victim, as it calls procs if a boss // check and see that victim is still valid if (!victim) return; Crime.CheckAttemptedMurder(ch, victim); target = new Target(victim); ch.BreakInvisibility(); break; case TargetType.singleCharacterDefensive: if (String.IsNullOrEmpty(argument)) { victim = ch; } else { victim = ch.GetCharRoom(argument); if (victim == null) { ch.SendText("They aren't here.\r\n"); return; } } target = new Target(victim); break; case TargetType.self: if (!String.IsNullOrEmpty(argument) && !MUDString.NameContainedIn(argument, ch.Name) && "me".Equals(argument, StringComparison.CurrentCultureIgnoreCase) && "self".Equals(argument, StringComparison.CurrentCultureIgnoreCase)) { ch.SendText("You cannot cast this spell on another.\r\n"); return; } target = new Target(ch); break; case TargetType.objectInInventory: if (String.IsNullOrEmpty(argument)) { ch.SendText("What item should the spell be cast upon?\r\n"); return; } obj = ch.GetObjCarrying(argument); if (obj == null) { ch.SendText("You are not carrying that.\r\n"); return; } target = new Target(obj); break; case TargetType.objectInRoom: if (String.IsNullOrEmpty(argument)) { ch.SendText("What should the spell be cast upon?\r\n"); return; } obj = ch.GetObjHere(argument); if (obj == null) { ch.SendText("You do not see that here.\r\n"); return; } target = new Target(obj); break; case TargetType.objectCorpse: target = new Target(argument); break; case TargetType.objectOrCharacter: if (String.IsNullOrEmpty(argument)) { if (ch.Fighting != null) victim = ch.Fighting; else { ch.SendText("Cast upon what?\r\n"); return; } } else if (!(victim = ch.GetCharRoom(argument))) { obj = ch.GetObjHere(argument); } if (victim != null) { target = new Target(victim); } else if (obj != null) { target = new Target(obj); } else { ch.SendText("You do not see that here.\r\n"); return; } break; case TargetType.singleCharacterRanged: if (String.IsNullOrEmpty(argument)) { victim = ch.Fighting; if (victim == null) { ch.SendText("Cast the spell on whom?\r\n"); return; } } else { victim = ch.GetCharRoom(argument); if (!victim) { ch.SendText("They aren't here.\r\n"); return; } // Ranged combat. // // TODO: FIXME: The next line does not successfully get the required argument. //int dir = Movement.FindExit(ch, arg3); //if (ch._level >= Limits.LEVEL_IMMORTAL) //{ // buf = String.Format("Looking for {0} to the {1}.\r\n", arg2, arg3); // ch.SendText(buf); //} //if (ch._inRoom._exitData[dir].HasFlag(Exit.ExitFlags.walled) // || ch._inRoom._exitData[dir].HasFlag(Exit.ExitFlags.blocked) // || ch._inRoom._exitData[dir].HasFlag(Exit.ExitFlags.secret) // || ch._inRoom._exitData[dir].HasFlag(Exit.ExitFlags.closed) // || !ch._inRoom._exitData[dir]._targetRoom // || ch._inRoom._area != ch._inRoom._exitData[dir]._targetRoom._area) //{ // ch.SendText("You see nothing in that direction.\r\n"); // return; //} //room2 = Movement.FindRoom(ch, arg3); //if (room2 == null) //{ // ch.SendText("You see nothing in that direction.\r\n"); // return; //} //victim = CharData.GetCharAtRoom(room2, ch, arg2); //if (victim == null) //{ // Room room3; // if (room2._exitData[dir] && ((room3 = Room.GetRoom(room2._exitData[dir]._vnum))) && // spell == Spell.SpellList["fireball"]) // { // victim = CharData.GetCharAtRoom(room3, ch, arg2); // } //} //if (victim == null) //{ // ch.SendText("They aren't here.\r\n"); // return; //} //} //end else } //end else if (ch.IsAffected(Affect.AFFECT_CHARM) && ch.Master == victim) { ch.SendText("You can't do that to your master!.\r\n"); return; } if (Combat.IsSafe(ch, victim)) return; // Combat.IsSafe could wipe out victim, as it calls procs if a boss // check and see that victim is still valid if (!victim) return; Crime.CheckAttemptedMurder(ch, victim); target = new Target(victim); ch.BreakInvisibility(); break; } int beats = 0; // For quick chant if (!ch.IsClass(CharClass.Names.bard) && !ch.IsClass(CharClass.Names.psionicist)) { ch.SendText( "You begin casting...\r\n" ); if( "ventriloquate".Equals(spell.Name, StringComparison.CurrentCultureIgnoreCase )) { if( spell.ValidTargets == TargetType.singleCharacterOffensive || spell.ValidTargets == TargetType.singleCharacterRanged ) SocketConnection.Act( "$n&n begins casting an offensive spell...", ch, null, null, SocketConnection.MessageTarget.room ); else SocketConnection.Act( "$n&n begins casting...", ch, null, null, SocketConnection.MessageTarget.room ); } beats = spell.CastingTime; } else if (ch.IsClass(CharClass.Names.bard)) { ch.SendText( "You begin singing...\r\n" ); SocketConnection.Act( "$n&n starts singing...", ch, null, null, SocketConnection.MessageTarget.room ); beats = 0; } if (!ch.IsClass(CharClass.Names.psionicist) && !ch.IsClass(CharClass.Names.bard)) { // Characters with int of 110 have normal memtimes. // int of 100 worsens casting times by 10% // with an int of 55 casting times are doubled. // This may seem a bit harsh, but keep in mind any // casters are very likely to have an int above 100, as it // is their prime requisite. 120 is the max int for Grey Elves // to start. if (ch.IsClass(CharClass.Names.cleric) || ch.IsClass(CharClass.Names.druid) || ch.IsClass(CharClass.Names.paladin) || ch.IsClass(CharClass.Names.antipaladin)) { beats = ( beats * 110 ) / ch.GetCurrWis(); } else if (ch.IsClass( CharClass.Names.shaman)) { beats = ( beats * 330 ) / ( ch.GetCurrInt() + ( ch.GetCurrWis() * 2 ) ); } else { beats = ( beats * 110 ) / ch.GetCurrInt(); } if( ch.CheckSkill("quick chant", PracticeType.only_on_success) ) { beats = beats / 2; } /* * A check for impossibly long cast times...came about from a player * trying to cast when feebleminded. 100 casting time is arbitrary. */ if( beats > 100 ) { ch.SendText( "Forget it! In your present state you haven't a dream of success.\r\n" ); return; } ch.WaitState( beats ); if( CheckHypnoticPattern( ch ) ) { return; } CastData caster = new CastData(); caster.Who = ch; caster.Eventdata = Event.CreateEvent( Event.EventType.spell_cast, beats, ch, target, spell ); Database.CastList.Add( caster ); ch.SetAffectBit( Affect.AFFECT_CASTING ); } else if (ch.IsClass( CharClass.Names.psionicist)) { ch.WaitState( beats ); if( CheckHypnoticPattern( ch ) ) { return; } ch.PracticeSpell( spell ); int mana = 0; if( !ch.IsImmortal() && !ch.IsNPC() && ch.Level < ( spell.SpellCircle[ (int)ch.CharacterClass.ClassNumber ] * 4 + 1 ) && MUDMath.NumberPercent() > ((PC)ch).SpellAptitude[spell.Name]) { ch.SendText( "You lost your concentration.\r\n" ); SocketConnection.Act( "&+r$n&n&+r's face flushes white for a moment.&n", ch, null, null, SocketConnection.MessageTarget.room ); ch.CurrentMana -= mana / 2; } else { ch.CurrentMana -= mana; string buf = String.Format( "Spell {0} ({1}) being willed by {2}", spell, spell.Name, ch.Name ); Log.Trace( buf ); ch.SetAffectBit( Affect.AFFECT_CASTING ); FinishSpell( ch, spell, target ); } if( ch.CurrentPosition > Position.sleeping && ch.CurrentMana < 0 ) { ch.WaitState( 2 * Event.TICK_PER_SECOND ); ch.SendText( "&+WThat last spe&+wll w&+Las a _bitvector&+l much...&n\r\n" ); ch.CurrentPosition = Position.standing; ch.Fighting = null; SocketConnection.Act( "$n&n collapses from exhaustion&n.", ch, null, null, SocketConnection.MessageTarget.room ); ch.CurrentPosition = Position.sleeping; } if( spell.ValidTargets == TargetType.singleCharacterOffensive && victim && victim.Master != ch && victim != ch && victim.IsAwake() ) { foreach( CharData roomChar in ch.InRoom.People ) { if( victim == roomChar && !victim.Fighting ) { victim.AttackCharacter( ch ); break; } } } } else if( ch.IsClass(CharClass.Names.bard )) { ch.WaitState( 0 ); // Create an event to handle the spell CastData caster = new CastData(); caster.Who = ch; caster.Eventdata = Event.CreateEvent( Event.EventType.bard_song, Event.TICK_SONG, ch, target, spell ); caster.Eventdata = Event.CreateEvent( Event.EventType.bard_song, Event.TICK_SONG * 2, ch, target, spell); caster.Eventdata = Event.CreateEvent( Event.EventType.bard_song, Event.TICK_SONG * 3, ch, target, spell); caster.Eventdata = Event.CreateEvent( Event.EventType.bard_song, Event.TICK_SONG * 4, ch, target, spell); caster.Eventdata = Event.CreateEvent( Event.EventType.bard_song, Event.TICK_SONG * 5, ch, target, spell); Database.CastList.Add( caster ); ch.SetAffectBit( Affect.AFFECT_SINGING ); } return; }