/// <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> /// 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> /// Per-tick mana recovery. /// </summary> /// <param name="ch"></param> /// <returns></returns> public static int ManaGain( CharData ch ) { int gain; int percent = 0; if( ch == null ) { Log.Error( "ManaGain(): null ch", 0 ); return 0; } if( ch.IsNPC() ) { gain = 4 + ch.Level / 10; } else { /* at 17 gain == base 5, at 34 gain == base 6, at 51 gain == base 7 */ gain = 4 + ch.Level / 17; percent = 100; switch( ch.CurrentPosition ) { case Position.sleeping: percent += 100; break; case Position.resting: percent += 50; if (ch.HasActionBit(PC.PLAYER_MEDITATING)) { percent += 50; int number = MUDMath.NumberPercent(); if( number < ( (PC)ch ).SkillAptitude[ "meditate" ] ) percent += 150; ch.PracticeSkill( "meditate" ); } break; } if( ( (PC)ch ).Hunger == 0 ) { percent -= 50; } if( ( (PC)ch ).Thirst == 0 ) { percent -= 50; } } if( percent < 0 ) percent = 0; gain = ( gain * percent ) / 100; if (!ch.IsClass(CharClass.Names.bard)) gain = ( gain * ch.GetCurrPow() ) / 100; else gain = ( gain * ch.GetCurrCha() ) / 100; return Math.Min( gain, ch.MaxMana - ch.CurrentMana ); }
/// <summary> /// Summoning an elemental. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="level"></param> /// <param name="indexNumber"></param> public static void SummonElem(CharData ch, Spell spell, int level, int indexNumber) { int numpets = 0; foreach (CharData petChar in Database.CharList) { if (petChar != ch && petChar.Master == ch && petChar.IsNPC() && petChar.HasActionBit(MobTemplate.ACT_PET)) numpets++; } //just a WAG as far as number...check for some consistency with necro int maxpets = ch.Level / 20 + ch.GetCurrCha() / 35; if (ch.Level >= Limits.LEVEL_AVATAR) { string text = String.Format("You can summon at most {0} pets.\r\n", maxpets); ch.SendText(text); } if (numpets >= maxpets) { ch.SendText("You cannot handle any more pets!\r\n"); return; } CharData elemental = Database.CreateMobile(Database.GetMobTemplate(indexNumber)); elemental.AddToRoom(ch.InRoom); elemental.SetCoins(0, 0, 0, 0); elemental.Level = 27 + MUDMath.Dice(2, 5); elemental.MaxHitpoints = 150 + MUDMath.Dice(16, level / 2); elemental.Hitpoints = elemental.MaxHitpoints; elemental.ArmorPoints -= (ch.Level * 3 / 2); elemental.SetActionBit(MobTemplate.ACT_NOEXP); switch (indexNumber) { case StaticMobs.MOB_NUMBER_EARTH_PECH: SocketConnection.Act("$n&n rises from the ground before your eyes.", elemental, null, null, SocketConnection.MessageTarget.room); break; case StaticMobs.MOB_NUMBER_AIR_SLYPH: elemental.ArmorPoints = -130; elemental.PermAgility = 100; SocketConnection.Act("$n&n appears out of thin air.", elemental, null, null, SocketConnection.MessageTarget.room); break; case StaticMobs.MOB_NUMBER_FIRE_SERPENT: SocketConnection.Act("$n&n bursts into existence in a roaring ball of flame.", elemental, null, null, SocketConnection.MessageTarget.room); break; case StaticMobs.MOB_NUMBER_WATER_NEREID: SocketConnection.Act("$n&n coalesces into existence.", elemental, null, null, SocketConnection.MessageTarget.room); elemental.MaxHitpoints += 100; elemental.Hitpoints = elemental.MaxHitpoints; break; default: Log.Error("SummonElem: bad indexNumber in switch: " + indexNumber); ch.SendText("You managed to summon a bad indexNumber! Shame on you.\r\n"); break; } SocketConnection.Act("$N&n says 'Your wish is my command $n&n.'", ch, null, elemental, SocketConnection.MessageTarget.room); SocketConnection.Act("$N&n tells you 'Your wish is my command.'", ch, null, elemental, SocketConnection.MessageTarget.character); CharData.AddFollower(elemental, ch); elemental.Master = ch; Affect af = new Affect(Affect.AffectType.spell, spell.Name, level / 2 + MUDMath.Dice(4, level / 2), Affect.Apply.none, 0, Affect.AFFECT_CHARM); elemental.AddAffect(af); // Set the MobIndex.ACT_PET bit as well elemental.SetActionBit(MobTemplate.ACT_PET); elemental.FlightLevel = ch.FlightLevel; if (ch.Fighting) { Combat.SetFighting(elemental, ch.Fighting); } return; }
/// <summary> /// Gets the cost (cash value) of an object. /// </summary> /// <param name="customer"></param> /// <param name="keeper"></param> /// <param name="obj"></param> /// <param name="isBuyTransaction"></param> /// <returns></returns> public static int GetCost(CharData customer, CharData keeper, Object obj, bool isBuyTransaction) { Shop shop; int cost; if (!obj) return 0; if (!(shop = keeper.MobileTemplate.ShopData)) { cost = obj._cost; } else { if (isBuyTransaction) { cost = obj._cost * shop.PercentBuy / 100; } else { cost = 0; foreach (ObjTemplate.ObjectType itemtype in shop.BuyTypes) { if (obj._itemType == itemtype) { cost = obj._cost * shop.PercentSell / 100; break; } } } } if (obj._itemType == ObjTemplate.ObjectType.staff || obj._itemType == ObjTemplate.ObjectType.wand) { cost = cost * obj._values[2] / obj._values[1]; } // Modify for charisma. Base 80% / 120%, with 1% bonus per 5 charisma. Capped at 100% to // prevent using buy-sell repetition to generate cash from the same item repeatedly. if (!isBuyTransaction) { if (customer.GetCurrCha() < 100) { cost = (cost * (80 + customer.GetCurrCha() / 5)) / 100; } } else { if (customer.GetCurrCha() < 100) { cost = (cost * (120 - customer.GetCurrCha() / 5)) / 100; } } return cost; }