/// <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> /// Search the local area for food. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Forage(CharData ch, string[] str) { if( ch == null ) return; int indexNumber; Object obj = null; int[] flist = new int[10] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 25 }; if (!ch.InRoom) { ch.SendText("There's no foraging to be done here.\r\n"); return; } if (ch.FlightLevel != 0) { ch.SendText("Right, you're going to find something way up here.\r\n"); return; } int chance = 10 + (ch.GetCurrLuck() / 20); if (ch.IsClass(CharClass.Names.ranger) || ch.IsClass(CharClass.Names.hunter) || ch.IsImmortal()) chance = 75; if (chance < MUDMath.NumberPercent()) { ch.SendText("You don't find anything at all.\r\n"); ch.Wait += 10; return; } ch.Wait += 15; TerrainType sector = ch.InRoom.TerrainType; // TODO: FIXME: Don't use hard-coded item numbers! switch (sector) { default: ch.SendText("Nothing edible could be growing here.\r\n"); return; case TerrainType.field: flist[0] = 80; flist[1] = 84; flist[2] = 86; flist[6] = 91; flist[7] = 80; break; case TerrainType.hills: flist[0] = 82748; flist[1] = 86; flist[2] = 92; flist[6] = 94; break; case TerrainType.underground_wild: flist[0] = 3352; flist[5] = 3352; flist[6] = 7711; flist[1] = 85; flist[2] = 88; flist[7] = 82; flist[8] = 83; break; case TerrainType.swamp: flist[0] = 3352; flist[1] = 88; flist[5] = 94; flist[6] = 83; flist[7] = 89; break; case TerrainType.forest: flist[0] = 2057; flist[1] = 81651; flist[2] = 90; flist[3] = 93; flist[4] = 92; flist[5] = 90; flist[6] = 87; flist[7] = 84; break; } //end switch if (ch.IsClass(CharClass.Names.ranger) || ch.IsClass(CharClass.Names.hunter)) indexNumber = flist[MUDMath.NumberRange(0, 9)]; else indexNumber = flist[MUDMath.NumberRange(0, 4)]; if (indexNumber == 0) { ch.SendText("You find nothing edible.\r\n"); return; } string buf = String.Format("Forage: {0} found index number {1} in room {2}.", ch.Name, indexNumber, ch.InRoom.IndexNumber); ImmortalChat.SendImmortalChat(null, ImmortalChat.IMMTALK_SPAM, 0, buf); /* if (fvnum == StaticObjects.OBJECT_NUMBER_SPRING) { //don't allow endless springs for ( obj = ch.in_room.contents; obj; obj = obj.next_content ) { if (obj.pIndexData.vnum == fvnum) { Descriptor._actFlags("You notice the $p&n.", ch, obj, null, Descriptor.MessageTarget.character); return; } } } */ ObjTemplate objTemplate = Database.GetObjTemplate(indexNumber); // Foraging found an object that doesn't exist -- log an error and fail gracefully. if (objTemplate == null) { Log.Error("Forage: invalid object index number " + indexNumber + " in terrain type " + sector + "."); ch.SendText("The area appears to have been picked clean.\r\n"); return; } obj = Database.CreateObject(objTemplate, 1); obj.AddToRoom(ch.InRoom); obj.FlyLevel = 0; if (indexNumber == StaticObjects.OBJECT_NUMBER_SPRING) // give spring a timer; { obj.Timer = 10 + MUDMath.NumberRange(1, 20); } SocketConnection.Act("You find $p&n.", ch, obj, null, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n forages around and finds $p&n.", ch, obj, null, SocketConnection.MessageTarget.room); return; }
/// <summary> /// Guard someone - try to prevent them from becoming the target of attacks. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Guard(CharData ch, string[] str) { if( ch == null ) return; CharData worldChar; if (ch.IsNPC()) return; if (!ch.HasSkill("guard")) { ch.SendText("Guard!? You can't even protect yourself!\r\n"); return; } if (str.Length == 0) { if (!((PC)ch).Guarding) { ch.SendText("Guard who?\r\n"); return; } if (!((PC)ch).Guarding) { string buf = "You are guarding " + (((PC)ch).Guarding.IsNPC() ? ((PC)ch).Guarding.ShortDescription : ((PC)ch).Guarding.Name) + ".\r\n"; ch.SendText(buf); return; } } if (!MUDString.StringsNotEqual(str[0], "who")) { if (!((PC)ch).Guarding) { ch.SendText("You are not guarding anyone.\r\n"); } else { SocketConnection.Act("You are guarding $N&n.", ch, null, ((PC)ch).Guarding, SocketConnection.MessageTarget.character); } foreach (CharData it in Database.CharList) { worldChar = it; if (worldChar.IsNPC()) { continue; } if (((PC)worldChar).Guarding && ((PC)worldChar).Guarding == ch) { SocketConnection.Act("$N&n is guarding you.", ch, null, worldChar, SocketConnection.MessageTarget.character); } } return; } CharData victim = ch.GetCharRoom(str[0]); if (!victim) { ch.SendText("You don't see them here.\r\n"); return; } if (victim == ch) { ch.SendText("You no longer guard anyone.\r\n"); ((PC)ch).Guarding = null; return; } if (ch.IsClass(CharClass.Names.paladin) && victim.IsEvil()) { ch.SendText("Their blackened soul is hardly worth the effort.\r\n"); return; } if (((PC)ch).Guarding) { SocketConnection.Act("$N&n stops guarding you.", ((PC)ch).Guarding, null, ch, SocketConnection.MessageTarget.character); SocketConnection.Act("You stop guarding $N&n.", ch, null, ((PC)ch).Guarding, SocketConnection.MessageTarget.character); } ((PC)ch).Guarding = victim; SocketConnection.Act("You now guard $N&n.", ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n is now guarding you.", ch, null, victim, SocketConnection.MessageTarget.victim); return; }
/// <summary> /// Train: For monks or mystics, lets them learn skills and traditions. For everyone /// else it tells them they practice by using their skills. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Train(CharData ch, string[] str) { if( ch == null ) return; int count, teacherTradition = 0; string buf; Object icon = null; CharData teacher = null; int skillLevel; if (!ch.IsClass(CharClass.Names.monk) && !ch.IsClass(CharClass.Names.mystic)) { // Send them to the error message. Practice(ch, str); return; } if (ch.IsNPC()) { return; } //find a teacher foreach (CharData iteacher in ch.InRoom.People) { if (iteacher.CharacterClass == ch.CharacterClass && iteacher.HasActionBit(MobTemplate.ACT_TEACHER)) { teacher = iteacher; break; } } // find an icon foreach (Object iconobj in ch.InRoom.Contents) { if (iconobj.ItemType == ObjTemplate.ObjectType.tradition_icon) { icon = iconobj; break; } } // if not in room, check on the teacher if (!icon && teacher) { foreach (Object iconobj in teacher.Carrying) { if (iconobj.ItemType == ObjTemplate.ObjectType.tradition_icon) { icon = iconobj; break; } } } if (icon && teacher) { teacherTradition = icon.Values[0]; } int tradition = ((PC)ch).Tradition; if (str.Length == 0) { if (tradition == 0 && teacherTradition == 0) { // tell em how to train ch.SendText("One trains their skills by first training a tradition.\r\n"); return; } string buf1; if (tradition != teacherTradition && teacherTradition <= TraditionData.Table.Length && teacherTradition != 0) { // they can train a new tradition here if (tradition == 0) { buf1 = String.Format("To train the {0} tradition, type 'train tradition'.\r\n", TraditionData.Names[teacherTradition]); } else { buf1 = String.Format("It will cost you {0} points to train tradition in the {1}.\r\n", ch.Level, TraditionData.Names[teacherTradition]); } ch.SendText(buf1); return; } //show existing monk skills buf1 = "&+WTradition:&n "; if (tradition != 0) { buf = String.Format("&+B{0}&n\r\n", TraditionData.Names[tradition]); } else { buf = String.Format("none\r\n"); } buf1 += buf; buf1 += "\r\n&+WYou can train the following:&n\r\n\r\n"; buf1 += " Skill Level Cost\r\n"; for (count = 0; count < TraditionData.Table.Length; ++count) { if (TraditionData.Table[count].Tradition != tradition) continue; string sn = (TraditionData.Table[count].TraditionName); skillLevel = (((PC)ch).SkillAptitude[sn] + 1) / 20; if (skillLevel < 5 && TraditionData.Table[count].Cost[skillLevel] >= 0) { buf = String.Format(" &n&+c{0} &+Y{1}&n &+r{2}&n\r\n", MUDString.PadStr(Skill.SkillList[sn].Name, 25), MUDString.PadStr(StringConversion.SkillString(((PC)ch).SkillAptitude[sn]), 25), TraditionData.Table[count].Cost[skillLevel]); } else { buf = String.Format(" &n&+c{0} &+Y{1}&n\r\n", MUDString.PadStr(Skill.SkillList[sn].Name, 25), StringConversion.SkillString(((PC)ch).SkillAptitude[sn])); } buf1 += buf; } // end for buf1 += "\r\n"; ch.SendText(buf1); return; } if (!MUDString.StringsNotEqual(str[0], "tradition")) { if (str.Length < 2 || MUDString.StringsNotEqual(str[1], "confirm")) { ch.SendText("To train monk traditions, type 'train tradition confirm'\r\n"); return; } if (!teacher) { ch.SendText("You see no teacher here.\r\n"); return; } if (teacherTradition == 0) { ch.SendText("This teacher needs to be in a monastery to teach.\r\n"); return; } if (teacherTradition >= TraditionData.Table.Length) { ch.SendText("It seems you have found an order so secretive that even we\r\n"); ch.SendText("don't know what they do!\r\n"); return; } if (((PC)ch).Tradition > 0 && ((PC)ch).SkillPoints < ch.Level) { ch.SendText("You do not have enough skill points to change traditions.\r\n"); return; } if (((PC)ch).Tradition > 0) { ((PC)ch).SkillPoints -= ch.Level; } ((PC)ch).Tradition = teacherTradition; buf = "You are now an initiate of the " + TraditionData.Names[teacherTradition] + "\r\n"; ch.SendText(buf); return; } // Now we process arg string again and train the skills. MonkSkill skill = StringLookup.MonkSkillLookup(str[0]); if (skill == null ) { ch.SendText("You can't train that skill here.\r\n"); return; } if (!teacher) { ch.SendText("You see no teacher here.\r\n"); return; } for (count = 0; count < TraditionData.Table.Length; ++count) { if (skill.Name != (TraditionData.Table[count].TraditionName)) continue; if (TraditionData.Table[count].Tradition != ((PC)ch).Tradition) continue; skillLevel = (((PC)ch).MonkAptitude[skill.Name] + 1) / 20; if (skillLevel >= 5) { ch.SendText("You have already mastered that skill.\r\n"); return; } if (TraditionData.Table[count].Cost[skillLevel] < 0) { ch.SendText("You cannot train that skill any higher in this tradition.\r\n"); return; } if (TraditionData.Table[count].Cost[skillLevel] > ((PC)ch).SkillPoints) { ch.SendText("You lack the experience to train this skill further.\r\n"); return; } // Train the skill ((PC)ch).SkillAptitude[skill.Name] += 20; ((PC)ch).SkillPoints -= TraditionData.Table[count].Cost[skillLevel]; while (TraditionData.Table[count].Cost[skillLevel + 1] == 0 && skillLevel < 4) { // bump em up to the next level free of charge ((PC)ch).SkillAptitude[skill.Name] += 20; skillLevel++; } buf = String.Format("&+cYou advance in {0}.&n\r\n", skill.Name); ch.SendText(buf); return; } }
/// <summary> /// Command to cast a spell. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Cast(CharData ch, string[] str) { if( ch == null ) return; if ((ch.IsClass(CharClass.Names.psionicist) || ch.IsClass(CharClass.Names.enslaver)) && !ch.IsImmortal()) { ch.SendText("Psionicists use the WILL command to invoke their powers.\r\n"); return; } if (ch.IsClass(CharClass.Names.bard) && !ch.IsImmortal()) { ch.SendText("Bards use the SING or PLAY commands to invoke their powers.\r\n"); return; } if (ch.Riding && ch.InRoom == ch.Riding.InRoom) { ch.SendText("You cannot cast while mounted!\r\n"); return; } if (ch.IsAffected( Affect.AFFECT_MINOR_PARA) || ch.IsAffected( Affect.AFFECT_HOLD)) { ch.SendText("You can't cast when you're paralyzed!\r\n"); return; } if (str.Length == 0) { ch.SendText("Cast what spell where?\r\n"); return; } Magic.Cast(ch, String.Join(" ", str)); }
/// <summary> /// Shows the list of songs available to the player, if any. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Songs(CharData ch, string[] str) { if( ch == null ) return; string buf = String.Empty; int level; if (ch.IsNPC() || ch.IsClass(CharClass.Names.bard)) { ch.SendText("&nYou do not need any stinking songs!\r\n"); return; } buf += "&n&+rALL songs available for your class.&n\r\n"; buf += "&+RLv Song&n\r\n"; for (level = 1; level < 56; level++) { bool pSong = true; foreach (KeyValuePair<String, Song> kvp in Database.SongList) { if (kvp.Value.SongCircle[(int)ch.CharacterClass.ClassNumber] != level) continue; if (pSong) { buf += "&+Y" + MUDString.PadInt(level, 2) + "&+y:&n"; pSong = false; } else { buf += " "; } buf += " "; buf += "&n&+c" + MUDString.PadStr(kvp.Key, 21) + " &+Y" + ((PC)ch).SongAptitude[kvp.Key] + "&n (" + StringConversion.SpellSchoolString(kvp.Value.PerformanceType) + ")"; buf += "\r\n"; } } ch.SendText(buf); return; }
/// <summary> /// Cant language for thieves only. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Cant(CharData ch, string[] str) { if( ch == null ) return; string buf; if (str.Length == 0) { ch.SendText("Cant what?\r\n"); return; } if (!ch.IsClass(CharClass.Names.thief) && !ch.IsImmortal()) { ch.SendText("You speak gibberish.\r\n"); buf = String.Format("$n says, '{0}'\r\n", NounType.RandomSentence()); SocketConnection.Act(buf, ch, null, null, SocketConnection.MessageTarget.room); return; } string text = String.Join(" ", str); // We don't want to let them know they're drunk. SocketConnection.Act("You cant '&n$T&n'", ch, null, text, SocketConnection.MessageTarget.character); text = DrunkSpeech.MakeDrunk(text, ch); string random = NounType.RandomSentence(); foreach (CharData roomChar in ch.InRoom.People) { if (roomChar == ch || roomChar.IsNPC()) continue; if (roomChar.IsImmortal() || roomChar.IsClass(CharClass.Names.thief)) { buf = String.Format("{0} cants '&n$T&n'", ch.ShowNameTo(roomChar, true)); } else { buf = String.Format("{0} says, '{1}'\r\n", ch.ShowNameTo(roomChar, true), random); } SocketConnection.Act(buf, roomChar, null, SocketConnection.TranslateText(text, ch, roomChar), SocketConnection.MessageTarget.character); } return; }
/// <summary> /// Checks whether spells can be cast in this room. /// </summary> /// <param name="ch">The caster</param> /// <param name="song">Is this a bard song?</param> /// <param name="start">true if just starting to cast, false if finishing up the spell.</param> /// <returns>true if the caster is able to cast spells here.</returns> public bool CheckCastable( CharData ch, bool song, bool start ) { if( !ch.IsClass( CharClass.Names.psionicist ) ) { if( HasFlag( ROOM_SILENT ) ) { ch.SendText( "Your voice makes no sound in this room!\r\n" ); return false; } if( !song && HasFlag( ROOM_NO_MAGIC ) ) { // Extra message and a wait state if this happens when they start casting. if( start ) { ch.SendText( "You start casting..." ); ch.WaitState( 6 ); } ch.SendText( "After a brief gathering of energy, your spell fizzles!\r\n" ); return false; } } else if( HasFlag( ROOM_NO_PSIONICS ) ) { ch.SendText( "Something here prevents you from focusing your will.\r\n" ); ch.WaitState( 2 ); return false; } // No reason they can't cast. return true; }
/// <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; }
public static bool CheckAggressive( CharData ch, CharData victim ) { if( ch == null ) { Log.Error( "CheckAggressive: called with null ch.", 0 ); return false; } if( victim == null ) { Log.Error( "CheckAggressive: called with null victim.", 0 ); return false; } if (!victim.IsAggressive(ch)) { return false; } if( !ch.IsNPC() && ( (PC)ch ).AggressiveLevel >= 1 && ( (PC)ch ).AggressiveLevel < ch.Hitpoints && ch.CurrentPosition == Position.standing && !ch.IsAffected( Affect.AFFECT_CASTING ) ) { ch.SendText( "You charge aggressively at your foe!\r\n" ); if (ch.IsClass(CharClass.Names.thief) || ch.IsClass(CharClass.Names.assassin) || ch.IsClass(CharClass.Names.bard) || ch.IsClass(CharClass.Names.mercenary)) { Backstab(ch, victim); SetFighting(victim, ch); } else { CombatRound(ch, victim, String.Empty); } return true; } return false; }
/// <summary> /// Do one round of attacks for one character. /// Note: This is a round, not a single attack! /// </summary> /// <param name="ch"></param> /// <param name="victim"></param> /// <param name="skill"></param> /// <returns></returns> public static bool CombatRound(CharData ch, CharData victim, string skill) { Object wield; int chance = 0; if( ch == null ) { return false; } if( victim == null ) { 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; } /* I don't know how a dead person can hit someone/be hit. */ if( victim.CurrentPosition == Position.dead || victim.Hitpoints < -10 ) { StopFighting( victim, true ); return true; } /* * Set the fighting fields now. */ if( victim.CurrentPosition > Position.stunned ) { if( !victim.Fighting ) SetFighting( victim, ch ); // Can't have bashed/prone people just automatically be standing. if( victim.CurrentPosition == Position.standing ) victim.CurrentPosition = Position.fighting; } // HORRIBLE HORRIBLE! We've got index numbers hard-coded. TODO: FIXME: BUG: Get rid of this! if( ch.IsNPC() && ch.MobileTemplate != null && ( ch.MobileTemplate.IndexNumber == 9316 || ch.MobileTemplate.IndexNumber == 9748 ) && MUDMath.NumberPercent() < 20 ) { CheckShout( ch, victim ); } ch.BreakInvisibility(); // Everyone gets at least one swing/breath in battle. // This handles breathing, roaring, etc. if (!CheckRaceSpecial(ch, victim, skill)) SingleAttack(ch, victim, skill, ObjTemplate.WearLocation.hand_one); // Thrikreen primary hand extra attack, only thri extra attack that is // given to non-warriors in addition to warriors if( ch.GetRace() == Race.RACE_THRIKREEN && MUDMath.NumberPercent() < ch.Level ) { if( ch.IsClass(CharClass.Names.warrior) ) { switch( MUDMath.NumberRange( 1, 4 ) ) { case 1: if( Object.GetEquipmentOnCharacter( ch, ObjTemplate.WearLocation.hand_one ) ) SingleAttack(ch, victim, skill, ObjTemplate.WearLocation.hand_one); break; case 2: if( Object.GetEquipmentOnCharacter( ch, ObjTemplate.WearLocation.hand_two ) ) SingleAttack(ch, victim, skill, ObjTemplate.WearLocation.hand_two); break; case 3: if( Object.GetEquipmentOnCharacter( ch, ObjTemplate.WearLocation.hand_three ) ) SingleAttack(ch, victim, skill, ObjTemplate.WearLocation.hand_three); break; case 4: if( Object.GetEquipmentOnCharacter( ch, ObjTemplate.WearLocation.hand_four ) ) SingleAttack(ch, victim, skill, ObjTemplate.WearLocation.hand_four); break; } } else { SingleAttack(ch, victim, skill, ObjTemplate.WearLocation.hand_one); if( MUDMath.NumberPercent() < ch.Level / 2 ) { SingleAttack(ch, victim, skill, ObjTemplate.WearLocation.hand_one); } } } // Don't hurt a corpse. if( victim.CurrentPosition == Position.dead || victim.Hitpoints < -10 ) { StopFighting( ch, false ); { return true; } } // For NPCs we assume they have max skill value for their level. // When checking combat skills we only prDescriptor.actice them on a successful // check in order to make them go up slower. If they go up too slow // we can always practice them before they check. chance = ch.GetAttackChance(2); if( MUDMath.NumberPercent() < chance ) { ch.PracticeSkill( "second attack" ); SingleAttack(ch, victim, skill, ObjTemplate.WearLocation.hand_one); if( ch.Fighting != victim ) { return false; } } // Check for Thri-Kreen arm #3 if( ch.GetRace() == Race.RACE_THRIKREEN && ( wield = Object.GetEquipmentOnCharacter( ch, ObjTemplate.WearLocation.hand_three ) ) ) { if( wield.HasWearFlag( ObjTemplate.WEARABLE_WIELD ) ) { if( ch.IsNPC() ) { if( !ch.HasSkill( "second attack" )) { chance = ch.Level / 5; // Up to 10% chance of third arm for psis and } // other miscellaneous thris else { chance = ((ch.Level - Skill.SkillList["second attack"].ClassAvailability[(int)ch.CharacterClass.ClassNumber]) * 2 + 25); } } else { if (((PC)ch).SkillAptitude.ContainsKey("second attack")) { chance = ((PC)ch).SkillAptitude["second attack"]; } else { chance = 0; } } if( chance > 95 ) chance = 95; if( MUDMath.NumberPercent() < chance ) { ch.PracticeSkill( "second attack" ); SingleAttack(ch, victim, skill, ObjTemplate.WearLocation.hand_three); if( ch.Fighting != victim ) { return false; } } } } chance = ch.GetAttackChance(3); if( MUDMath.NumberPercent() < chance ) { ch.PracticeSkill( "third attack" ); SingleAttack(ch, victim, skill, ObjTemplate.WearLocation.hand_one); if( ch.Fighting != victim ) { return false; } } chance = ch.GetAttackChance(4); if( MUDMath.NumberPercent() < chance ) { ch.PracticeSkill( "fourth attack" ); SingleAttack(ch, victim, skill, ObjTemplate.WearLocation.hand_one); if( ch.Fighting != victim ) { return false; } } // Check for dual wield. May want to allow a second swing when dual wielding. // We'll wait and see what combat looks like before we decide - Xangis wield = Object.GetEquipmentOnCharacter( ch, ObjTemplate.WearLocation.hand_two ); if( wield ) { if( wield.HasWearFlag( ObjTemplate.WEARABLE_WIELD ) ) { ch.PracticeSkill( "dual wield" ); if (ch.IsNPC()) { chance = ch.Level; } else { if (((PC)ch).SkillAptitude.ContainsKey("dual wield")) { chance = ((PC)ch).SkillAptitude["dual wield"] * 2 / 3; } else { chance = 0; } } chance += ch.IsClass(CharClass.Names.ranger) ? 10 : 0; if( MUDMath.NumberPercent() < chance ) { SingleAttack(ch, victim, skill, ObjTemplate.WearLocation.hand_two); } } if( ch.Fighting != victim ) { return false; } } // Check for fourth arm on thrikreen if( ch.GetRace() == Race.RACE_THRIKREEN && ( wield = Object.GetEquipmentOnCharacter( ch, ObjTemplate.WearLocation.hand_four ) ) ) { if( wield.HasWearFlag( ObjTemplate.WEARABLE_WIELD ) ) { ch.PracticeSkill( "dual wield" ); chance = ch.IsNPC() ? ( ch.Level * 3 / 2 + 20 ) : ( (PC)ch ).SkillAptitude[ "dual wield" ]; if( chance > 95 ) { chance = 95; } if( MUDMath.NumberPercent() < chance ) { SingleAttack(ch, victim, skill, ObjTemplate.WearLocation.hand_four); } } if( ch.Fighting != victim ) { return false; } } // Don't hurt a corpse. if( victim.CurrentPosition == Position.dead || victim.Hitpoints < -10 ) { StopFighting( ch, false ); return true; } return false; }
/// <summary> /// Processes a blur-type attack. Returns true if the victim died. /// </summary> /// <param name="ch"></param> /// <param name="victim"></param> /// <returns></returns> public static bool BlurAttack( CharData ch, CharData victim ) { if (ch == null) return false; int numAttacks; int count; if( ch.IsAffected( Affect.AFFECT_CASTING ) ) { return false; } if( ch.IsAffected( Affect.AFFECT_BLUR ) && MUDMath.NumberPercent() < 25 ) { SocketConnection.Act( "$n&n moves with a BLUR of speed!", ch, null, null, SocketConnection.MessageTarget.room ); SocketConnection.Act( "You move with a BLUR of speed!", ch, null, null, SocketConnection.MessageTarget.character ); for( count = 0, numAttacks = 4; count < numAttacks && victim.CurrentPosition > Position.dead; ++count ) { SingleAttack( ch, victim, String.Empty, ObjTemplate.WearLocation.hand_one ); } } else { Object wield; if( MUDMath.NumberPercent() > 10 ) return false; if( ch.IsClass(CharClass.Names.hunter) || ch.IsClass(CharClass.Names.ranger )) numAttacks = 2; else if( ch.GetRace() == Race.RACE_OGRE || ch.GetRace() == Race.RACE_CENTAUR ) numAttacks = 4; else numAttacks = 9; if( MUDMath.NumberPercent() < ch.GetCurrLuck() ) numAttacks++; if( MUDMath.NumberPercent() < victim.GetCurrLuck() ) numAttacks--; /* 9716 is the index number for the dagger of the wind. */ // HORRIBLE HORRIBLE TODO: FIXME: BUG: Never hard-code item index numbers. if( ( wield = Object.GetEquipmentOnCharacter( ch, ObjTemplate.WearLocation.hand_one ) ) && wield.ObjIndexData.IndexNumber == 9716 ) { SocketConnection.Act( "&+c$n&+c's $p&n &+cbegins to move with the &+Wspeed&+c of a &+lstorm&+c!&n", ch, wield, null, SocketConnection.MessageTarget.room ); SocketConnection.Act( "Your $p&n &+cbegins to move with the &+Wspeed&+c of a &+lstorm&+c!&n", ch, wield, null, SocketConnection.MessageTarget.character ); for( count = 0; count < numAttacks && victim.CurrentPosition > Position.dead; ++count ) { SingleAttack(ch, victim, String.Empty, ObjTemplate.WearLocation.hand_one); } return ( victim.CurrentPosition > Position.dead ); } if( ( wield = Object.GetEquipmentOnCharacter( ch, ObjTemplate.WearLocation.hand_two ) ) && wield.ObjIndexData.IndexNumber == 9716 ) { SocketConnection.Act( "&+c$n&+c's $p&n &+cbegins to move with the &+Wspeed&+c of a &+lstorm&+c!&n", ch, wield, null, SocketConnection.MessageTarget.room ); SocketConnection.Act( "Your $p&n &+cbegins to move with the &+Wspeed&+c of a &+lstorm&+c!&n", ch, wield, null, SocketConnection.MessageTarget.character ); for( count = 0; count < numAttacks && victim.CurrentPosition > Position.dead; ++count ) { SingleAttack(ch, victim, String.Empty, ObjTemplate.WearLocation.hand_two); } return ( victim.CurrentPosition > Position.dead ); } if( ( wield = Object.GetEquipmentOnCharacter( ch, ObjTemplate.WearLocation.hand_three ) ) && wield.ObjIndexData.IndexNumber == 9716 ) { SocketConnection.Act( "&+c$n&+c's $p&n &+cbegins to move with the &+Wspeed&+c of a &+lstorm&+c!&n", ch, wield, null, SocketConnection.MessageTarget.room ); SocketConnection.Act( "Your $p&n &+cbegins to move with the &+Wspeed&+c of a &+lstorm&+c!&n", ch, wield, null, SocketConnection.MessageTarget.character ); for( count = 0; count < numAttacks && victim.CurrentPosition > Position.dead; ++count ) { SingleAttack(ch, victim, String.Empty, ObjTemplate.WearLocation.hand_three); } return ( victim.CurrentPosition > Position.dead ); } if( ( wield = Object.GetEquipmentOnCharacter( ch, ObjTemplate.WearLocation.hand_four ) ) && wield.ObjIndexData.IndexNumber == 9716 ) { SocketConnection.Act( "&+c$n&+c's $p&n &+cbegins to move with the &+Wspeed&+c of a &+lstorm&+c!&n", ch, wield, null, SocketConnection.MessageTarget.room ); SocketConnection.Act( "Your $p&n &+cbegins to move with the &+Wspeed&+c of a &+lstorm&+c!&n", ch, wield, null, SocketConnection.MessageTarget.character ); for( count = 0; count < numAttacks && victim.CurrentPosition > Position.dead; ++count ) { SingleAttack(ch, victim, String.Empty, ObjTemplate.WearLocation.hand_four); } return ( victim.CurrentPosition > Position.dead ); } } return false; }
/// <summary> /// The backstab function was cloned from Command.Backstab for use by mobile AI code. /// the difference is that a mob that wants to backstab already knows their /// victim, so no need for the argument stuff /// /// Modified by to allow auto-stab for backstabbers with aggr_level set. Called /// from Commandbackstab() /// /// Added ability to stab if piercer is secondary. Useful for mercs. /// /// Reduced damage for bards and mercs. /// </summary> /// <param name="ch"></param> /// <param name="victim"></param> /// <returns></returns> public static bool Backstab(CharData ch, CharData victim) { Object obj; Object obj2; int chance = 0; int stabChance; // Can't stab if lacking the skill if (!ch.HasSkill("backstab")) { ch.SendText("Leave backstabbing to the assassins!\r\n"); return false; } if (victim == null) { ch.SendText("Pick a target!\r\n"); return false; } // Can't stab if blind if (ch.IsBlind()) return false; // Can't stab on horseback if (ch.Riding) { ch.SendText("You can't get close enough while mounted.\r\n"); return false; } // Can't stab yourself if (victim == ch) return false; 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) return false; /* Check size of ch vs. victim. */ if (victim.CurrentPosition > Position.sleeping) { /* If ch is more than 2 sizes smaller it's too small. */ if ((ch.CurrentSize - 2) > victim.CurrentSize && victim.CurrentPosition >= Position.stunned && !(ch.IsNPC() && ch.MobileTemplate.IndexNumber == 10165)) { ch.SendText("Such tiny beings evade your skills.\r\n"); return false; } /* Ch 2 or more sizes larger than victim => bad! */ if ((ch.CurrentSize + 2) < victim.CurrentSize && victim.CurrentPosition >= Position.stunned) { ch.SendText("It is rather ineffective to stab someone in the calf.\r\n"); return false; } } ObjTemplate.WearLocation hand = 0; if ((obj = Object.GetEquipmentOnCharacter(ch, ObjTemplate.WearLocation.hand_one)) && obj.ItemType == ObjTemplate.ObjectType.weapon && (AttackType.Table[obj.Values[3]].SkillName) == "1h piercing") hand = ObjTemplate.WearLocation.hand_one; if (hand == 0 && (obj2 = Object.GetEquipmentOnCharacter(ch, ObjTemplate.WearLocation.hand_two)) && obj2.ItemType == ObjTemplate.ObjectType.weapon && (AttackType.Table[obj2.Values[3]].SkillName) == "1h piercing") hand = ObjTemplate.WearLocation.hand_two; if (hand == 0) { ch.SendText("You need to wield a piercing weapon.\r\n"); return false; } if (!ch.IsNPC()) ch.PracticeSkill("backstab"); Crime.CheckAttemptedMurder(ch, victim); ch.WaitState(Skill.SkillList["backstab"].Delay); if (ch.IsNPC()) { stabChance = 2 * ch.Level; } else { stabChance = ((PC)ch).SkillAptitude["backstab"]; } if (Math.Abs(ch.CurrentSize - victim.CurrentSize) == 2) stabChance -= 10; switch (victim.CurrentPosition) { default: break; case Position.mortally_wounded: case Position.incapacitated: case Position.unconscious: stabChance += 80; break; case Position.stunned: case Position.sleeping: stabChance += 40; break; case Position.reclining: stabChance += 20; break; case Position.resting: case Position.sitting: stabChance += 10; break; } //end switch // Half as likely to succeed on those that are aware if (victim.IsAffected(Affect.AFFECT_AWARE) || victim.IsAffected( Affect.AFFECT_SKL_AWARE)) { chance /= 2; } string lbuf = String.Format("Command.Backstab: {0} is attempting with a {1}%% chance.", ch.Name, stabChance); ImmortalChat.SendImmortalChat(null, ImmortalChat.IMMTALK_SPAM, 0, lbuf); if (MUDMath.NumberPercent() < stabChance) { /* First hit on backstab. Check for instant kill. - Xangis */ if (ch.HasSkill("instant kill")) { if (!ch.IsNPC()) chance = ((PC)ch).SkillAptitude["instant kill"]; else chance = (ch.Level * 3) / 2 + 20; // People over level 50 get a bonus, equates to about 1-2% if (ch.Level > 50) chance += 25; chance += (ch.Level - victim.Level); // Immortals will get a bonus too if (ch.IsImmortal()) chance *= 4; // Half as likely to succeed on those that are aware if (victim.IsAffected(Affect.AFFECT_AWARE) || victim.IsAffected(Affect.AFFECT_SKL_AWARE)) { chance /= 2; } if (MUDMath.NumberRange(1, 20000) < chance) { if (!ch.IsNPC()) ch.PracticeSkill("instant kill"); { lbuf = String.Format("backstab: {0} hit an instakill on {1} with a {2} chance in hundredths of a percent.", ch.Name, victim.Name, (chance / 30)); ImmortalChat.SendImmortalChat(null, ImmortalChat.IMMTALK_SPAM, 0, lbuf); Log.Trace(lbuf); ch.WaitState(15); SingleAttack(ch, victim, "instant kill", hand); return true; } } } SingleAttack(ch, victim, "backstab", hand); /* No double stabs when the first stab kills'm. */ if (victim.CurrentPosition == Position.dead) return true; /* Case of thieves/assassins doing a double backstab. */ obj = Object.GetEquipmentOnCharacter(ch, ObjTemplate.WearLocation.hand_two); /* Stop 2nd-hand non-pierce backstabs. */ if (!ch.IsNPC() && (obj != null) && (hand != ObjTemplate.WearLocation.hand_two) && obj.ItemType == ObjTemplate.ObjectType.weapon && (AttackType.Table[obj.Values[3]].SkillName) == "1h piercing") { /* Thieves get 1/2 chance at double, assassins get 2/3. */ // Xangis - removed double stab for thieves 6-9-00 if ((ch.IsClass(CharClass.Names.assassin)) && (MUDMath.NumberPercent() < ((PC)ch).SkillAptitude["backstab"] * 2 / 3)) { lbuf = String.Format("backstab: {0} hit a double backstab.", ch.Name); ImmortalChat.SendImmortalChat(null, ImmortalChat.IMMTALK_SPAM, 0, lbuf); SingleAttack(ch, victim, "backstab", ObjTemplate.WearLocation.hand_two); } } } else /* Send a "you miss your backstab" messge & engage. */ { InflictDamage(ch, victim, 0, "backstab", ObjTemplate.WearLocation.hand_one, AttackType.DamageType.pierce); } return true; }
/// <summary> /// Hit one guy once. /// /// Hitroll is now done on a 200-sided die rather than a 20-sided die /// This allows for more dynamic modifiers to hitroll. /// i.e. a couple extra points of strength and whatnot _may_ make the /// difference between a hit and a miss rather than incrementing something /// every 10-20 points of an ability we can modify it every 1-2 points. /// </summary> /// <param name="ch">attacker</param> /// <param name="victim">person being attacked</param> /// <param name="skill">damage type being used (skill)</param> /// <param name="weapon">wear location of weapon (usually primary or secondary hand)</param> /// <returns>true if victim is killed, otherwise false</returns> public static bool SingleAttack(CharData ch, CharData victim, string skill, ObjTemplate.WearLocation weapon) { string text; int dam; int chance; /* * Can't beat a dead char! * Guard against weird room-leavings. */ if( victim.CurrentPosition == Position.dead || victim.Hitpoints < -10 ) { text = String.Format("SingleAttack: ch {0} fighting dead victim {1}.", ch.Name, victim.Name ); Log.Error( text, 0 ); ch.Fighting = null; if( ch.CurrentPosition == Position.fighting ) ch.CurrentPosition = Position.standing; return true; } if( ch.InRoom != victim.InRoom ) { text = String.Format("SingleAttack: ch {0} not with victim {1}.", ch.Name, victim.Name ); Log.Error( text, 0 ); ch.Fighting = null; if( ch.CurrentPosition == Position.fighting ) ch.CurrentPosition = Position.standing; return false; } /* No casting/being para'd and hitting at the same time. */ if ((ch.IsAffected(Affect.AFFECT_CASTING)) || ch.IsAffected(Affect.AFFECT_MINOR_PARA) || ch.IsAffected(Affect.AFFECT_HOLD)) { return false; } // Inertial barrier will prevent some attacks. At the following levels a person // affected by inertial barrier will be able to avoid this percentage of attacks: // 1 = 7% 5 = 10% 10 = 13% 20 = 20% 30 = 27% 40 = 33% 50 = 39% 51 = 40% if (victim.IsAffected(Affect.AFFECT_INERTIAL_BARRIER) && MUDMath.NumberPercent() > (victim.Level * 2 / 3 + 7)) return false; // Keep in mind that CheckRiposte returns a boolean. if (skill != "kick" && skill != "backstab" && skill != "circle" && CheckRiposte( ch, victim ) ) { SingleAttack( victim, ch, String.Empty, ObjTemplate.WearLocation.hand_one ); return false; } if (CheckParry(ch, victim) && skill != "backstab" && skill != "circle") return false; if (CheckShieldBlock(ch, victim) && skill != "backstab" && skill != "circle") return false; if (CheckDodge(ch, victim) && skill != "backstab" && skill != "circle") return false; /* * Figure out the type of damage message if we don't have an associated attack skill. */ Object wield = Object.GetEquipmentOnCharacter( ch, weapon ); if (String.IsNullOrEmpty(skill)) { skill = "barehanded fighting"; if( wield && wield.ItemType == ObjTemplate.ObjectType.weapon ) skill = AttackType.Table[wield.Values[3]].SkillName; } /* * Weapon proficiencies. */ string weaponGsn = "barehanded fighting"; AttackType.DamageType damType = AttackType.DamageType.bludgeon; if( wield && wield.ItemType == ObjTemplate.ObjectType.weapon ) { if( wield.Values[ 3 ] >= 0 && wield.Values[ 3 ] < AttackType.Table.Length ) { weaponGsn = (AttackType.Table[wield.Values[3]].SkillName); damType = AttackType.Table[ wield.Values[ 3 ] ].DamageInflicted; } else { text = String.Format( "SingleAttack: bad weapon damage type {0} caused by {1} ({2}).", skill, wield.Name, wield.ObjIndexData ? wield.ObjIndexData.IndexNumber : -1 ); Log.Error( text, 0 ); wield.Values[ 3 ] = 0; } } /* * Calculate to-hit-armor-class-0 versus armor. */ int hitroll00 = ch.CharacterClass.HitrollLevel0; int hitroll40 = ch.CharacterClass.HitrollLevel40; /* Weapon-specific hitroll and damroll */ int hitroll = MUDMath.Interpolate( ch.Level, hitroll00, hitroll40 ) - ( ch.GetHitroll( weapon ) * 3 ); int victimAC = Math.Max( -100, victim.GetAC() ); // Added blindfighting skill - Xangis if( !CharData.CanSee( ch, victim ) ) { if( ch.CheckSkill( "blindfighting" ) ) { victimAC -= 5; } else { victimAC -= 40; } } /* Weapon proficiencies * * * The twohanded version of a weapon proficiency *MUST* follow the onehanded * version in the definitions. This is stupid. */ if( wield && wield.ItemType == ObjTemplate.ObjectType.weapon ) { if( !wield.HasFlag( ObjTemplate.ITEM_TWOHANDED ) ) { if( ch.CheckSkill( weaponGsn )) { victimAC += 20; ch.PracticeSkill( weaponGsn ); } } else { // This is not going to work. if (ch.CheckSkill(weaponGsn+1)) { victimAC += 20; ch.PracticeSkill(weaponGsn); } } } else if( ch.CheckSkill("barehanded fighting")) { victimAC += 20; } /* * The moment of excitement! */ int diceroll = MUDMath.NumberRange( 0, 199 ); // Give them a small bonus if they can make a successful luck check. if( MUDMath.NumberPercent() <= ch.GetCurrLuck() ) diceroll += 5; /* Made really lucky chars get saved by the godz. */ if( diceroll == 0 || ( diceroll <= 196 && diceroll < hitroll - victimAC ) || ( MUDMath.NumberPercent() < victim.GetCurrLuck() / 40 ) ) { /* Miss. */ return InflictDamage(ch, victim, 0, skill, weapon, damType); } /* * Hit. * Calc damage. * * NPCs are more badass barehanded than players. If they weren't * the game would be too damned easy since mobs almost never have * weapons. * * Increased mob damage by about 1/6 * It was previously level/2 to level*3/2 (25-75 at 50, average 50) * It is now level*3/5 to level*10/6 (30-87 at 50, average 59) * * Added the + ch.level - 1 'cause mobs still not hittin' hard enough */ if( ch.IsNPC() ) { dam = MUDMath.NumberRange( ( ch.Level * 3 / 5 ), ( ch.Level * 14 / 8 ) ) + ( ch.Level - 1 ); if (wield) { dam += MUDMath.Dice(wield.Values[1], wield.Values[2]); } else if (ch.CheckSkill("unarmed damage")) { dam += MUDMath.NumberRange(1, (ch.GetSkillChance("unarmed damage") / 12)); } } else { if (wield) { dam = MUDMath.Dice(wield.Values[1], wield.Values[2]); } else { if (!ch.IsClass(CharClass.Names.monk)) { dam = MUDMath.NumberRange(1, (2 + (int)ch.CurrentSize / 3)); if (ch.CheckSkill("unarmed damage")) { dam += MUDMath.NumberRange(1, (ch.GetSkillChance("unarmed damage") / 12)); } } else { int min; // monk barehanded damage - Xangis ch.PracticeSkill("unarmed damage"); chance = ch.GetSkillChance("unarmed damage"); if (chance < 13) { min = 1; } else { min = chance / 13; } // at max skill for barehanded and unarmed, a monk will get // a damage of 7-38, an average of 22.5 damage per hit before // modifiers. This is slightly better than a 6d6 weapon (average of 21 dmg) // this is slightly worse than a 6d7 weapon (average of 24 dmg) dam = MUDMath.NumberRange(min, ((chance / 3) + min)); } } if( ( wield && dam > 1000 ) && ch.Level < Limits.LEVEL_AVATAR ) { text = String.Format( "SingleAttack damage range > 1000 from {0} to {1}", wield.Values[ 1 ], wield.Values[ 2 ] ); Log.Error( text, 0 ); } } /* * Played a character with an armor class of 126 (awful agility). * Wasn't getting pounded much at all. Added a damage bonus applied * when the target's ac is worse than 100. * * This also means that someone who makes their weapon proficiency * check against someone with an ac of 81 or higher will also get a * damage bonus of 1% per ac point. * * This applies to mobs too, so if a mob has a terrible AC it will * get whacked harder. I call this the "soft as a pudding" code. * * This would also make AC debuffs stronger if they can make ac worse * than 100. */ if( victimAC > 100 ) { dam += ( (victimAC - 100) * dam) / 100; } /* * Bonuses. */ dam += ch.GetDamroll( weapon ); /* Weapon proficiencies, players only */ /* Up to 50% increase based on weapon skill */ if (wield && !ch.IsNPC()) { dam += dam * ch.GetSkillChance(weaponGsn) / 180; } /* Up to 33% for offense skill */ /* This means someone that has mastered a weapon and offense automatically does double damage in combat */ chance = ch.GetSkillChance("offense"); dam += dam * chance / 270; /* Bad idea to get caught napping in a fight */ if( !victim.IsAwake() ) dam *= 2; /* Backstab: 2 + one per 9 levels, 7x damage at 50 */ if (skill == "backstab") { // Cap was previously too low. It has been raised because a merc that was previously // stabbing for 180 now stabs for 64. Assassins will still be able to stab for // 175 and mercs for 116 with this revised cap. Keep in mind that a sorc can easily // fist for 250. int cap = 100 + 12 * ch.Level; if( ch.IsClass(CharClass.Names.mercenary) || ch.IsClass(CharClass.Names.bard )) cap = cap * 2 / 3; dam *= ( 2 + ( ch.Level / 9 ) ); /* damage cap applied here */ dam = Math.Min( dam, cap ); } else if (skill == "circle") /* 150% to 200% at lev. 50 */ dam += dam / 2 + ( dam * ch.Level ) / 100; if( dam <= 0 ) dam = 1; return InflictDamage(ch, victim, dam, skill, weapon, damType); }
/// <summary> /// Shows a character's score screen. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Score(CharData ch, string[] str) { if( ch == null ) return; string text = String.Empty; if (ch == null) { Log.Error("Command.Score(): null ch.", 0); return; } Affect prev; text += "&+WName: &+G" + ch.Name + "&n" + (ch.IsNPC() ? String.Empty : ((PC)ch).Title) + "\r\n"; text += "&+WRace:&n " + MUDString.PadStr(Race.RaceList[ch.GetRace()].ColorName, 16); text += "&+WClass:&n " + MUDString.PadStr(ch.CharacterClass.WholistName, 32) + "&n\r\n"; text += "&+WLevel: " + MUDString.PadInt(ch.Level, 5) + " Played: " + (ch.TimePlayed.Hours) + " hours &+WSex: "; text += System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(ch.GetSexString()) + "\r\n"; if (ch.Fighting == null) { text += "&+WExperience: &+B" + StringConversion.ExperienceString(ch) + "&n\r\n\r\n"; } text += "&+WCurrent/Max Health: [&n&+g" + MUDString.PadInt(ch.Hitpoints, 5) + "&+W / &n&+g" + MUDString.PadInt(ch.GetMaxHit(), 5); text += "&+W] Coins: Carried In Bank\r\n"; text += "&+WCurrent/Max Moves: [&n&+g" + MUDString.PadInt(ch.CurrentMoves, 5) + "&+W / &n&+g" + MUDString.PadInt(ch.MaxMoves, 5); text += "&+W] &+WPlatinum " + MUDString.PadInt(ch.GetPlatinum(), 5) + " "; text += (ch.IsNPC() ? 0 : ((PC)ch).Bank.Platinum) + "\r\n"; text += "Current/Max Mana: [&n&+g" + MUDString.PadInt(ch.CurrentMana, 5) + "&+W / &n&+g" + MUDString.PadInt(ch.MaxMana, 5); text += "&+W] &+YGold " + MUDString.PadInt(ch.GetGold(), 5) + " " + (ch.IsNPC() ? 0 : ((PC)ch).Bank.Gold) + "\r\n"; text += " &n&+wSilver " + MUDString.PadInt(ch.GetSilver(), 5) + " "; text += (ch.IsNPC() ? 0 : ((PC)ch).Bank.Silver) + "\r\n"; text += "&+WFrags: &+W" + MUDString.PadInt((ch.IsNPC() ? 0 : ((PC)ch).Frags), 3) + "&n &n&+yCopper "; text += MUDString.PadInt(ch.GetCopper(), 5) + " " + (ch.IsNPC() ? 0 : ((PC)ch).Bank.Copper) + "\r\n"; if (!ch.IsNPC()) { text += "&+WTotal Deaths: &+W" + MUDString.PadInt(((PC)ch).MobDeaths + ((PC)ch).PlayerDeaths, 5) + "&n &+WMobs Killed: &+W"; text += MUDString.PadInt(((PC)ch).MobKills, 5) + "&n\r\n&+WPlayers Killed: &+W" + MUDString.PadInt(((PC)ch).PlayerKills, 5); text += "&n &+WPlayer Deaths: &+W" + MUDString.PadInt(((PC)ch).PlayerDeaths, 5) + "&n\r\n"; } if (!ch.IsNPC()) { int divisor = ((PC)ch).Created.Quantity; if (divisor == 0) divisor = 1; text += String.Format("&+WItems Created: &n{0} &+WTotal Value: &n{1} &+WBest: &n{2} &+WAvg: &n{3}\r\n", MUDString.PadInt(((PC)ch).Created.Quantity, 5), MUDString.PadInt(((PC)ch).Created.TotalCost, 5), MUDString.PadInt(((PC)ch).Created.MaxCost, 5), MUDString.PadInt((((PC)ch).Created.TotalCost / divisor), 5)); divisor = ((PC)ch).Destroyed.Quantity; if (divisor == 0) divisor = 1; text += String.Format("&+WItems Destroyed: &n{0} &+WTotal Value: &n{1} &+WBest: &n{2} &+WAvg: &n{3}\r\n", MUDString.PadInt(((PC)ch).Destroyed.Quantity, 5), MUDString.PadInt(((PC)ch).Destroyed.TotalCost, 5), MUDString.PadInt(((PC)ch).Destroyed.MaxCost, 5), MUDString.PadInt((((PC)ch).Destroyed.TotalCost / divisor), 5)); } if (!ch.IsNPC()) { text += "&+WTotal Score: &+W" + ((PC)ch).Score + "&n\r\n"; } if (ch.IsClass(CharClass.Names.monk) || ch.IsClass(CharClass.Names.mystic)) { text += "&+WTradition: &+B" + TraditionData.Names[((PC)ch).Tradition] + "&n\r\n"; text += "&+WTraining Points: &+B" + (ch.IsNPC() ? 0 : ((PC)ch).SkillPoints) + "&n\r\n"; } if (ch.Followers != null && ch.Followers.Count > 0) { text += "&+BFollowers:&n\r\n"; foreach (CharData follower in ch.Followers) { if (follower == null) { continue; } text += follower.ShowNameTo(ch, true) + " &n\r\n"; } text += "\r\n"; } if (ch.IsAffected( Affect.AFFECT_POISON)) { text += "&+GYou are poisoned.&n\r\n"; } if ((ch.IsAffected( Affect.AFFECT_DETECT_MAGIC) || ch.IsImmortal()) && MUDString.StringsNotEqual(BitvectorFlagType.AffectString(ch.AffectedBy, true), "none")) { text += "&+BEnchantments: &+W" + BitvectorFlagType.AffectString(ch.AffectedBy, true) + "&n\r\n\r\n"; } if (ch.Affected != null) { bool printed = false; prev = null; foreach (Affect affect in ch.Affected) { if (!printed) { text += "&+BActive Spells:&+W\r\n"; printed = true; } /* Show only new affects to mortals. */ if (prev != null && prev.Value == affect.Value && prev.Type == affect.Type && !ch.IsImmortal()) { prev = affect; continue; } prev = affect; if (affect.Type == Affect.AffectType.skill && !String.IsNullOrEmpty(affect.Value) && ch.IsImmortal()) { text += MUDString.CapitalizeANSIString( Skill.SkillList[affect.Value].Name ); } else if (affect.Type == Affect.AffectType.skill && !String.IsNullOrEmpty(affect.Value)) { continue; } else if (affect.Type == Affect.AffectType.spell && !String.IsNullOrEmpty(affect.Value)) { text += MUDString.CapitalizeANSIString(Spell.SpellList[affect.Value].Name); } else if (affect.Type == Affect.AffectType.song && !String.IsNullOrEmpty(affect.Value)) { text += MUDString.CapitalizeANSIString(affect.Value); } else { text += "Something"; } if (ch.IsImmortal()) { foreach (AffectApplyType apply in affect.Modifiers) { text += " modifies " + StringConversion.AffectApplyString(apply.Location) + " by " + apply.Amount; } text += " for " + affect.Duration + " hours with bits " + affect.AffectString(false) + ".\r\n"; } else { if (affect.Duration == 0 && ch.IsAffected( Affect.AFFECT_DETECT_MAGIC)) { text += " (fading rapidly)\r\n"; } else if (affect.Duration == 1 && ch.IsAffected( Affect.AFFECT_DETECT_MAGIC)) { text += " (fading)\r\n"; } else { text += "\r\n"; } } } } text += "&n"; ch.SendText(text); return; }
/// <summary> /// Code to check if someone just fragged. /// Will also have to add to race, class, and guild frag tables in /// addition to the master frag table. This does not update any /// lists yet and instead only updates the totals. /// </summary> /// <param name="ch"></param> /// <param name="victim"></param> public static void CheckForFrag( CharData ch, CharData victim ) { // NPC's don't participate in fragging, can't frag yourself, // have to be within 10 levels, no same side frags, no frags // from races not participating in racewars, have to be level // 20 to frag, and have to be a valid class. // Check to see if kill qualifies for a frag. if( ch.IsNPC() ) return; if( victim.IsNPC() ) return; if( ch == victim ) return; if( ch.GetRacewarSide() == Race.RacewarSide.neutral ) return; if( !ch.IsRacewar( victim ) ) return; if( ch.IsImmortal() ) return; if( victim.IsImmortal() ) return; if( victim.Level < 20 ) return; if( ch.Level < 20 ) return; if( ( ch.Level - 10 ) > victim.Level ) return; if (ch.IsClass(CharClass.Names.none) || victim.IsClass(CharClass.Names.none)) return; if( victim.GetOrigRace() > Limits.MAX_PC_RACE ) return; // Give frag to ch. ( (PC)ch ).Frags++; // Protect against polymorphed character race frags. if( ch.GetOrigRace() < Limits.MAX_PC_RACE ) { _fraglist._totalFragsByRaceAndClass[ch.GetOrigRace()][ (int)ch.CharacterClass.ClassNumber]++; _fraglist._totalFragsBySide[ (int)ch.GetRacewarSide() ]++; } if( ( (PC)ch ).GuildMembership != null ) ( (PC)ch ).GuildMembership.Frags++; // Take frag from victim. ( (PC)victim ).Frags--; // Protect against polymorphed character race frags if( victim.GetOrigRace() < Limits.MAX_PC_RACE ) { _fraglist._totalFragsByRaceAndClass[victim.GetOrigRace()][ (int)victim.CharacterClass.ClassNumber]--; _fraglist._totalFragsBySide[ (int)victim.GetRacewarSide() ]--; } if (((PC)victim).GuildMembership != null) { ((PC)victim).GuildMembership.Frags--; } ch.SendText( "&+WYou gain a frag!&n\r\n" ); victim.SendText( "&+WYou lose a frag!&n\r\n" ); string text = ch.Name + " has fragged " + victim.Name + " in room " + ch.InRoom.IndexNumber + "."; ImmortalChat.SendImmortalChat( ch, ImmortalChat.IMMTALK_DEATHS, Limits.LEVEL_AVATAR, text ); Log.Trace( text ); // Check to see if either person goes up or down on their particular lists. if( ( (PC)ch ).Frags > 0 ) { SortFraglist( ch, _fraglist._topFrags ); SortFraglist( ch, _fraglist._topRaceFrags[ ch.GetOrigRace() ] ); SortFraglist(ch, _fraglist._topClassFrags[(int)ch.CharacterClass.ClassNumber]); } else if( ( (PC)ch ).Frags < 0 ) { SortFraglist( ch, _fraglist._bottomFrags ); SortFraglist( ch, _fraglist._bottomRaceFrags[ ch.GetOrigRace() ] ); SortFraglist(ch, _fraglist._bottomClassFrags[(int)ch.CharacterClass.ClassNumber]); } if( ( (PC)victim ).Frags > 0 ) { SortFraglist( victim, _fraglist._topFrags ); SortFraglist( victim, _fraglist._topRaceFrags[ victim.GetOrigRace() ] ); SortFraglist(victim, _fraglist._topClassFrags[(int)victim.CharacterClass.ClassNumber]); } else if( ( (PC)victim ).Frags < 0 ) { SortFraglist( victim, _fraglist._bottomFrags ); SortFraglist( victim, _fraglist._bottomRaceFrags[ victim.GetOrigRace() ] ); SortFraglist(victim, _fraglist._bottomClassFrags[(int)victim.CharacterClass.ClassNumber]); } _fraglist.Save(); return; }
/// <summary> /// Sing -- using a bard song without an instrument. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Sing(CharData ch, string[] str) { if( ch == null ) return; if (!ch.IsClass(CharClass.Names.bard) && !ch.IsImmortal()) { ch.SendText("You don't know how to sing.\r\n"); return; } if (ch.IsAffected(Affect.AFFECT_MINOR_PARA) || ch.IsAffected(Affect.AFFECT_HOLD)) { ch.SendText("You can't Sing when you're paralyzed!\r\n"); return; } if (ch.HasActionBit(Affect.AFFECT_MUTE)) { ch.SendText("You have no voice!\r\n"); return; } if (str.Length == 0) { if (ch.IsAffected(Affect.AFFECT_SINGING)) { ch.RemoveAffect(Affect.AFFECT_SINGING); ch.SendText("You stop singing.\r\n"); } else { ch.SendText("Sing what?\r\n"); } return; } Magic.Cast(ch, String.Join(" ", str)); }
/// <summary> /// Innate command. Shows and activates innate abilitiies. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Innate(CharData ch, string[] str) { if( ch == null ) return; string text; Affect af = new Affect(); // Use innates here if (str.Length > 0 && !String.IsNullOrEmpty(str[0])) { if (!MUDString.IsPrefixOf(str[0], "strength")) { if (ch.HasInnate(Race.RACE_STRENGTH)) { if (ch.IsAffected(Affect.AFFECT_STRENGTH_INCREASED)) { ch.SendText("You are already affected by strength."); return; } if (ch.HasInnateTimer(InnateTimerData.Type.strength)) { ch.SendText("You need to rest a bit first.\r\n"); return; } af.Type = Affect.AffectType.spell; af.Value = "strength"; af.Duration = MUDMath.Dice(8, 5); af.AddModifier(Affect.Apply.strength, MUDMath.Dice(2, 4) + 15); af.SetBitvector(Affect.AFFECT_STRENGTH_INCREASED); ch.AddAffect(af); ch.SendText("You feel stronger.\r\n"); ch.AddInnateTimer(InnateTimerData.Type.strength, 24); ch.WaitState(14); } else { ch.SendText("You don't know how to do that.\r\n"); return; } } else if (!MUDString.IsPrefixOf(str[0], "levitate")) { if (ch.HasInnate(Race.RACE_LEVITATE)) { if (ch.IsAffected( Affect.AFFECT_LEVITATE)) { ch.SendText("You are already levitating."); return; } if (ch.HasInnateTimer(InnateTimerData.Type.levitate)) { ch.SendText("You need to rest a bit first.\r\n"); return; } af.Type = Affect.AffectType.spell; af.Value = "levitation"; af.Duration = MUDMath.Dice(8, 5); af.SetBitvector(Affect.AFFECT_LEVITATE); ch.AddAffect(af); ch.SendText("Your feet rise off the ground.\r\n"); ch.AddInnateTimer(InnateTimerData.Type.levitate, 24); ch.WaitState(6); } else { ch.SendText("You don't know how to do that.\r\n"); return; } } else if (!MUDString.IsPrefixOf(str[0], "faerie")) { if (ch.HasInnate(Race.RACE_FAERIE_FIRE)) { CharData victim = ch.GetCharRoom(str[0]); if (victim == null) ch.SendText("You do not see them here."); else { Spell spl = Spell.SpellList["faerie fire"]; if (spl != null) { spl.Invoke(ch, ch.Level, victim); } } } else { ch.SendText("You don't know how to do that.\r\n"); return; } } else if (!MUDString.IsPrefixOf(str[0], "invisibility")) { if (ch.HasInnate(Race.RACE_INVIS)) { if (ch.IsAffected( Affect.AFFECT_INVISIBLE)) { ch.SendText("You are already invisible!\r\n"); return; } if (ch.HasInnateTimer(InnateTimerData.Type.invisibility)) { ch.SendText("You need to rest a bit first.\r\n"); return; } af.Type = Affect.AffectType.spell; af.Value = "invisibility"; af.Duration = MUDMath.Dice(2, 8) + 4; af.SetBitvector(Affect.AFFECT_INVISIBLE); SocketConnection.Act("$n&n fades out of existence.", ch, null, null, SocketConnection.MessageTarget.room); ch.SendText("You vanish.\r\n"); ch.AddAffect(af); ch.AddInnateTimer(InnateTimerData.Type.invisibility, 24); ch.WaitState(24); } else { ch.SendText("You don't know how to do that.\r\n"); return; } } else if (!MUDString.IsPrefixOf(str[0], "enlarge")) { if (ch.HasInnate(Race.RACE_ENLARGE)) { if (ch.IsAffected(Affect.AFFECT_ENLARGED)) { ch.SendText("You are already enlarged.\r\n"); return; } if (ch.HasInnateTimer(InnateTimerData.Type.enlarge)) { ch.SendText("You need to rest a bit first.\r\n"); return; } af.Type = Affect.AffectType.spell; af.Value = "enlarge"; af.Duration = MUDMath.Dice(8, 5); af.AddModifier(Affect.Apply.size, 1); af.SetBitvector(Affect.AFFECT_NONE); ch.AddAffect(af); ch.SendText("&nYou grow to almost twice your normal size!\r\n"); SocketConnection.Act("$n&n grows to almost twice $s normal size!&n", ch, null, null, SocketConnection.MessageTarget.room); ch.AddInnateTimer(InnateTimerData.Type.enlarge, 24); ch.WaitState(10); } else { ch.SendText("You don't know how to do that.\r\n"); return; } } else if (!MUDString.IsPrefixOf(str[0], "shift")) { if (str.Length > 1 && !MUDString.IsPrefixOf(str[1], "astral")) { if (ch.HasInnate(Race.RACE_SHIFT_ASTRAL)) { if (ch.HasInnateTimer(InnateTimerData.Type.shift_astral)) { ch.SendText("You need to rest a bit first.\r\n"); return; } Spell spell = StringLookup.SpellLookup("plane shift"); if (!spell) { ch.SendText("Something seems to be blocking your ability to shift."); Log.Error("Innate Shift: 'plane shift' spell not found. Check the spells file."); return; } spell.Invoke(ch, ch.Level, new Target(str[1])); ch.AddInnateTimer(InnateTimerData.Type.shift_astral, 8); ch.WaitState(10); } else { ch.SendText("You don't know how to do that.\r\n"); return; } } else if (str.Length > 1 && !MUDString.IsPrefixOf(str[1], "prime")) { if (ch.HasInnate(Race.RACE_SHIFT_PRIME)) { if (ch.HasInnateTimer(InnateTimerData.Type.shift_prime)) { ch.SendText("You need to rest a bit first.\r\n"); return; } Spell spell = StringLookup.SpellLookup("plane shift"); if (!spell) { ch.SendText("Something seems to be blocking your ability to shift."); Log.Error("Innate Shift: 'plane shift' spell not found. Check the spells file."); return; } spell.Invoke(ch, ch.Level, new Target(str[1])); ch.AddInnateTimer(InnateTimerData.Type.shift_prime, 8); ch.WaitState(10); } else { ch.SendText("You don't know how to do that.\r\n"); return; } } } else { ch.SendText("That's not implemented yet. :(\r\n"); } return; } text = String.Format("&+BInnate abilities available to your race and class:&n\r\n"); if (ch.HasInnate(Race.RACE_BODYSLAM)) text += "&n bodyslam\r\n"; if (ch.HasInnate(Race.RACE_CHARGE)) text += "&n charge\r\n"; if (ch.HasInnate(Race.RACE_FLY)) text += "&n fly\r\n"; if (ch.HasInnate(Race.RACE_PASSDOOR)) text += "&n pass door\r\n"; if (ch.HasInnate(Race.RACE_SWIM)) text += "&n swim\r\n"; if (ch.HasInnate(Race.RACE_WATERBREATH)) text += "&n water breathing\r\n"; if (ch.HasInnate(Race.RACE_INFRAVISION)) text += "&n infravision\r\n"; if (ch.HasInnate(Race.RACE_ULTRAVISION)) text += "&n ultravision\r\n"; if (ch.HasInnate(Race.RACE_DETECT_ALIGN) || ch.IsClass(CharClass.Names.antipaladin) || ch.IsClass(CharClass.Names.paladin)) text += "&n detect align\r\n"; if (ch.HasInnate(Race.RACE_DETECT_INVIS)) text += "&n detect invis\r\n"; if (ch.HasInnate(Race.RACE_DETECT_HIDDEN)) text += "&n detect hidden\r\n"; if (ch.HasInnate(Race.RACE_MUTE)) text += "&n mute\r\n"; if (ch.HasInnate(Race.RACE_DOORBASH)) text += "&n doorbash\r\n"; if (ch.HasInnate(Race.RACE_SHRUG)) text += "&n shrug\r\n"; if (ch.HasInnate(Race.RACE_ODSNEAK)) text += "&n outdoor sneak\r\n"; if (ch.HasInnate(Race.RACE_UDSNEAK)) text += "&n underdark sneak\r\n"; if (ch.HasInnate(Race.RACE_STRENGTH)) text += "&n strength\r\n"; if (ch.HasInnate(Race.RACE_FAERIE_FIRE)) text += "&n faerie fire\r\n"; // if( ch.HasInnate( Race.RACE_STEAL )) // buf += "&n steal\r\n" ); if (ch.HasInnate(Race.RACE_ENLARGE)) text += "&n enlarge\r\n"; if (ch.HasInnate(Race.RACE_INVIS)) text += "&n invisibility\r\n"; // if( ch.HasInnate( Race.RACE_SUMMON_HOARDE )) // buf += "&n summon hoarde\r\n" ); if (ch.HasInnate(Race.RACE_SHIFT_PRIME)) text += "&n shift prime\r\n"; if (ch.HasInnate(Race.RACE_SHIFT_ASTRAL)) text += "&n shift astral\r\n"; if (ch.HasInnate(Race.RACE_LEVITATE)) text += "&n levitate\r\n"; if (ch.HasInnate(Race.RACE_BITE)) text += "&n bite\r\n"; ch.SendText(text); return; }
/// <summary> /// Stance: Change combat fighting stance for martial artists. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Stance(CharData ch, string[] str) { if( ch == null ) return; MonkSkill stance = null; string buf; if ((!ch.IsClass(CharClass.Names.monk) && !ch.IsClass(CharClass.Names.mystic)) || ch.IsNPC()) { ch.SendText("You don't know how to do anything fancier than just stand there.\r\n"); return; } if (str.Length == 0) { buf = String.Format("Available stances: none"); if (((PC)ch).MonkAptitude["Bear Stance"] != 0) buf += ", bear"; if (((PC)ch).MonkAptitude["Cat Stance"] != 0) buf += ", cat"; if (((PC)ch).MonkAptitude["Cobra Stance"] != 0) buf += ", cobra"; if (((PC)ch).MonkAptitude["Crane Stance"] != 0) buf += ", crane"; if (((PC)ch).MonkAptitude["Dragon Stance"] != 0) buf += ", dragon"; if (((PC)ch).MonkAptitude["Dragonfly Stance"] != 0) buf += ", dragonfly"; if (((PC)ch).MonkAptitude["Hawk Stance"] != 0) buf += ", hawk"; if (((PC)ch).MonkAptitude["Leopard Stance"] != 0) buf += ", leopard"; if (((PC)ch).MonkAptitude["Mantis Stance"] != 0) buf += ", mantis"; if (((PC)ch).MonkAptitude["Monkey Stance"] != 0) buf += ", monkey"; if (((PC)ch).MonkAptitude["Snake Stance"] != 0) buf += ", snake"; if (((PC)ch).MonkAptitude["Tiger Stance"] != 0) buf += ", tiger"; buf += "\r\n"; ch.SendText(buf); return; } if ("none".StartsWith(str[0])) { ((PC)ch).Stance = String.Empty; SocketConnection.Act("You break out of your fighting stance and return to a relaxed position.", ch, null, null, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n leaves $s fighting stance.", ch, null, null, SocketConnection.MessageTarget.room); return; } if ("bear".StartsWith(str[0])) stance = Database.MonkSkillList["Bear Stance"]; else if ("cat".StartsWith(str[0])) stance = Database.MonkSkillList["Cat Stance"]; else if ("cobra".StartsWith(str[0])) stance = Database.MonkSkillList["Cobra Stance"]; else if ("crane".StartsWith(str[0])) stance = Database.MonkSkillList["Crane Stance"]; else if ("dragon".StartsWith(str[0])) stance = Database.MonkSkillList["Dragon Stance"]; else if ("dragonfly".StartsWith(str[0])) stance = Database.MonkSkillList["Bear Stance"]; else if ("hawk".StartsWith(str[0])) stance = Database.MonkSkillList["Hawk Stance"]; else if ("leopard".StartsWith(str[0])) stance = Database.MonkSkillList["Leopard Stance"]; else if ("mantis".StartsWith(str[0])) stance = Database.MonkSkillList["Mantis Stance"]; else if ("monkey".StartsWith(str[0])) stance = Database.MonkSkillList["Monkey Stance"]; else if ("snake".StartsWith(str[0])) stance = Database.MonkSkillList["Snake Stance"]; else if ("tiger".StartsWith(str[0])) stance = Database.MonkSkillList["Tiger Stance"]; if (((PC)ch).MonkAptitude[stance.Name] == 0) { buf = String.Format("You try to shift into a {0} stance and realize you have no idea what you are doing.", StringConversion.StanceString(stance.Name)); SocketConnection.Act(buf, ch, null, null, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n contorts $s body strangely, nearly tying $mself into a knot. With a frustrated look, $e returns to a standing position.", ch, null, null, SocketConnection.MessageTarget.room); ((PC)ch).Stance = String.Empty; return; } ((PC)ch).Stance = stance.Name; buf = String.Format("You shift your body into a {0} stance.", StringConversion.StanceString(stance.Name)); SocketConnection.Act(buf, ch, null, null, SocketConnection.MessageTarget.character); buf = String.Format("$n&n changes $s stance, looking much like a {0}", StringConversion.StanceString(stance.Name)); SocketConnection.Act(buf, ch, null, null, SocketConnection.MessageTarget.room); return; }
/// <summary> /// Paladin "lay hands" healing command. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void LayHands(CharData ch, string[] str) { if( ch == null ) return; Affect af = new Affect(); if (ch.Level <= Limits.LEVEL_AVATAR && !ch.IsClass(CharClass.Names.paladin)) { ch.SendText("&nYou aren't holy enough to do that!\r\n"); return; } if (ch.IsAffected(Affect.AFFECT_LAYHANDS_TIMER)) { ch.SendText("&nIt is too soon to accomplish that!\r\n"); return; } if (str.Length == 0) { ch.SendText("&nLayhands on whom?\r\n"); return; } CharData victim = ch.GetCharRoom(str[0]); if (!victim) { ch.SendText("&nThey're not here.\r\n"); return; } if (victim != ch && ch.Fighting) { ch.SendText("&nYou can only layhands on yourself while fighting.\r\n"); return; } if (victim.Hitpoints < victim.GetMaxHit()) victim.Hitpoints = Math.Min(victim.Hitpoints + 300, victim.GetMaxHit()); victim.UpdatePosition(); if (ch != victim) { SocketConnection.Act("You lay your hands upon $N.", ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n lays $s hands upon you.", ch, null, victim, SocketConnection.MessageTarget.victim); SocketConnection.Act("$n&n lays $s hands upon $N&n.", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim); } else { ch.SendText("&nYou layhands upon yourself.\r\n"); SocketConnection.Act("$n&n lays hands upon $mself&n.", ch, null, null, SocketConnection.MessageTarget.room); } victim.SendText("&+WYou feel a warm glow!&n\r\n"); if (ch.Level >= Limits.LEVEL_AVATAR) return; af.Value = "layhands timer"; af.Type = Affect.AffectType.skill; af.Duration = 24; af.SetBitvector(Affect.AFFECT_LAYHANDS_TIMER); ch.AddAffect(af); }
/// <summary> /// Sets and gets player configuration options. /// </summary> /// <param name="ch"></param> /// <param name="argument"></param> public static void Toggle(CharData ch, string[] str) { if( ch == null ) return; if (ch.IsNPC()) return; if (str.Length == 0) { ch.SendText("&+L+------------+------------------------------------------------------------+&n\r\n"); ch.SendText("&+L|&n&+m Option&+L |&n&+r Description&n &+L|&n\r\n"); ch.SendText("&+L+------------+------------------------------------------------------------+&n\r\n"); ch.SendText(ch.HasActionBit(PC.PLAYER_AUTOWRAP) ? "&+L[&+WX&+L]&n Autowrap &+L|&n &+cThe MUD automatically wraps long lines of text. &+L|&n\r\n" : "&+L[ ]&n Autowrap &+L|&n &+cThe MUD does not automatically wrap long text. &+L|&n\r\n" ); ch.SendText(ch.HasActionBit(PC.PLAYER_BLANK) ? "&+L[&+WX&+L]&n Blank &+L|&n &+cYou have a blank line before your prompt. &+L|&n\r\n" : "&+L[ ]&n Blank &+L|&n &+cYou have no blank line before your prompt. &+L|&n\r\n" ); ch.SendText(ch.HasActionBit(PC.PLAYER_BRIEF) ? "&+L[&+WX&+L]&n Brief &+L|&n &+cYou see brief descriptions. &+L|&n\r\n" : "&+L[ ]&n Brief &+L|&n &+cYou see long descriptions. &+L|&n\r\n" ); ch.SendText(ch.HasActionBit(PC.PLAYER_CAST_TICK) ? "&+L[&+WX&+L]&n Casttick &+L|&n &+cYou see your casting ticks. &+L|&n\r\n" : "&+L[ ]&n Casttick &+L|&n &+cFor spam's sake, no casting ticks! &+L|&n\r\n" ); ch.SendText(ch.HasActionBit(PC.PLAYER_COMBINE) ? "&+L[&+WX&+L]&n Combine &+L|&n &+cYou see object lists in combined format. &+L|&n\r\n" : "&+L[ ]&n Combine &+L|&n &+cYou see object lists in single format. &+L|&n\r\n" ); ch.SendText(ch.HasActionBit(PC.PLAYER_COLOR) ? "&+L[&+WX&+L]&n Color &+L|&n &+cYou see ANSI colors. &+L|&n\r\n" : "&+L[ ]&n Color &+L|&n &+cYou don't see ANSI colors. &+L|&n\r\n" ); ch.SendText(ch.HasActionBit(PC.PLAYER_COLOR_CON) ? "&+L[&+WX&+L]&n Colorcon &+L|&n &+cYou see ANSI colors in consider messages. &+L|&n\r\n" : "&+L[ ]&n Colorcon &+L|&n &+cYou don't see ANSI colors in consider messages. &+L|&n\r\n" ); ch.SendText(ch.HasActionBit(PC.PLAYER_MSP) ? "&+L[&+WX&+L]&n MSP &+L|&n &+cMUD Sound Protocol (MSP) support is on. &+L|&n\r\n" : "&+L[ ]&n MSP &+L|&n &+cMUD Sound Protocol (MSP) support is off. &+L|&n\r\n" ); ch.SendText(ch.HasActionBit(PC.PLAYER_PAGER) ? "&+L[&+WX&+L]&n Pager &+L|&n &+cText is shown in separate pages. &+L|&n\r\n" : "&+L[ ]&n Pager &+L|&n &+cText is shown all-at-once with no paging. &+L|&n\r\n" ); ch.SendText(ch.HasActionBit(PC.PLAYER_PROMPT) ? "&+L[&+WX&+L]&n Prompt &+L|&n &+cYou have a prompt. &+L|&n\r\n" : "&+L[ ]&n Prompt &+L|&n &+cYou don't have a prompt. &+L|&n\r\n" ); ch.SendText(ch.HasActionBit(PC.PLAYER_SHOUT) ? "&+L[&+WX&+L]&n Shout &+L|&n &+cYou can hear shouts. &+L|&n\r\n" : "&+L[ ]&n Shout &+L|&n &+cYou cover your ears when someone is yelling. &+L|&n\r\n" ); ch.SendText(ch.HasActionBit(PC.PLAYER_TELNET_GA) ? "&+L[&+WX&+L]&n TelnetGA &+L|&n &+cYou receive a telnet go-ahead sequence. &+L|&n\r\n" : "&+L[ ]&n TelnetGA &+L|&n &+cYou don't receive a telnet GA sequence. &+L|&n\r\n" ); ch.SendText(ch.HasActionBit(PC.PLAYER_TELL) ? "&+L[&+WX&+L]&n Tell &+L|&n &+cYou can hear tells. &+L|&n\r\n" : "&+L[ ]&n Tell &+L|&n &+cYou are ignoring tells. &+L|&n\r\n" ); ch.SendText(ch.HasActionBit(PC.PLAYER_VICIOUS) ? "&+L[&+WX&+L]&n Vicious &+L|&n &+cYou are vicious and will kill mortally wounded foes. &+L|&n\r\n" : "&+L[ ]&n Vicious &+L|&n &+cYou aren't vicious and will spare a mortally wounded foe. &+L|&n\r\n"); ch.SendText(ch.HasActionBit(PC.PLAYER_MAP) ? "&+L[&+WX&+L]&n Map &+L|&n &+cYou see the maps. &+L|&n\r\n" : "&+L[ ]&n Map &+L|&n &+cYou do not see maps. &+L|&n\r\n"); if (ch.HasActionBit(PC.PLAYER_SILENCE)) { ch.SendText( "&+L[&+WX&+L]&n Silence &+L|&n &+cYou are silenced. &+L|&n\r\n"); } ch.SendText(!ch.HasActionBit(PC.PLAYER_NO_EMOTE) ? "&n" : "&+L[ ]&n emote | &+cYou can't emote. &+L|&n\r\n"); ch.SendText("&+L+------------+------------------------------------------------------------+&n\r\n"); string termStr = "&+L|&n &+cYour terminal type is " + ch.Socket.Terminal + " and MCCP is " + ch.Socket.MCCPEnabled + "."; while (termStr.Length < 82) termStr += " "; termStr += "&+L|&n\r\n"; ch.SendText(termStr); ch.SendText("&+L+-------------------------------------------------------------------------+&n\r\n"); } else { string word; Bitvector bit; int fSet; if (str[0][0] == '-') { fSet = 0; word = str[0].Substring(1); } else if (str[0][0] == '+') { fSet = 1; word = str[0].Substring(1); } else { fSet = 2; word = str[0]; } if (("blank".StartsWith(word, StringComparison.CurrentCultureIgnoreCase))) bit = PC.PLAYER_BLANK; else if (("autowrap".StartsWith(word, StringComparison.CurrentCultureIgnoreCase))) bit = PC.PLAYER_AUTOWRAP; else if (("brief".StartsWith(word, StringComparison.CurrentCultureIgnoreCase))) bit = PC.PLAYER_BRIEF; else if (("casttick".StartsWith(word, StringComparison.CurrentCultureIgnoreCase))) bit = PC.PLAYER_CAST_TICK; else if (("combine".StartsWith(word, StringComparison.CurrentCultureIgnoreCase))) bit = PC.PLAYER_COMBINE; else if (("color".StartsWith(word, StringComparison.CurrentCultureIgnoreCase))) { if (ch.Socket.Terminal == SocketConnection.TerminalType.TERMINAL_ENHANCED) { ch.SendText("You cannot turn color off when using the enhanced client.\r\n"); return; } bit = PC.PLAYER_COLOR; } else if (("colorcon".StartsWith(word, StringComparison.CurrentCultureIgnoreCase))) bit = PC.PLAYER_COLOR_CON; else if (("msp".StartsWith(word, StringComparison.CurrentCultureIgnoreCase))) bit = PC.PLAYER_MSP; else if (("pager".StartsWith(word, StringComparison.CurrentCultureIgnoreCase))) bit = PC.PLAYER_PAGER; else if (("shout".StartsWith(word, StringComparison.CurrentCultureIgnoreCase))) bit = PC.PLAYER_SHOUT; else if (("prompt".StartsWith(word, StringComparison.CurrentCultureIgnoreCase))) bit = PC.PLAYER_PROMPT; else if (("telnetga".StartsWith(word, StringComparison.CurrentCultureIgnoreCase))) bit = PC.PLAYER_TELNET_GA; else if (("tell".StartsWith(word, StringComparison.CurrentCultureIgnoreCase))) bit = PC.PLAYER_TELL; else if (("vicious".StartsWith(word, StringComparison.CurrentCultureIgnoreCase))) bit = PC.PLAYER_VICIOUS; else if (("map".StartsWith(word, StringComparison.CurrentCultureIgnoreCase))) bit = PC.PLAYER_MAP; else if (("vicious".StartsWith(word, StringComparison.CurrentCultureIgnoreCase))) bit = PC.PLAYER_VICIOUS; else if (("compact".StartsWith(word, StringComparison.CurrentCultureIgnoreCase))) bit = PC.PLAYER_BLANK; else if (word.Contains("wimpy")) { CommandType.Interpret(ch, "wimpy " + word.Substring((word.IndexOf("wimpy") + 5))); return; } else if ("mccp".StartsWith(word, StringComparison.CurrentCultureIgnoreCase)) { ch.Socket.MCCPEnabled = !ch.Socket.MCCPEnabled; if (ch.Socket.MCCPEnabled) { ch.SendText("MCCP is now enabled."); } else { ch.SendText("MCCP is now disabled."); } return; } else { ch.SendText("&nConfig which option?\r\n"); return; } if (ch.IsClass(CharClass.Names.paladin) && bit == PC.PLAYER_VICIOUS) { ch.SendText("Paladins may not toggle vicious.\r\n"); /* Just to make sure they don't have it toggled on. */ ch.RemoveActionBit(bit); return; } if (fSet == 1) { if (bit != PC.PLAYER_NONE) { ch.SetActionBit(bit); } ch.SendText( (String.Format("&n{0} is now ON.\r\n", word.ToUpper()))); } else if (fSet == 0) { if (bit != PC.PLAYER_NONE) { ch.RemoveActionBit(bit); } ch.SendText((String.Format("&n{0} is now OFF.\r\n", word.ToUpper()))); } else if (fSet == 2) { if (bit != PC.PLAYER_NONE) { ch.ToggleActionBit(bit); } if (ch.HasActionBit(bit)) ch.SendText((String.Format("&n{0} is now ON.\r\n", word.ToUpper()))); else ch.SendText((String.Format("&n{0} is now OFF.\r\n", word.ToUpper()))); } } return; }
/// <summary> /// Looks at an object, mobile, or room. /// </summary> /// <param name="ch">The acting character.</param> /// <param name="str">Command arguments.</param> public static void LookCommand(CharData ch, string[] str) { if( ch == null ) return; // Build argument list, stripping articles. bool inside = false; List<String> args = new List<string>(str); for (int i = (args.Count - 1); i >= 0; i-- ) { if (args[i].Equals("in", StringComparison.CurrentCultureIgnoreCase) || args[i].Equals("i", StringComparison.CurrentCultureIgnoreCase)) { args.RemoveAt(i); inside = true; } else if (args[i].Equals("at", StringComparison.CurrentCultureIgnoreCase)) { args.RemoveAt(i); } } // If it's a mob that isn't switched, bail. if (ch.Socket == null) return; if (ch.CurrentPosition < Position.sleeping) { ch.SendText("&nYou can't see anything but &+Ystars&n! See how pretty!\r\n"); return; } if (ch.CurrentPosition == Position.sleeping) { ch.SendText("&nYou can't see anything, you're &+Lsleeping&n! Zzz.\r\n"); return; } if (ch.IsBlind()) return; // Look panel for ships. if ( args.Count > 0 && args[0].Equals("panel", StringComparison.CurrentCultureIgnoreCase)) { CommandType.Interpret(ch, "Lookpanel"); return; } // Look out for ships. if (args.Count > 0 && args[0].Equals("out", StringComparison.CurrentCultureIgnoreCase)) { CommandType.Interpret(ch, "Lookout"); return; } Object obj; Exit exit; string pdesc; int number = 0; string output = String.Empty; // 'look' or 'look auto' or 'look room' if (args.Count == 0 || args[0].Equals( "auto", StringComparison.CurrentCultureIgnoreCase) || args[0].Equals("room", StringComparison.CurrentCultureIgnoreCase)) { if (ch.InRoom == null) { ch.SendText("You are not in a room. You are just floating in empty space. This should never happen. You should <petition> someone for help.\r\n"); Log.Error("Character executing Commandlook command from null room: " + ch.Name); return; } if (ch.FlightLevel > 0) { switch (ch.FlightLevel) { case CharData.FlyLevel.low: ch.SendText("Hovering above "); break; case CharData.FlyLevel.medium: ch.SendText("Flying above "); break; case CharData.FlyLevel.high: ch.SendText("Flying high above "); break; } } if (!ch.HasActionBit(PC.PLAYER_GODMODE) && ch.InRoom.IsDark() && !ch.HasInnate(Race.RACE_ULTRAVISION) && !ch.IsAffected( Affect.AFFECT_ULTRAVISION)) { ch.SendText("&+lSomewhere\r\n"); } else if (!ch.HasActionBit(PC.PLAYER_GODMODE)) { String roomOpen = String.Empty; String roomClose = String.Empty; if (!ch.IsNPC() && ch.Socket.Terminal == SocketConnection.TerminalType.TERMINAL_ENHANCED) { roomOpen = "<zone>" + ch.InRoom.Area.Name + "</zone><roomTitle>"; roomClose = "</roomTitle>"; } else { roomClose = "&n\r\n"; } // Added support for both manual and automatic descriptions on the worldmap. if (!ch.InRoom.Area.HasFlag(Area.AREA_WORLDMAP) || ch.InRoom.Title.Length > 1) { output += roomOpen + ch.InRoom.Title + roomClose; } else { output += roomOpen + "No room title." + roomClose; } } else { if (!ch.IsNPC() && ch.Socket.Terminal == SocketConnection.TerminalType.TERMINAL_ENHANCED) { ch.SendText("<zone>" + ch.InRoom.Area.Name + "</zone>"); } Look.ShowRoomInfo(ch, ch.InRoom); } if (!ch.IsNPC() && !ch.HasActionBit(PC.PLAYER_GODMODE) && ch.InRoom.IsDark() && !ch.HasInnate(Race.RACE_ULTRAVISION) && !ch.IsAffected( Affect.AFFECT_ULTRAVISION)) { ch.SendText("&+LIt is pitch black...&n \r\n"); Look.ShowCharacterToCharacter(ch.InRoom.People, ch); return; } if (!ch.IsNPC() && (args.Count > 0 && (args[0].Equals("room", StringComparison.CurrentCultureIgnoreCase) || args[0].Equals("auto", StringComparison.CurrentCultureIgnoreCase)))) { String roomDescOpen = String.Empty; String roomDescClose = String.Empty; String mapSpace = String.Empty; if (!ch.IsNPC() && ch.Socket.Terminal == SocketConnection.TerminalType.TERMINAL_ENHANCED) { roomDescOpen = "<roomDescription>"; roomDescClose = "</roomDescription>"; } else { roomDescClose = "&n\r\n"; mapSpace = " "; } if( !ch.HasActionBit(PC.PLAYER_BRIEF) && !ch.InRoom.Area.HasFlag(Area.AREA_WORLDMAP)) { // Added support for both manual and automatic descriptions on the worldmap. if (ch.InRoom.Description.Length > 0) { output += roomDescOpen + " " + (ch.InRoom.Description.Trim()) + roomDescClose; } //else if (ch._inRoom.WorldmapTerrainType < Database.SystemData.MapInfo.Length) // output += roomDescOpen + mapSpace + Database.SystemData.MapInfo[ch._inRoom.WorldmapTerrainType].RoomDescription + roomDescClose; else { output += roomDescOpen + " No room description." + roomDescClose; } } } if (!String.IsNullOrEmpty(output)) { ch.SendText(output); } if (ch.InRoom.Area.HasFlag(Area.AREA_WORLDMAP)) { if (ch.HasActionBit(PC.PLAYER_MAP)) { Command.Worldmap(ch, null); } else if (!ch.IsNPC() && ch.Socket.Terminal != SocketConnection.TerminalType.TERMINAL_ENHANCED) { ch.SendText("\r\n"); } } if (ch.InRoom.HasFlag(RoomTemplate.ROOM_SILENT)) { ch.SendText("&nIt seems preternaturally quiet.\r\n"); } CommandType.Interpret(ch, "exits auto"); Look.ShowRoomAffects(ch, ch.InRoom); Look.ShowListToCharacter(ch.InRoom.Contents, ch, false, false); Look.ShowCharacterToCharacter(ch.InRoom.People, ch); return; } // 'look direction' int door = -1; if (args.Count > 0) { if ("north".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 0; else if ("east".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 1; else if ("south".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 2; else if ("west".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 3; else if ("up".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 4; else if ("down".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 5; else if ("northwest".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 6; else if ("southwest".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 7; else if ("northeast".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 8; else if ("southeast".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 9; else if ("nw".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 6; else if ("sw".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 7; else if ("ne".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 8; else if ("se".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 9; } if (door != -1) { // If no exit data, then return. exit = ch.InRoom.ExitData[door]; if (!exit) { ch.SendText("There's nothing to see in that direction.\r\n"); return; } if (exit.HasFlag(Exit.ExitFlag.walled)) { ch.SendText("There's a wall in the way.\r\n"); return; } // Check for farsee if ((ch.IsAffected( Affect.AFFECT_FARSEE) || ch.HasActionBit(PC.PLAYER_GODMODE)) && !exit.HasFlag(Exit.ExitFlag.closed)) { if (exit.TargetRoom) { Room room = ch.InRoom; ch.RemoveFromRoom(); ch.AddToRoom(Room.GetRoom(exit.IndexNumber)); CommandType.Interpret(ch, "look"); ch.RemoveFromRoom(); ch.AddToRoom(room); return; } ch.SendText("Nothing special there.\r\n"); } if (exit.Description.Length != 0) { ch.SendText(exit.Description); } else { ch.SendText("Nothing special there.\r\n"); } if (exit.Keyword.Length != 0) { if (exit.HasFlag(Exit.ExitFlag.bashed)) SocketConnection.Act("The $d has been bashed from its &n&+whinges&n.", ch, null, exit.Keyword, SocketConnection.MessageTarget.character); else if (exit.HasFlag(Exit.ExitFlag.closed)) SocketConnection.Act("The $d is closed.", ch, null, exit.Keyword, SocketConnection.MessageTarget.character); else if (exit.HasFlag(Exit.ExitFlag.secret)) SocketConnection.Act("The $d is secret.", ch, null, exit.Keyword, SocketConnection.MessageTarget.character); else if (exit.HasFlag(Exit.ExitFlag.blocked)) SocketConnection.Act("The $d is blocked.", ch, null, exit.Keyword, SocketConnection.MessageTarget.character); } else { if (exit.HasFlag(Exit.ExitFlag.bashed)) { SocketConnection.Act("The door has been bashed from its &n&+whinges&n.", ch, null, null, SocketConnection.MessageTarget.character); } else if (exit.HasFlag(Exit.ExitFlag.closed)) SocketConnection.Act("The door is closed.", ch, null, null, SocketConnection.MessageTarget.character); else if (exit.HasFlag(Exit.ExitFlag.is_door)) SocketConnection.Act("The door is open.", ch, null, null, SocketConnection.MessageTarget.character); else if (exit.HasFlag(Exit.ExitFlag.secret)) SocketConnection.Act("The door is secret.", ch, null, exit.Keyword, SocketConnection.MessageTarget.character); else if (exit.HasFlag(Exit.ExitFlag.blocked)) SocketConnection.Act("The $d is blocked.", ch, null, exit.Keyword, SocketConnection.MessageTarget.character); } // If exit found, don't keep looking. return; } // Look inside something ('look in'). if (inside) { // 'look in' if (args.Count < 1) { ch.SendText("Look in what?\r\n"); return; } obj = ch.GetObjHere(args[0]); if (!obj) { ch.SendText("You do not see that here.\r\n"); return; } switch (obj.ItemType) { default: ch.SendText("That is not a container.\r\n"); break; case ObjTemplate.ObjectType.drink_container: if (obj.Values[1] == -1) { ch.SendText("It is full.\r\n"); break; } if (obj.Values[1] <= 0) { ch.SendText("It is empty.\r\n"); break; } output += "It's "; if (obj.Values[1] < obj.Values[0] / 4) output += "less than half"; else if (obj.Values[1] < (3 * obj.Values[0] / 4)) output += "about half"; else if (obj.Values[1] < obj.Values[0]) output += "more than half"; else output += "completely"; output += " full of a " + Liquid.Table[obj.Values[2]].Color + "liquid.\r\n"; ch.SendText(output); break; case ObjTemplate.ObjectType.quiver: case ObjTemplate.ObjectType.container: case ObjTemplate.ObjectType.npc_corpse: case ObjTemplate.ObjectType.pc_corpse: if (Macros.IsSet(obj.Values[1], ObjTemplate.CONTAINER_CLOSED.Vector)) { ch.SendText("It is closed.\r\n"); break; } SocketConnection.Act("$p&n contains:", ch, obj, null, SocketConnection.MessageTarget.character, true); Look.ShowListToCharacter(obj.Contains, ch, true, true); break; case ObjTemplate.ObjectType.portal: SocketConnection.Act("A $p&n leads to:", ch, obj, null, SocketConnection.MessageTarget.character); output += Room.GetRoom(obj.Values[0]).Title + "\r\n"; output += Room.GetRoom(obj.Values[0]).Description; output += "\r\n"; ch.SendText(output); break; } return; } // Look at another char. if (args.Count > 0) { CharData victim = ch.GetCharRoom(args[0]); if (victim != null) { Look.ShowCharacterToCharacterFull(victim, ch); return; } } // Look at an object. if (args.Count > 0) { // Check inventory. obj = ch.GetObjCarrying(args[0]); // If not in inventory, check eq. if (obj == null) obj = ch.GetObjWear(args[0]); // If not on character, check room. if (obj == null) obj = Object.GetObjFromList(ch.InRoom.Contents, ch, args[0]); // If object found, show it to the char. if (obj != null) { pdesc = (Database.GetExtraDescription(args[0], obj.ExtraDescription)); if (pdesc.Length != 0) { ch.SendText(pdesc); } else if ((pdesc = (Database.GetExtraDescription(args[0], obj.ObjIndexData.ExtraDescriptions))).Length > 0) { ch.SendText(pdesc); } else if (obj.FullDescription.Length > 0) { ch.SendText(obj.FullDescription); ch.SendText("\r\n"); } if (obj.HasAffect(Affect.AffectType.skill, "poison weapon")) { if (ch.IsClass(CharClass.Names.thief) || ch.IsClass(CharClass.Names.assassin) || MUDMath.NumberPercent() < ch.GetCurrInt() / 2) ch.SendText("It has a &+Gsickly &+Lcolored&n hue.\r\n"); } return; } } // Look at an object in the room if (args.Count > 0) { int count = 0; foreach (Object iobj in ch.InRoom.Contents) { if (CharData.CanSeeObj(ch, iobj)) { pdesc = (Database.GetExtraDescription(args[0], iobj.ExtraDescription)); if (pdesc.Length != 0) { if (++count == number) { ch.SendText(pdesc); return; } continue; } pdesc = (Database.GetExtraDescription(args[0], iobj.ObjIndexData.ExtraDescriptions)); if (pdesc.Length != 0) { if (++count == number) { ch.SendText(pdesc); return; } continue; } if (MUDString.NameContainedIn(args[0], iobj.Name)) { if (++count == number) { ch.SendText(iobj.FullDescription); ch.SendText("\r\n"); return; } continue; } } } } // Check for room extra descriptions if (args.Count > 0) { pdesc = (Database.GetExtraDescription(args[0], ch.InRoom.ExtraDescriptions)); if (!String.IsNullOrEmpty(pdesc)) { ch.SendText(pdesc); return; } } ch.SendText("You do not see that here.\r\n"); return; }
/// <summary> /// Psionics should be instantaneous and exempt from the casting stuff, and suffer /// lag after their power goes off. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Will(CharData ch, string[] str) { if( ch == null ) return; if (!ch.IsClass(CharClass.Names.psionicist) && !ch.IsClass(CharClass.Names.enslaver) && !ch.IsImmortal()) { ch.SendText("Your mind is much too puny for you to focus your will.\r\n"); return; } if (ch.IsClass(CharClass.Names.bard) && !ch.IsImmortal()) { ch.SendText("Bards use the SING or PLAY commands to invoke their powers.\r\n"); return; } if (ch.Riding && ch.Riding.InRoom == ch.InRoom) { ch.SendText("It's too hard to concentrate! Dismount.\r\n"); return; } // Psis should be able to will stuff when paralyzed, thus no check for para. if (str.Length == 0) { ch.SendText("Will which what where?\r\n"); return; } Magic.Cast(ch, String.Join(" ", str)); }
/// <summary> /// Defines how a player's prompt is displayed. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Prompt(CharData ch, string[] str) { string buf = String.Empty; /* Unswitched NPC's get kicked out */ if (ch.Socket == null) return; /* Will always have a pc ch after this */ ch = (ch.Socket.Original != null ? ch.Socket.Original : ch.Socket.Character); if (str.Length == 0) { if (ch.HasActionBit(PC.PLAYER_PROMPT)) { buf = "-prompt"; } else { buf = "+prompt"; } CommandType.Interpret(ch, "toggle " + buf); return; } if (str[0].Equals("raw", StringComparison.CurrentCultureIgnoreCase)) buf += "&n&+g<%h&n&+g/%H&n&+ghp %mm %v&n&+g/%V&n&+gmv>\r\n&n&+g<&n%D&n %B&+g>&n "; else if (str[0].Equals("dark", StringComparison.CurrentCultureIgnoreCase)) buf += "&+L<&n&+m%h&+L/&n&+m%H&+Lhp &n&+m%v&+L/&n&+m%V&+Lmv>\r\n&+L<&n%T&n %E&+L>&n "; else if (str[0].Equals("meter", StringComparison.CurrentCultureIgnoreCase)) if (ch.IsClass(CharClass.Names.psionicist) || ch.IsClass(CharClass.Names.bard)) buf += "&n&+g<%h&n&+g/%H&n&+ghp %mm/%MM %v&n&+g/%V&n&+gmv>\r\n&n&+g<&n%d&n %b&+g>&n "; else buf += "&n&+g<%h&n&+g/%H&n&+ghp %v&n&+g/%V&n&+gmv>\r\n&n&+g<&n%d&n %b&+g>&n "; else if (str[0].Equals("chains", StringComparison.CurrentCultureIgnoreCase)) buf += "&+L<&n&+m%h&+L/&n&+m%H&+Lhp &n&+m%v&+L/&n&+m%V&+Lmv>\r\n&+L<&n%t&n %e&+L>&n "; else if (str[0].Equals("status", StringComparison.CurrentCultureIgnoreCase)) buf += "&n&+g<%l&+g:%h&n&+g/%H&n&+ghp %mm %v&n&+g/%V&n&+gmv>\r\n&n&+g<&n%D&n %B&+g>&n "; else if (str[0].Equals("all", StringComparison.CurrentCultureIgnoreCase)) if (ch.IsClass(CharClass.Names.psionicist) || ch.IsClass(CharClass.Names.bard)) buf += "&n&+g<%h&n&+g/%H&n&+ghp %m/%Mm %v&n&+g/%V&n&+gmv>\r\n&n&+g<&n%D&n %B&+g>&n "; else buf += "&n&+g<%h&n&+g/%H&n&+ghp %v&n&+g/%V&n&+gmv>\r\n&n&+g<&n%D&n %B&+g>&n "; else if (str[0].Equals("fancy", StringComparison.CurrentCultureIgnoreCase)) if (ch.IsClass(CharClass.Names.psionicist) || ch.IsClass(CharClass.Names.bard)) buf += "&n&+g<%h&n&+g/%H&n&+ghp %m/%Mm &+g%v&n&+g/%V&n&+gmv %a>\r\n&n%n&+g< &n%l&n &n%d&n %b&+g>&n "; else buf += "&n&+g<%h&n&+g/%H&n&+ghp &+g%v&n&+g/%V&n&+gmv %a>\r\n&n%n&+g< &n%l&n &n%d&n %b&+g>&n"; else { string text = String.Join(" ", str); if (text.Length > 70) text = text.Substring(0, 70); buf = text; } ((PC)ch).Prompt = buf; ch.SendText("&nOk.\r\n"); 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> /// Bandage someone's wounds. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Bandage(CharData ch, string[] str) { if( ch == null ) return; if (ch.IsNPC() || !ch.HasSkill("bandage")) { ch.SendText("You don't know how to bandage!\r\n"); return; } if (str.Length == 0) { ch.SendText("Bandage whom?\r\n"); return; } CharData victim = ch.GetCharRoom(str[0]); if (victim == null) { ch.SendText("They're not here.\r\n"); return; } if (victim.Hitpoints > 0) { ch.SendText("They do not need your help.\r\n"); return; } int chance = ((PC)ch).SkillAptitude["bandage"]; if (ch.IsClass(CharClass.Names.cleric)) chance += 4; else if (ch.IsClass(CharClass.Names.antipaladin)) chance -= 4; /* Don't allow someone doing more than 1 pt. of damage with bandage. */ int change = (Math.Max(chance - MUDMath.NumberPercent(), -1) / 20) + 1; // Bandage is rarely used, make it likely to increase ch.PracticeSkill("bandage"); ch.PracticeSkill("bandage"); ch.PracticeSkill("bandage"); ch.WaitState(Skill.SkillList["bandage"].Delay); if (change < 0) { ch.SendText("You just made the problem worse!\r\n"); SocketConnection.Act("$n&n tries bandage you but your condition only worsens.", ch, null, victim, SocketConnection.MessageTarget.victim); SocketConnection.Act("$n&n tries bandage $N&n but $S condition only worsens.", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim); } else if (change > 0) { ch.SendText("You manage to fix them up a _bitvector.\r\n"); SocketConnection.Act("$n&n bandages you.", ch, null, victim, SocketConnection.MessageTarget.victim); SocketConnection.Act("$n&n bandages $N&n.", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim); } else { ch.SendText("Your bandaging attempt had no effect.\r\n"); SocketConnection.Act("$n&n tries to bandage you but the wounds are too great.", ch, null, victim, SocketConnection.MessageTarget.victim); SocketConnection.Act("$n&n tries to bandage $N&n but is unable to have any effect.", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim); } victim.Hitpoints += change; victim.UpdatePosition(); return; }
/// <summary> /// Command to set your level of aggressiveness. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Aggressive(CharData ch, string[] str) { if( ch == null ) return; string arg1 = String.Empty; if (ch.IsNPC()) return; if (ch.IsClass(CharClass.Names.paladin)) { ch.SendText("Your beliefs prevent you from acting in such a manner.\r\n"); return; } if (arg1.Length > 0) { if (MUDString.NameContainedIn("off", arg1) || MUDString.NameContainedIn("none", arg1)) { ((PC)ch).AggressiveLevel = -1; } else { int aggr; Int32.TryParse(arg1, out aggr); if (aggr < -1 || aggr > ch.GetMaxHit()) { ch.SendText("Value out of range.\r\n"); return; } ((PC)ch).AggressiveLevel = aggr; } } string buf; if (((PC)ch).AggressiveLevel == -1) { buf = "You are not aggressive.\r\n"; } else { buf = "You are aggressive to creatures if hit points above " + ((PC)ch).AggressiveLevel + ".\r\n"; } ch.SendText(buf); 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> /// 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 ); }