/// <summary> /// Song of chaos. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="level"></param> /// <param name="target"></param> /// <returns></returns> public static bool SongChaos( CharData ch, Spell spell, int level, Target target ) { foreach( CharData victim in ch.InRoom.People ) { } return true; }
/// <summary> /// Song of calming. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="level"></param> /// <param name="target"></param> /// <returns></returns> public static bool SongCalming( CharData ch, Spell spell, int level, Target target ) { foreach( CharData victim in ch.InRoom.People ) { Combat.StopFighting( victim, false ); victim.WaitState( 2 ); } return true; }
private object _var; // A miscellaneous variable #endregion Fields #region Constructors /// <summary> /// Parameterized constructor. /// </summary> /// <param name="itype"></param> /// <param name="itime"></param> /// <param name="varg1"></param> /// <param name="varg2"></param> /// <param name="var"></param> public Event( EventType itype, int itime, Target varg1, Target varg2, object var ) { ++_numEvents; _type = itype; _time = itime; _arg1 = varg1; _arg2 = varg2; _var = var; }
/// <summary> /// Song of armor. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="level"></param> /// <param name="target"></param> /// <returns></returns> public static bool SongArmor( CharData ch, Spell spell, int level, Target target ) { ch.SendText( "You Sing a song of protection.\r\n" ); foreach( CharData victim in ch.InRoom.People ) { if (victim.IsAffected( Affect.AFFECT_ARMOR)) continue; Affect af = new Affect( Affect.AffectType.song, spell.Name, 4, Affect.Apply.ac, ( 0 - ( 10 + ( level / 5 ) ) ), Affect.AFFECT_ARMOR ); victim.CombineAffect( af ); victim.SendText( "You feel someone protecting you.\r\n" ); } return true; }
/// <summary> /// Song of heroism. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="level"></param> /// <param name="target"></param> /// <returns></returns> public static bool SongHeroism( CharData ch, Spell spell, int level, Target target ) { foreach( CharData victim in ch.InRoom.People ) { Affect af = new Affect(Affect.AffectType.song, spell.Name, (level / 8), Affect.Apply.hitroll, (level / 6 + 1), Affect.AFFECT_NONE); victim.AddAffect(af); af = new Affect(Affect.AffectType.song, spell.Name, (level / 8), Affect.Apply.damroll, (level / 11 + 1), Affect.AFFECT_NONE); victim.AddAffect(af); SocketConnection.Act( "$n&n looks more courageous.", victim, null, null, SocketConnection.MessageTarget.room ); victim.SendText( "You feel righteous.\r\n" ); } return true; }
/// <summary> /// When a spell event terminates, we need something to happen. /// /// By this point we should have terminated the spell/song event data /// and should only need the info about the character and the spell /// and the argument(s). /// /// Passing of the correct function parameters should be handled by the /// event system. /// </summary> /// <param name="ch">The caster</param> /// <param name="spell">The spell number</param> /// <param name="target">The _targetType</param> public static void FinishSpell( CharData ch, Spell spell, Target target ) { Object obj; int chance = 0; bool found = false; string lbuf = String.Format("Magic.FinishSpell: {0} by {1}", spell.Name, ch.Name); Log.Trace( lbuf ); for( int i = Database.CastList.Count - 1; i >= 0; i--) { if (Database.CastList[i].Who && Database.CastList[i].Who == ch) { Database.CastList.RemoveAt( i ); } } // If they're not casting at the end of the song or spell // they certainly can't finish it. if( ch.IsAffected( Affect.AFFECT_CASTING ) ) ch.RemoveAffect( Affect.AFFECT_CASTING ); else { return; } if( !ch.CheckConcentration(spell) ) return; if( ( ch.IsAffected( Affect.AFFECT_MUTE ) || ch.HasInnate( Race.RACE_MUTE ) ) && !ch.IsClass( CharClass.Names.psionicist ) ) { ch.SendText( "Your lips move but no sound comes out.\r\n" ); return; } // Make sure the room is still castable. if( !ch.InRoom.CheckCastable( ch, false, false ) ) return; if( ch.InRoom.CheckStarshell( ch ) ) return; MemorizeData memorized = null; if (!ch.IsNPC() && !ch.IsImmortal() && !ch.IsClass(CharClass.Names.psionicist)) { foreach( MemorizeData mem in ((PC)ch).Memorized ) { if( !mem.Memmed ) continue; if( mem.Name == spell.Name ) { found = true; memorized = mem; break; } } if (!found && !ch.IsNPC() && !ch.IsClass(CharClass.Names.psionicist)) { ch.SendText( "You do not have that spell memorized!\r\n" ); if( spell.ValidTargets == TargetType.objectOrCharacter ) target = null; else if( spell.ValidTargets == TargetType.none ) target = null; return; } } if( ch.IsAffected( Affect.AFFECT_FEEBLEMIND ) ) { ch.SendText( "You are just too stupid to cast that spell!\r\n" ); SocketConnection.Act( "$n&n screws up $s face in concentration.", ch, null, null, SocketConnection.MessageTarget.room ); SocketConnection.Act( "$n&n tries really, really hard to complete a spell, but fails.", ch, null, null, SocketConnection.MessageTarget.room ); return; } // Locate targets. CharData victim = null; switch( spell.ValidTargets ) { default: Log.Trace( "FinishSpell: bad TargetType for spell {1}.", spell ); return; case TargetType.objectOrCharacter: if( ch.IsAffected( Affect.AFFECT_BLIND ) ) { ch.SendText( "You cannot see to cast that spell.\r\n" ); return; } break; case TargetType.none: break; case TargetType.trap: ch.SendText( "You cannot cast a trap!\r\n" ); return; case TargetType.singleCharacterOffensive: victim = (CharData)target; if( ch.IsAffected( Affect.AFFECT_BLIND ) ) { //allow casting if in combat and no _targetType specified if( !( ch.Fighting && victim == ch.Fighting ) ) { ch.SendText( "You cannot see to cast that spell.\r\n" ); return; } } if( !victim ) { ch.SendText( "They aren't here.\r\n" ); return; } if( !victim.InRoom || victim.InRoom != ch.InRoom ) { ch.SendText( "They are not here.\r\n" ); return; } if( Combat.IsSafe( ch, victim ) ) return; // Command.is_safe could wipe out victim, as it calls procs if a boss // check and see that victim is still valid if( !victim ) return; Crime.CheckAttemptedMurder( ch, victim ); /* Check for globes. This will stop any spells of type TargetType.singleCharacterOffensive * but area effect spells with type TargetType.none will get through, since we * don't know whether they will be offensive or not. The only thing we can * really do is add this same thing in the Command.SpellDamage function to prevent * those from getting through. However, we must treat cases of things like * an area effect sleep spell as a special case within the SpellWhatever * function in Spells.cs. However, by the nature of the spell, anything * that is not either offensive and not direct damage, it should get through * just so that these spells have some weaknesses for the strategic to get * around. */ /* * TODO: Find out why this globe code was commented out and either uncomment or delete. if( CharData.IsAffected( victim, Affect.AFFECT_MAJOR_GLOBE ) && Spell.Table[spell].spell_circle[ch.cclass] <= 6 ) { Descriptor._actFlags( "&+RThe globe around $N&n's body bears the brunt of your assault!&n", ch, null, victim, Descriptor.MessageTarget.character ); Descriptor._actFlags( "&+RYour globe deflects $n&+R's attack!&n", ch, null, victim, Descriptor.MessageTarget.victim ); Descriptor._actFlags( "&+R$N&+R's globe deflects $n&+R's attack!&n", ch, null, victim, Descriptor.MessageTarget.room ); return; } if( CharData.IsAffected( victim, Affect.AFFECT_GREATER_SPIRIT_WARD ) && Spell.Table[spell].spell_circle[ch.cclass] <= 5 ) { Descriptor._actFlags( "&+WThe aura around $N&n's body bears the brunt of your assault!&n", ch, null, victim, Descriptor.MessageTarget.character ); Descriptor._actFlags( "&+WYour globe absorbs $n&+W's attack!&n", ch, null, victim, Descriptor.MessageTarget.victim ); Descriptor._actFlags( "&+W$N&+W's aura absorbs $n&+W's attack!&n", ch, null, victim, Descriptor.MessageTarget.room ); return; } if( CharData.IsAffected( victim, Affect.AFFECT_MINOR_GLOBE ) && Spell.Table[spell].spell_circle[ch.cclass] <= 4 ) { Descriptor._actFlags( "&+RThe globe around $N&n's body bears the brunt of your assault!&n", ch, null, victim, Descriptor.MessageTarget.character ); Descriptor._actFlags( "&+RYour globe deflects $n&+R's attack!&n", ch, null, victim, Descriptor.MessageTarget.victim ); Descriptor._actFlags( "&+R$N&+R's globe deflects $n&+R's attack!&n", ch, null, victim, Descriptor.MessageTarget.room ); return; } if( CharData.IsAffected( victim, Affect.AFFECT_SPIRIT_WARD ) && Spell.Table[spell].spell_circle[ch.cclass] <= 3 ) { Descriptor._actFlags( "&+WThe aura around $N&n's body bears the brunt of your assault!&n", ch, null, victim, Descriptor.MessageTarget.character ); Descriptor._actFlags( "&+WYour globe absorbs $n&+W's attack!&n", ch, null, victim, Descriptor.MessageTarget.victim ); Descriptor._actFlags( "&+W$N&+W's aura absorbs $n&+W's attack!&n", ch, null, victim, Descriptor.MessageTarget.room ); return; } */ break; case TargetType.singleCharacterWorld: victim = (CharData)target; if( ch.IsAffected( Affect.AFFECT_BLIND ) && victim != ch ) { ch.SendText( "You cannot see to cast that spell.\r\n" ); return; } break; case TargetType.singleCharacterDefensive: victim = (CharData)target; if( ch.IsAffected( Affect.AFFECT_BLIND ) && victim != ch ) { ch.SendText( "You cannot see to cast that spell.\r\n" ); return; } if( !victim || victim.InRoom != ch.InRoom ) { ch.SendText( "They aren't here.\r\n" ); return; } break; case TargetType.self: break; case TargetType.objectInInventory: obj = (Object)target; if( ch.IsAffected( Affect.AFFECT_BLIND ) ) { ch.SendText( "You cannot see to cast that spell.\r\n" ); return; } if( !obj || obj.CarriedBy != ch ) { ch.SendText( "You are not carrying that.\r\n" ); return; } break; case TargetType.objectInRoom: obj = (Object)target; if( ch.IsAffected( Affect.AFFECT_BLIND ) ) { ch.SendText( "You cannot see to cast that spell.\r\n" ); return; } if( !obj || ( obj.CarriedBy != ch && obj.InRoom != ch.InRoom ) ) { ch.SendText( "You do not see that here.\r\n" ); return; } break; case TargetType.objectCorpse: break; case TargetType.singleCharacterRanged: victim = (CharData)target; if( ch.IsAffected( Affect.AFFECT_BLIND ) ) { ch.SendText( "You cannot see to cast that spell.\r\n" ); return; } if( !victim || victim.FlightLevel != ch.FlightLevel || !CharData.CanSee( ch, victim ) ) { ch.SendText( "Your prey has disappeared.\r\n" ); return; } //check that _targetType is still within the spell range if( ch.InRoom == victim.InRoom ) { break; } bool targetInRange = false; int dir; for( dir = 0; dir < Limits.MAX_DIRECTION; dir++ ) { if( !ch.InRoom.ExitData[ dir ] || ch.InRoom.ExitData[ dir ].HasFlag( Exit.ExitFlag.secret ) || ch.InRoom.ExitData[ dir ].HasFlag( Exit.ExitFlag.closed ) || ch.InRoom.ExitData[ dir ].HasFlag( Exit.ExitFlag.blocked ) || ch.InRoom.ExitData[dir].HasFlag(Exit.ExitFlag.walled)) continue; if( ch.InRoom.ExitData[ dir ].TargetRoom == victim.InRoom ) { targetInRange = true; break; } // for fireball we check two rooms away if( ch.InRoom.ExitData[ dir ].TargetRoom && ch.InRoom.ExitData[ dir ].TargetRoom.ExitData[ dir ] && ch.InRoom.ExitData[ dir ].TargetRoom.ExitData[ dir ].TargetRoom == victim.InRoom ) { targetInRange = true; break; } } if( !targetInRange ) { ch.SendText( "They are no longer in range!\r\n" ); return; } break; } // No wait state - we already made them wait. ch.PracticeSpell( spell ); if (ch.IsNPC()) { chance = 85; } else if (ch.HasSpell(spell.Name)) { chance = ((PC)ch).SpellAptitude[spell.Name]; } if( !ch.IsImmortal() && ( MUDMath.NumberPercent() > chance ) ) { ch.SendText( "You lost your concentration.\r\n" ); SocketConnection.Act( "&+r$n&n&+r stops chanting abruptly.&n", ch, null, null, SocketConnection.MessageTarget.room ); } else { // TODO: Figure out whether this should be re-enabled. //if( song ) //{ // ch.SendText( "You complete a verse of the song...\r\n" ); // ch.GainExperience( 1 ); // SaySong( ch, spell ); //} //else { if (!ch.IsClass(CharClass.Names.psionicist)) { ch.SendText( "You complete your spell...\r\n" ); if( MUDString.StringsNotEqual( spell.Name, "ventriloquate" ) ) SaySpell( ch, spell ); } ch.GainExperience( 1 ); } if( !ch.IsNPC() ) { string buf = String.Format( "Spell ({0}) being cast by {1}", spell.Name, ch.Name ); Log.Trace( buf ); } int level = Macros.Range(1, ch.Level, Limits.LEVEL_HERO); spell.Invoke(ch, level, target); if( memorized && !ch.IsNPC() && !ch.IsImmortal() ) { memorized.Memmed = false; memorized.Memtime = memorized.FullMemtime; } } if( ( spell.ValidTargets == TargetType.singleCharacterOffensive || spell.ValidTargets == TargetType.singleCharacterRanged ) && victim && victim.Master != ch && victim != ch && victim.IsAwake() ) { if( ch.InRoom == victim.InRoom ) { if( !victim.Fighting && CharData.CanSee( victim, ch ) ) victim.AttackCharacter( ch ); } else { // Range spell presumably, since different rooms Combat.StartGrudge( victim, ch, true ); foreach( CharData roomChar in ch.InRoom.People ) { if( roomChar == victim ) continue; if( roomChar.FlightLevel != ch.FlightLevel ) continue; //protectors will be aggro'd if (roomChar.HasActionBit(MobTemplate.ACT_PROTECTOR) && (roomChar.GetRace() == victim.GetRace())) { Combat.StartGrudge( roomChar, ch, true ); } // all aggro mobs will hunt down caster if (roomChar.HasActionBit(MobTemplate.ACT_AGGRESSIVE)) { Combat.StartGrudge(roomChar, ch, true); } } } } return; }
/// <summary> /// Song of weakness. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="level"></param> /// <param name="target"></param> /// <returns></returns> public static bool SongWeakling( CharData ch, Spell spell, int level, Target target ) { Affect af = new Affect(); foreach( CharData victim in ch.InRoom.People ) { if (victim.IsAffected( Affect.AFFECT_STRENGTH_REDUCED) || Magic.SpellSavingThrow( level, victim, AttackType.DamageType.black_magic ) ) continue; af.Type = Affect.AffectType.song; af.Value = spell.Name; af.Duration = level / 7; af.AddModifier(Affect.Apply.strength, -( level / 2 )); af.SetBitvector(Affect.AFFECT_STRENGTH_REDUCED); if( level > 25 ) { af.AddModifier( Affect.Apply.damroll, 0 - ( level / 7 )); } victim.AddAffect(af); SocketConnection.Act( "$n&n looks weaker.", victim, null, null, SocketConnection.MessageTarget.room ); victim.SendText( "You feel weaker.\r\n" ); } return true; }
/// <summary> /// TODO: Put Bard song code here. /// </summary> /// <param name="ch"></param> /// <param name="song"></param> /// <param name="target"></param> public static void SongVerse(CharData ch, Song song, Target target) { }
/// <summary> /// Song of sleep. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="level"></param> /// <param name="target"></param> /// <returns></returns> public static bool SongSleep( CharData ch, Spell spell, int level, Target target ) { foreach( CharData victim in ch.InRoom.People ) { if (victim.IsAffected(Affect.AFFECT_SLEEP) || Magic.SpellSavingThrow( level, victim, AttackType.DamageType.charm ) || victim.GetRace() == Race.RACE_VAMPIRE || ch.IsSameGroup( victim ) ) { continue; } Affect af = new Affect(Affect.AffectType.song, spell.Name, level / 8, Affect.Apply.none, 0, Affect.AFFECT_SLEEP); victim.CombineAffect(af); if( victim.IsAwake() ) { victim.SendText( "You feel very sleepy... zzzzz.\r\n" ); if (ch.Fighting || victim.CurrentPosition == Position.fighting) { Combat.StopFighting(victim, false); } CommandType.Interpret( victim, "sleep" ); } } return true; }
/// <summary> /// Song of quagmire (move drain). /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="level"></param> /// <param name="target"></param> /// <returns></returns> public static bool SongQuagmire( CharData ch, Spell spell, int level, Target target ) { foreach( CharData victim in ch.InRoom.People ) { if( victim.CurrentMoves < 0 ) continue; victim.CurrentMoves -= MUDMath.Dice( 2, ( level / 2 ) ) + 5; if( victim.CurrentMoves < 0 ) victim.CurrentMoves = 0; victim.SendText( "Your feet feel mired to the ground.\r\n" ); } return true; }
/// <summary> /// Song of nightmares. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="level"></param> /// <param name="target"></param> /// <returns></returns> public static bool SongNightmares( CharData ch, Spell spell, int level, Target target ) { Affect af = new Affect(); foreach( CharData victim in ch.InRoom.People ) { if (victim.IsAffected(Affect.AFFECT_FEAR) || Magic.SpellSavingThrow(level, victim, AttackType.DamageType.black_magic ) ) { ch.SendText( "You have failed.\r\n" ); ch.SendText( "You resist the urge to panic.\r\n" ); continue; } af.Type = Affect.AffectType.song; af.Value = spell.Name; af.Duration = 1 + ( level / 7 ); af.SetBitvector( Affect.AFFECT_FEAR ); victim.AddAffect(af); SocketConnection.Act( "$N&n is scared!", ch, null, victim, SocketConnection.MessageTarget.character ); victim.SendText( "You are scared!\r\n" ); SocketConnection.Act( "$N&n is scared!", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim ); CommandType.Interpret( victim, "flee" ); if( victim.IsNPC() ) Combat.StartFearing( victim, ch ); } return true; }
/// <summary> /// Forwards to Act with default argument of false. We don't use a default argument version that /// takes 5 or 6 parameters because the spell compiler doesn't like default arguments. /// </summary> /// <param name="format"></param> /// <param name="ch"></param> /// <param name="arg1"></param> /// <param name="arg2"></param> /// <param name="type"></param> public static void Act(string format, CharData ch, Target arg1, Target arg2, MessageTarget type) { Act(format, ch, arg1, arg2, type, false); }
/// <summary> /// Generic spell processing function. Handles basic spells based on values set in the spell file. /// </summary> /// <param name="ch"></param> /// <param name="level"></param> /// <param name="target"></param> public void GenericSpellFunction(CharData ch, int level, Target target) { switch (ValidTargets) { case TargetType.singleCharacterOffensive: { CharData opponent = (CharData)target; if (level > LevelCap) { level = LevelCap; } int damage = MUDMath.Dice( level, DamageDicePerLevel ) + BaseDamage; bool saved = Magic.SpellSavingThrow(level, opponent, DamageInflicted); bool affects = true; if (saved) { switch (SavingThrowEffect) { case SavingThrowResult.negates: damage = 0; affects = false; ch.SendText("Nothing happens.\r\n"); return; case SavingThrowResult.halfDamage: damage /= 2; affects = true; break; case SavingThrowResult.halfDamageNoAffects: damage /= 2; affects = true; break; case SavingThrowResult.fullDamageNoAffects: affects = false; break; case SavingThrowResult.none: affects = true; break; } } if (damage > 0) { Combat.InflictSpellDamage(ch, opponent, damage, this, DamageInflicted); } if (affects) { // Apply "negates" to character. for (int n = 0; n < Negates.Length; n++) { opponent.RemoveAffect(new Bitvector(n, Negates[n])); } Affect af = new Affect(); af.Level = ch.Level; af.BitVectors = Provides; af.Value = Name; af.Type = Affect.AffectType.spell; for (int p = 0; p < Provides.Length; p++) { if (Provides[p] != 0) { if (!ch.IsAffected(new Bitvector(p, Provides[p]))) { opponent.AddAffect(af); if (!String.IsNullOrEmpty(MessageCompleted)) { SocketConnection.Act(MessageCompleted, ch, null, null, SocketConnection.MessageTarget.character); } else { ch.SendText("Ok.\r\n"); } if (!String.IsNullOrEmpty(MessageCompletedToTarget)) { SocketConnection.Act(MessageCompletedToTarget, ch, opponent, null, SocketConnection.MessageTarget.victim); } if (!String.IsNullOrEmpty(MessageCompletedToRoom)) { SocketConnection.Act(this.MessageCompletedToRoom, ch, opponent, null, SocketConnection.MessageTarget.room); } } else { switch (StackingType) { case StackType.addDuration: ch.SendText("Your spell has no effect.\r\n"); throw new NotSupportedException("Spell addDuration stacking types are not yet supported."); case StackType.addModifier: ch.SendText("Your spell has no effect.\r\n"); throw new NotSupportedException("Spell addModifier stacking types are not yet supported."); case StackType.addModifierAddDuration: ch.SendText("Your spell has no effect.\r\n"); throw new NotSupportedException("Spell addModifierAddDuration stacking types are not yet supported."); case StackType.addModifierMaxDuration: ch.SendText("Your spell has no effect.\r\n"); throw new NotSupportedException("Spell addModifierMaxDuration stacking types are not yet supported."); case StackType.alwaysReplace: ch.SendText("Your spell has no effect.\r\n"); throw new NotSupportedException("Spell alwaysReplace stacking types are not yet supported."); case StackType.noRefresh: ch.SendText("Your spell has no effect.\r\n"); break; case StackType.replaceDuration: ch.SendText("Your spell has no effect.\r\n"); throw new NotSupportedException("Spell replaceDuration stacking types are not yet supported."); case StackType.takeMaxDuration: ch.SendText("Your spell has no effect.\r\n"); throw new NotSupportedException("Spell takeMaxDuration stacking types are not yet supported."); case StackType.takeMaxModifier: ch.SendText("Your spell has no effect.\r\n"); throw new NotSupportedException("Spell takeMaxModifier stacking types are not yet supported."); case StackType.takeMaxModifierAndDuration: ch.SendText("Your spell has no effect.\r\n"); throw new NotSupportedException("Spell takeMaxModifierAndDuration stacking types are not yet supported."); } } } } } return; } case TargetType.singleCharacterDefensive: case TargetType.self: { CharData victim; if (ValidTargets == TargetType.self) { victim = ch; } else { victim = (CharData)target; if (victim == null) { victim = ch; } } for (int n = 0; n < Negates.Length; n++) { victim.RemoveAffect(new Bitvector(n, Negates[n])); } Affect af = new Affect(); af.Level = ch.Level; af.BitVectors = Provides; af.Value = Name; af.Type = Affect.AffectType.spell; switch (this.Duration) { case SpellDurationType.oneHourPerlevel: af.Duration = level; break; case SpellDurationType.quarterHourPerLevel: af.Duration = level / 4; break; case SpellDurationType.halfHourPerLevel: af.Duration = level / 2; break; case SpellDurationType.oneDay: af.Duration = 24; break; case SpellDurationType.threeHoursPerLevel: af.Duration = level * 3; break; case SpellDurationType.twoHoursPerLevel: af.Duration = level * 2; break; case SpellDurationType.fourHoursPerLevel: af.Duration = level * 4; break; case SpellDurationType.oneHour: af.Duration = 1; break; case SpellDurationType.permanent: af.Duration = -1; break; case SpellDurationType.sixHours: af.Duration = 6; break; case SpellDurationType.threeHours: af.Duration = 3; break; case SpellDurationType.twoHours: af.Duration = 2; break; default: throw new NotSupportedException("Spells with duration type " + Duration + " are not implemented yet."); } foreach (AffectApplyType apply in Modifies) { af.AddModifier(apply.Location, apply.Amount); } for( int p = 0; p < Provides.Length; p++ ) { if( Provides[p] != 0 ) { if (!ch.IsAffected(new Bitvector(p, Provides[p]))) { victim.AddAffect(af); if (!String.IsNullOrEmpty(MessageCompleted)) { SocketConnection.Act(MessageCompleted, ch, null, null, SocketConnection.MessageTarget.character); } else { ch.SendText("Ok.\r\n"); } if (!String.IsNullOrEmpty(MessageCompletedToTarget)) { SocketConnection.Act(MessageCompletedToTarget, ch, victim, null, SocketConnection.MessageTarget.victim); } if (!String.IsNullOrEmpty(MessageCompletedToRoom)) { SocketConnection.Act(MessageCompletedToRoom, ch, victim, null, SocketConnection.MessageTarget.room); } } else { switch (StackingType) { case StackType.addDuration: ch.SendText("Your spell has no effect.\r\n"); throw new NotSupportedException("Spell addDuration stacking types are not yet supported."); case StackType.addModifier: ch.SendText("Your spell has no effect.\r\n"); throw new NotSupportedException("Spell addModifier stacking types are not yet supported."); case StackType.addModifierAddDuration: ch.SendText("Your spell has no effect.\r\n"); throw new NotSupportedException("Spell addModifierAddDuration stacking types are not yet supported."); case StackType.addModifierMaxDuration: ch.SendText("Your spell has no effect.\r\n"); throw new NotSupportedException("Spell addModifierMaxDuration stacking types are not yet supported."); case StackType.alwaysReplace: ch.SendText("Your spell has no effect.\r\n"); throw new NotSupportedException("Spell alwaysReplace stacking types are not yet supported."); case StackType.noRefresh: ch.SendText("Your spell has no effect.\r\n"); break; case StackType.replaceDuration: ch.SendText("Your spell has no effect.\r\n"); throw new NotSupportedException("Spell replaceDuration stacking types are not yet supported."); case StackType.takeMaxDuration: ch.SendText("Your spell has no effect.\r\n"); throw new NotSupportedException("Spell takeMaxDuration stacking types are not yet supported."); case StackType.takeMaxModifier: ch.SendText("Your spell has no effect.\r\n"); throw new NotSupportedException("Spell takeMaxModifier stacking types are not yet supported."); case StackType.takeMaxModifierAndDuration: ch.SendText("Your spell has no effect.\r\n"); throw new NotSupportedException("Spell takeMaxModifierAndDuration stacking types are not yet supported."); } } } } } break; case TargetType.multipleCharacterOffensive: { } break; case TargetType.objectCorpse: { } break; case TargetType.objectInInventory: { } break; case TargetType.objectInRoom: { } break; case TargetType.objectOrCharacter: { } break; case TargetType.ritual: { } break; case TargetType.singleCharacterRanged: { CharData opponent = (CharData)target; if (level > LevelCap) level = LevelCap; int damage = MUDMath.Dice(level, DamageDicePerLevel) + BaseDamage; bool saved = Magic.SpellSavingThrow(level, opponent, DamageInflicted); bool affects = true; if (saved) { switch (SavingThrowEffect) { case SavingThrowResult.negates: damage = 0; affects = false; break; case SavingThrowResult.halfDamage: damage /= 2; affects = true; break; case SavingThrowResult.halfDamageNoAffects: damage /= 2; affects = false; break; case SavingThrowResult.fullDamageNoAffects: affects = false; break; case SavingThrowResult.none: affects = true; break; } } if (damage > 0) { Combat.InflictSpellDamage(ch, opponent, damage, this, DamageInflicted); } if (affects) { // TODO: Apply "provides" to character. // TODO: Apply "negates" to character. } return; } case TargetType.singleCharacterWorld: { } break; case TargetType.trap: { } break; case TargetType.none: { Log.Error("Magic spell '" + Name + "' is flagged as TargetType.none. This should never happen. Fix it."); } break; } }
/// <summary> /// Creating a portal. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="level"></param> /// <param name="target"></param> /// <param name="indexNumber"></param> public static void MakePortal(CharData ch, Spell spell, int level, Target target, int indexNumber) { Room location; CharData victim = (CharData)target; Room original = ch.InRoom; if (ch.IsNPC()) return; if (!victim) { ch.SendText("Who exactly is your target?\r\n"); return; } if (victim.IsNPC() && !ch.IsImmortal()) return; if (!Magic.HasSpellConsent(ch, victim)) { return; } if (victim == ch || ch.InRoom == victim.InRoom) { ch.SendText("Seems like a waste of time.\r\n"); return; } if (!(location = victim.InRoom) || victim.InRoom.HasFlag(RoomTemplate.ROOM_SAFE) || victim.InRoom.HasFlag(RoomTemplate.ROOM_PRIVATE) || victim.InRoom.HasFlag(RoomTemplate.ROOM_SOLITARY)) { ch.SendText("You can't seem to get a fix their location.\r\n"); return; } if (!victim.IsNPC() && (ch.IsRacewar(victim)) && !ch.IsImmortal()) { ch.SendText("Don't you wish it was that easy!\r\n"); return; } Object portal = Database.CreateObject(Database.GetObjTemplate(indexNumber), 0); if (victim.InRoom.HasFlag(RoomTemplate.ROOM_NO_GATE) || ch.InRoom.HasFlag(RoomTemplate.ROOM_NO_GATE)) { SocketConnection.Act("$p opens for a brief instant and then collapses.&n", ch, portal, null, SocketConnection.MessageTarget.character); SocketConnection.Act("$p opens for a brief instant and then collapses.&n", ch, portal, null, SocketConnection.MessageTarget.room); SocketConnection.Act("$p opens for a brief instant and then collapses.&n", victim, portal, null, SocketConnection.MessageTarget.character); SocketConnection.Act("$p opens for a brief instant and then collapses.&n", victim, portal, null, SocketConnection.MessageTarget.room); portal.RemoveFromWorld(); return; } portal.Timer = level / 15; portal.Values[2] = level / 7; portal.Values[0] = location.IndexNumber; portal.AddToRoom(original); portal = Database.CreateObject(Database.GetObjTemplate(indexNumber), 0); portal.Timer = level / 15; portal.Values[2] = level / 7; portal.Values[0] = original.IndexNumber; portal.AddToRoom(location); switch (indexNumber) { case StaticObjects.OBJECT_NUMBER_PORTAL: SocketConnection.Act("$p&+Y rises up from the ground.&n", ch, portal, null, SocketConnection.MessageTarget.room); SocketConnection.Act("$p&+Y rises up before you.&n", ch, portal, null, SocketConnection.MessageTarget.character); if (location.People.Count > 0) { SocketConnection.Act("$p&+Y rises up from the ground.&n", location.People[0], portal, null, SocketConnection.MessageTarget.room); SocketConnection.Act("$p&+Y rises up from the ground.&n", location.People[0], portal, null, SocketConnection.MessageTarget.character); } break; case StaticObjects.OBJECT_NUMBER_MOONWELL: SocketConnection.Act("&+WSilver mists swirl and form into a $p.&n", ch, portal, null, SocketConnection.MessageTarget.room); SocketConnection.Act("&+WSilver mists swirl and form into a $p.&n", ch, portal, null, SocketConnection.MessageTarget.character); if (location.People.Count > 0) { SocketConnection.Act("&+WSilver mists swirl and form into a $p.&n", location.People[0], portal, null, SocketConnection.MessageTarget.room); SocketConnection.Act("&+WSilver mists swirl and form into a $p.&n", location.People[0], portal, null, SocketConnection.MessageTarget.character); } break; case StaticObjects.OBJECT_NUMBER_WORMHOLE: SocketConnection.Act("$p&+L appears from a warping of space and time.&n", ch, portal, null, SocketConnection.MessageTarget.room); SocketConnection.Act("$p&+L appears from a warping of space and time.&n", ch, portal, null, SocketConnection.MessageTarget.character); if (location.People.Count > 0) { SocketConnection.Act("$p&+L appears from a warping of space and time.&n", location.People[0], portal, null, SocketConnection.MessageTarget.room); SocketConnection.Act("$p&+L appears from a warping of space and time.&n", location.People[0], portal, null, SocketConnection.MessageTarget.character); } break; } ch.WaitState(8); return; }
/// <summary> /// Invokes a spell by calling either that spell's compiled code or the generic spell processing /// function, whichever is appropriate. /// </summary> /// <param name="ch"></param> /// <param name="level"></param> /// <param name="target"></param> public void Invoke(CharData ch, int level, Target target) { if (CompiledCode == null) { GenericSpellFunction(ch, level, target); } else { object o = CompiledCode.CreateInstance("ModernMUD.SpellScript"); Type type = o.GetType(); MethodInfo[] methods = type.GetMethods(); try { type.InvokeMember("Execute", BindingFlags.Default|BindingFlags.InvokeMethod, null, o, new object[] { ch, this, level, target }); } catch (Exception ex) { Log.Error("Error executing spell code for " + Name + ":" + ex.ToString()); } } }
public void ImprintSpell(Spell spell, int level, Target target) { int[] sucessRate = new[] { 80, 30, 25, 10 }; string text; Object obj = (Object)target; int freeSlots; int i; if (spell == null) { SendText("That is not a spell.\r\n"); return; } for (freeSlots = i = 1; i < 5; i++) if (obj.Values[i] != -1) freeSlots++; if (freeSlots > 4) { SocketConnection.Act("$p&n cannot contain any more spells.", this, obj, null, SocketConnection.MessageTarget.character); return; } int mana = 4 * Macros.ManaCost(this, spell); if (!IsNPC() && CurrentMana < mana) { SendText("You don't have enough mana.\r\n"); return; } if (MUDMath.NumberPercent() > ((PC)this).SpellAptitude[spell.Name] && (Level <= (spell.SpellCircle[(int)this.CharacterClass.ClassNumber] * 4 + 1))) { SendText("You lost your concentration.\r\n"); SocketConnection.Act("&+r$n&n&+r stops chanting abruptly.&n", this, null, null, SocketConnection.MessageTarget.room); CurrentMana -= mana / 2; return; } CurrentMana -= mana; // TODO: FIXME: BUG: Can't cram a string into a integer value inside an object. // This is a problem because it makes it impossible for objects to contain spells. //obj._values[free_slots] = spell; if (MUDMath.NumberPercent() > sucessRate[freeSlots - 1]) { text = String.Format("The magic enchantment has failed: the {0} vanishes.\r\n", StringConversion.ItemTypeString(obj)); SendText(text); obj.RemoveFromWorld(); ; return; } obj.ShortDescription = String.Empty; text = String.Format("a {0} of ", StringConversion.ItemTypeString(obj)); for (i = 1; i <= freeSlots; i++) { if (obj.Values[i] != -1) { text += SpellNumberToTextMap.GetSpellNameFromNumber(obj.Values[i]); if (i != freeSlots) { text += ", "; } else { text += String.Empty; } } } obj.ShortDescription = text; text = String.Format("{0} {1}", obj.Name, StringConversion.ItemTypeString(obj)); obj.Name = text; text = String.Format("You have imbued a new spell to the {0}.\r\n", StringConversion.ItemTypeString(obj)); SendText(text); return; }
/// <summary> /// Utility function to create a wall. Called by create wall spells. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="level"></param> /// <param name="target"></param> /// <param name="indexNumber"></param> /// <returns></returns> public static Object MakeWall(CharData ch, Spell spell, int level, Target target, int indexNumber) { if (ch == null || spell == null) return null; Object wall2; Exit exit; string targetName = (string)target; // If we were really smooth we would just create another _targetType called // tar_exit if (String.IsNullOrEmpty(targetName)) { ch.SendText("Specify a direction in which to cast the spell!\r\n"); return null; } Exit.Direction door = Movement.FindExit(ch, targetName); if (door == Exit.Direction.invalid) { ch.SendText("You failed!\r\n"); return null; } // The exit of the same direction should be flagged Exit.ExitFlags.walled if (!(exit = ch.InRoom.GetExit(door))) { ch.SendText("You failed!\r\n"); return null; } if (exit.HasFlag(Exit.ExitFlag.walled)) { ch.SendText("There's already a wall there!\r\n"); return null; } if (exit.HasFlag(Exit.ExitFlag.is_door)) { return null; } ObjTemplate wallTemplate = Database.GetObjTemplate(indexNumber); if (!wallTemplate) { string output = String.Format("MakeWall: null wall pointer from Database.GetObjTemplate( {0} ).", indexNumber); ImmortalChat.SendImmortalChat(null, ImmortalChat.IMMTALK_SPAM, 0, output); Log.Error(output, 0); ch.SendText("Uh oh, no template found for that wall.\r\n"); return null; } Object wall = Database.CreateObject(Database.GetObjTemplate(indexNumber), 0); if (!wall) { Log.Error("MakeWall: null wall pointer from Database.CreateObject", 0); ch.SendText("Whoops, failed to create wall. Report this as a bug.\r\n"); return null; } // Value[0] should be the direction that is blocked by the wall. wall.Values[0] = (int)door; // Set the wall's level wall.Values[2] = level; string text = String.Format("{0} {1} exit.&n", wall.FullDescription, door.ToString()); wall.FullDescription = text; exit.AddFlag(Exit.ExitFlag.walled); if (indexNumber == StaticObjects.OBJECT_NUMBER_WALL_ILLUSION) { exit.AddFlag(Exit.ExitFlag.illusion); } wall.Timer = ch.Level / 3; wall.Level = ch.Level; wall.AddToRoom(ch.InRoom); // Create the wall on the other side if (exit.TargetRoom) { if (exit.TargetRoom.ExitData[(int)Exit.ReverseDirection(door)]) { //we actually have a matching exit wall2 = Database.CreateObject(Database.GetObjTemplate(indexNumber), 0); // Value[0] should be the direction that is blocked by the wall. wall2.Values[0] = (int)Exit.ReverseDirection(door); // Set the wall's level wall2.Values[2] = level; text = String.Format("{0} {1} exit.&n", wall2.FullDescription, Exit.ReverseDirection(door).ToString()); wall2.FullDescription = text; if (exit.TargetRoom.ExitData[(int)Exit.ReverseDirection(door)]) { exit.TargetRoom.ExitData[(int)Exit.ReverseDirection(door)].AddFlag(Exit.ExitFlag.walled); } wall2.Timer = ch.Level / 2; wall2.Level = ch.Level; wall2.AddToRoom(Room.GetRoom(exit.IndexNumber)); if (Room.GetRoom(exit.IndexNumber).People.Count > 0) { text = String.Format("$p&n appears to the {0}.", Exit.ReverseDirection(door).ToString()); SocketConnection.Act(text, Room.GetRoom(exit.IndexNumber).People[0], wall2, null, SocketConnection.MessageTarget.all); } } //end if matching exit } return wall; }
/// <summary> /// Song of idiocy. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="level"></param> /// <param name="target"></param> /// <returns></returns> public static bool SongIdiocy( CharData ch, Spell spell, int level, Target target ) { Affect af = new Affect(); foreach( CharData victim in ch.InRoom.People ) { if (victim.IsAffected(Affect.AFFECT_FEEBLEMIND) || Magic.SpellSavingThrow( level, victim, AttackType.DamageType.black_magic ) ) { ch.SendText( "You failed!\r\n" ); continue; } af.Type = Affect.AffectType.song; af.Value = spell.Name; af.Duration = level / 9; af.AddModifier(Affect.Apply.intelligence, 0 - (level + 15)); af.SetBitvector(Affect.AFFECT_FEEBLEMIND); victim.AddAffect(af); SocketConnection.Act( "A dumb look crosses $n&n's face and $e starts to drool.", victim, null, null, SocketConnection.MessageTarget.room ); victim.SendText( "You feel _REALLY_ dumb.\r\n" ); } return true; }
/// <summary> /// Song of invisibility. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="level"></param> /// <param name="target"></param> /// <returns></returns> public static bool SongInvisibility( CharData ch, Spell spell, int level, Target target ) { int total = 0; int max = level / 8; foreach( CharData victim in ch.InRoom.People ) { if( !victim.IsSameGroup( ch ) || victim.IsAffected( Affect.AFFECT_INVISIBLE ) ) continue; if( total >= max ) return true; victim.SendText( "You slowly fade out of existence.\r\n" ); SocketConnection.Act( "$n&n slowly fades out of existence.", victim, null, null, SocketConnection.MessageTarget.room ); Affect af = new Affect(Affect.AffectType.song, spell.Name, (level / 6), Affect.Apply.none, 0, Affect.AFFECT_INVISIBLE); victim.AddAffect(af); total++; } foreach( Object obj in ch.InRoom.Contents ) { if( obj.HasFlag( ObjTemplate.ITEM_INVIS ) ) continue; if( total >= max ) return true; SocketConnection.Act( "&+L$p&+L fades away.", ch, obj, null, SocketConnection.MessageTarget.room ); SocketConnection.Act( "&+L$p&+L fades away.", ch, obj, null, SocketConnection.MessageTarget.character ); obj.AddFlag( ObjTemplate.ITEM_INVIS ); total++; } return true; }
/// <summary> /// The primary output interface for formatted output. /// </summary> /// <param name="format">Format string for output.</param> /// <param name="actor">The actor (or TargetType, depending on format string).</param> /// <param name="arg1">Argument Target, use varies based on format string.</param> /// <param name="arg2">Argument Target, use varies based on format string</param> /// <param name="type">Target of Act statement.</param> /// <param name="capitalize">If capitalize is set, the first word of the phrase will be capitalized (after variable substitution).</param> public static void Act( string format, CharData actor, Target arg1, Target arg2, MessageTarget type, bool capitalize ) { Object obj1 = (Object)arg1; Object obj2 = (Object)arg2; CharData victimChar = (CharData)arg2; string[] himHer = new[] { "it", "him", "her" }; string[] hisHer = new[] { "its", "his", "her" }; string str; // Discard null and zero-length messages. if( format.Length == 0 ) { return; } if( actor == null ) { Log.Error( "Act: null actor!", 0 ); return; } // To prevent crashes if( !actor.InRoom ) { Log.Error( "Act: Actor CharData (" + actor.Name + ") is not in a room!" ); return; } List<CharData> to = actor.InRoom.People; if( type == MessageTarget.victim || type == MessageTarget.room_vict ) { if( !victimChar ) { Log.Error( "Act: null victim with Descriptor.MessageTarget.victim.", 0 ); Log.Error(String.Format("Bad act string: {0}", format)); return; } to = victimChar.InRoom.People; } string outputBuffer; foreach (CharData roomChar in to) { if( !roomChar.Socket && roomChar.IsNPC() || !roomChar.IsAwake() ) continue; if( type == MessageTarget.room_vict && victimChar.FlightLevel != roomChar.FlightLevel ) continue; if( ( type == MessageTarget.room_vict || type == MessageTarget.room || type == MessageTarget.everyone_but_victim ) && actor.FlightLevel != roomChar.FlightLevel ) continue; if( type == MessageTarget.room_vict && ( roomChar == actor || roomChar == victimChar ) ) continue; if( type == MessageTarget.character && roomChar != actor ) continue; if( type == MessageTarget.victim && ( roomChar != victimChar || roomChar == actor ) ) continue; if( type == MessageTarget.room && roomChar == actor ) continue; if( type == MessageTarget.everyone_but_victim && ( roomChar == victimChar ) ) continue; if( type == MessageTarget.room_above && ( roomChar.FlightLevel <= actor.FlightLevel ) ) continue; if( type == MessageTarget.room_below && ( roomChar.FlightLevel >= actor.FlightLevel ) ) continue; str = format; if (str.Contains("$t")) str = str.Replace("$t", (String)arg1); if (str.Contains("$T")) str = str.Replace("$T", (String)arg2); if (str.Contains("$n") && actor != null) str = str.Replace("$n", actor.ShowNameTo(roomChar, false)); if (str.Contains("$N") && victimChar != null) str = str.Replace("$N", victimChar.ShowNameTo(roomChar, false)); if (str.Contains("$e") && actor != null) str = str.Replace("$e", actor.GetSexPronoun()); if (str.Contains("$E") && victimChar != null) str = str.Replace("$E", victimChar.GetSexPronoun()); if (str.Contains("$m") && actor != null) str = str.Replace("$m", himHer[Macros.Range(0, (int)actor.Gender, 2)]); if (str.Contains("$M") && victimChar != null) str = str.Replace("$M", himHer[Macros.Range(0, (int)victimChar.Gender, 2)]); if (str.Contains("$s") && actor != null) str = str.Replace("$s", hisHer[Macros.Range(0, (int)actor.Gender, 2)]); if (str.Contains("$S") && victimChar != null) str = str.Replace("$S", hisHer[Macros.Range(0, (int)victimChar.Gender, 2)]); if (str.Contains("$p") && obj1 != null) str = str.Replace("$p", CharData.CanSeeObj(roomChar, obj1) ? obj1.ShortDescription : "something"); if (str.Contains("$P") && obj1 != null) str = str.Replace("$P", CharData.CanSeeObj(roomChar, obj2) ? obj2.ShortDescription : "something"); if (str.Contains("$d")) { if (((string)arg2).Length == 0) { str = str.Replace("$d", "door"); } else { str = str.Replace("$d", (string)arg2); } } str += "\r\n"; ColorConvert( out outputBuffer, str, roomChar ); if (capitalize) { outputBuffer = MUDString.CapitalizeANSIString(outputBuffer); } if (roomChar.Socket) { roomChar.Socket.WriteToBuffer(outputBuffer); } } return; }
/// <summary> /// Song of obscrurement. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="level"></param> /// <param name="target"></param> /// <returns></returns> public static bool SongObscurement( CharData ch, Spell spell, int level, Target target ) { Affect af = new Affect(); foreach( CharData victim in ch.InRoom.People ) { if( victim.IsAffected( Affect.AFFECT_MINOR_INVIS ) || victim.IsAffected( Affect.AFFECT_INVISIBLE ) ) return true; SocketConnection.Act( "$n&n fades out of existence.", ch, null, null, SocketConnection.MessageTarget.room ); ch.SendText( "You vanish.\r\n" ); af.Type = Affect.AffectType.song; af.Value = spell.Name; af.Duration = level / 6; af.SetBitvector( Affect.AFFECT_MINOR_INVIS ); victim.AddAffect(af); } return true; }
/// <summary> /// Song of cowardice. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="level"></param> /// <param name="target"></param> /// <returns></returns> public static bool SongCowardice( CharData ch, Spell spell, int level, Target target ) { foreach( CharData victim in ch.InRoom.People ) { if( !victim.IsSameGroup( ch ) ) { Affect af = new Affect(Affect.AffectType.song, spell.Name, (level / 6 + 1), Affect.Apply.hitroll, (0 - (level / 3)), Affect.AFFECT_COWARDLY); victim.AddAffect(af); SocketConnection.Act( "$n&n looks unsure of $mself.", victim, null, null, SocketConnection.MessageTarget.room ); victim.SendText( "You feel less confident about your battle skills.\r\n" ); } } return true; }
/// <summary> /// Song of revelation. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="level"></param> /// <param name="target"></param> /// <returns></returns> public static bool SongRevelation( CharData ch, Spell spell, int level, Target target ) { foreach( CharData victim in ch.InRoom.People ) { victim.AffectStrip( Affect.AffectType.skill, "shadow form"); victim.RemoveAffect(Affect.AFFECT_HIDE); victim.RemoveAffect(Affect.AFFECT_INVISIBLE); SocketConnection.Act( "$n&n is revealed!", victim, null, null, SocketConnection.MessageTarget.room ); victim.SendText( "You are revealed!\r\n" ); } return true; }
/// <summary> /// Song of feasting. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="level"></param> /// <param name="target"></param> /// <returns></returns> public static bool SongFeasting( CharData ch, Spell spell, int level, Target target ) { foreach( CharData victim in ch.InRoom.People ) { if( victim.IsNPC() ) continue; int amount = level / 7 + 1; if( ( (PC)victim ).Hunger < 48 ) ( (PC)victim ).Hunger += amount; if( ( (PC)victim ).Thirst < 48 ) ( (PC)victim ).Thirst += amount; victim.SendText( "You stomach feels fuller.\r\n" ); } return true; }
/// <summary> /// Song of slowness. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="level"></param> /// <param name="target"></param> /// <returns></returns> public static bool SongSlowness( CharData ch, Spell spell, int level, Target target ) { Affect af = new Affect(); foreach( CharData victim in ch.InRoom.People ) { if( Magic.SpellSavingThrow( level, victim, AttackType.DamageType.magic_other ) ) { ch.SendText( "You failed!\r\n" ); continue; } // Removes haste, takes two castings to make a hasted person slowed if (victim.IsAffected( Affect.AFFECT_HASTE)) { victim.RemoveAffect(Affect.AFFECT_HASTE); victim.SendText( "You slow to your normal speed.\r\n" ); continue; } if (victim.IsAffected(Affect.AFFECT_SLOWNESS)) continue; af.Type = Affect.AffectType.song; af.Value = spell.Name; af.Duration = 6; af.SetBitvector( Affect.AFFECT_SLOWNESS ); victim.AddAffect(af); SocketConnection.Act( "&+R$n&+R moves much more slowly.&n", victim, null, null, SocketConnection.MessageTarget.room ); victim.SendText( "&+RYou feel yourself slowing down.&n\r\n" ); } return true; }
/// <summary> /// Song of flight. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="level"></param> /// <param name="target"></param> /// <returns></returns> public static bool SongFlight( CharData ch, Spell spell, int level, Target target ) { foreach( CharData victim in ch.InRoom.People ) { if (victim.IsAffected(Affect.AFFECT_FLYING)) return true; Affect af = new Affect(Affect.AffectType.song, spell.Name, (level / 6), Affect.Apply.none, 0, Affect.AFFECT_FLYING); victim.AddAffect(af); victim.SendText( "&+WYour feet rise off the ground.&n\r\n" ); SocketConnection.Act( "$n&n rises off the ground.", victim, null, null, SocketConnection.MessageTarget.room ); } return true; }
/// <summary> /// Song of warding, protective. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="level"></param> /// <param name="target"></param> /// <returns></returns> public static bool SongWarding( CharData ch, Spell spell, int level, Target target ) { Affect af = new Affect(); foreach( CharData victim in ch.InRoom.People ) { af.Type = Affect.AffectType.song; af.Value = spell.Name; af.Duration = level / 7; af.AddModifier(Affect.Apply.save_spell, 0 - ( ( level / 5 ) + 1 )); af.AddModifier(Affect.Apply.save_paralysis, 0 - ((level / 5) + 1)); af.AddModifier(Affect.Apply.save_petrification, 0 - ((level / 5) + 1)); af.AddModifier(Affect.Apply.save_poison, 0 - ((level / 5) + 1)); af.AddModifier(Affect.Apply.save_breath, 0 - ((level / 5) + 1)); af.SetBitvector(Affect.AFFECT_NONE); victim.AddAffect(af); } return true; }
/// <summary> /// Casts a spell using a magical object. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="level"></param> /// <param name="victim"></param> /// <param name="obj"></param> public static void ObjectCastSpell( CharData ch, Spell spell, int level, CharData victim, Object obj ) { Target target; if( spell == null || obj == null ) { return; } switch( spell.ValidTargets ) { default: Log.Error( "ObjectCastSpell: bad TargetType for spell {0}.", spell.Name ); return; case TargetType.none: target = null; break; case TargetType.trap: return; case TargetType.singleCharacterOffensive: if( !victim ) victim = ch.Fighting; if( !victim ) { ch.SendText( "You can't do that.\r\n" ); return; } if( Combat.IsSafe( ch, victim ) ) return; // Combat.IsSafe could wipe out victim, as it calls procs if a boss // check and see that victim is still valid if( !victim ) return; Crime.CheckAttemptedMurder( ch, victim ); target = new Target( victim ); break; case TargetType.singleCharacterDefensive: if( !victim ) victim = ch; target = new Target( victim ); break; case TargetType.self: target = new Target( ch ); break; case TargetType.objectInInventory: if( !obj ) { ch.SendText( "You can't do that.\r\n" ); return; } target = new Target( obj ); break; case TargetType.objectInRoom: if( !obj ) { ch.SendText( "You can't do that.\r\n" ); return; } target = new Target( obj ); break; case TargetType.objectCorpse: target = new Target( obj ); break; } spell.Invoke(ch, level, target); if( spell.ValidTargets == TargetType.singleCharacterOffensive && victim.Master != ch && ch != victim ) { foreach( CharData roomChar in ch.InRoom.People ) { if( victim == roomChar && !victim.Fighting ) { victim.AttackCharacter( ch ); break; } } } return; }
/// <summary> /// Song of healing. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="level"></param> /// <param name="target"></param> /// <returns></returns> public static bool SongHealing( CharData ch, Spell spell, int level, Target target ) { foreach( CharData victim in ch.InRoom.People ) { if( !ch.IsSameGroup( victim ) ) continue; int heal = MUDMath.Dice( 4, ( level / 3 ) ) + 1; if( victim.Hitpoints < victim.GetMaxHit() ) victim.Hitpoints = Math.Min( victim.Hitpoints + heal, victim.GetMaxHit() ); victim.UpdatePosition(); victim.SendText( "&+WYour wounds begin to heal.&n\r\n" ); } return true; }
/// <summary> /// Called by the cast function to process a spell's targets and react accordingly. /// </summary> /// <param name="ch"></param> /// <param name="spell"></param> /// <param name="argument"></param> private static void ProcessSpellTargets(CharData ch, Spell spell, string argument) { Object obj = null; Target target = null; CharData victim = null; if (!ch.IsClass(CharClass.Names.bard)) switch (spell.ValidTargets) { default: Log.Error("Magic.Cast: bad target type for spell {0}. Type is: {1}", spell.Name, spell.ValidTargets.ToString()); return; case TargetType.none: target = new Target(argument); break; case TargetType.singleCharacterWorld: victim = ch.GetCharWorld(argument); if (victim == null) { ch.SendText("Cast the spell on whom?\r\n"); return; } target = new Target(victim); break; case TargetType.trap: ch.SendText("You cannot cast a trap!\r\n"); return; case TargetType.singleCharacterOffensive: if (String.IsNullOrEmpty(argument)) { victim = ch.Fighting; if (victim == null) { if (ch.IsClass(CharClass.Names.psionicist)) ch.SendText("Will the spell upon whom?\r\n"); else ch.SendText("Cast the spell upon whom?\r\n"); return; } } else { victim = ch.GetCharRoom(argument); if (!victim) { ch.SendText("They aren't here.\r\n"); return; } } if (ch.IsAffected( Affect.AFFECT_CHARM) && ch.Master == victim) { ch.SendText("You can't do that to your master!.\r\n"); return; } if (Combat.IsSafe(ch, victim)) return; // Combat.IsSafe could wipe out victim, as it calls procs if a boss // check and see that victim is still valid if (!victim) return; Crime.CheckAttemptedMurder(ch, victim); target = new Target(victim); ch.BreakInvisibility(); break; case TargetType.singleCharacterDefensive: if (String.IsNullOrEmpty(argument)) { victim = ch; } else { victim = ch.GetCharRoom(argument); if (victim == null) { ch.SendText("They aren't here.\r\n"); return; } } target = new Target(victim); break; case TargetType.self: if (!String.IsNullOrEmpty(argument) && !MUDString.NameContainedIn(argument, ch.Name) && "me".Equals(argument, StringComparison.CurrentCultureIgnoreCase) && "self".Equals(argument, StringComparison.CurrentCultureIgnoreCase)) { ch.SendText("You cannot cast this spell on another.\r\n"); return; } target = new Target(ch); break; case TargetType.objectInInventory: if (String.IsNullOrEmpty(argument)) { ch.SendText("What item should the spell be cast upon?\r\n"); return; } obj = ch.GetObjCarrying(argument); if (obj == null) { ch.SendText("You are not carrying that.\r\n"); return; } target = new Target(obj); break; case TargetType.objectInRoom: if (String.IsNullOrEmpty(argument)) { ch.SendText("What should the spell be cast upon?\r\n"); return; } obj = ch.GetObjHere(argument); if (obj == null) { ch.SendText("You do not see that here.\r\n"); return; } target = new Target(obj); break; case TargetType.objectCorpse: target = new Target(argument); break; case TargetType.objectOrCharacter: if (String.IsNullOrEmpty(argument)) { if (ch.Fighting != null) victim = ch.Fighting; else { ch.SendText("Cast upon what?\r\n"); return; } } else if (!(victim = ch.GetCharRoom(argument))) { obj = ch.GetObjHere(argument); } if (victim != null) { target = new Target(victim); } else if (obj != null) { target = new Target(obj); } else { ch.SendText("You do not see that here.\r\n"); return; } break; case TargetType.singleCharacterRanged: if (String.IsNullOrEmpty(argument)) { victim = ch.Fighting; if (victim == null) { ch.SendText("Cast the spell on whom?\r\n"); return; } } else { victim = ch.GetCharRoom(argument); if (!victim) { ch.SendText("They aren't here.\r\n"); return; } // Ranged combat. // // TODO: FIXME: The next line does not successfully get the required argument. //int dir = Movement.FindExit(ch, arg3); //if (ch._level >= Limits.LEVEL_IMMORTAL) //{ // buf = String.Format("Looking for {0} to the {1}.\r\n", arg2, arg3); // ch.SendText(buf); //} //if (ch._inRoom._exitData[dir].HasFlag(Exit.ExitFlags.walled) // || ch._inRoom._exitData[dir].HasFlag(Exit.ExitFlags.blocked) // || ch._inRoom._exitData[dir].HasFlag(Exit.ExitFlags.secret) // || ch._inRoom._exitData[dir].HasFlag(Exit.ExitFlags.closed) // || !ch._inRoom._exitData[dir]._targetRoom // || ch._inRoom._area != ch._inRoom._exitData[dir]._targetRoom._area) //{ // ch.SendText("You see nothing in that direction.\r\n"); // return; //} //room2 = Movement.FindRoom(ch, arg3); //if (room2 == null) //{ // ch.SendText("You see nothing in that direction.\r\n"); // return; //} //victim = CharData.GetCharAtRoom(room2, ch, arg2); //if (victim == null) //{ // Room room3; // if (room2._exitData[dir] && ((room3 = Room.GetRoom(room2._exitData[dir]._vnum))) && // spell == Spell.SpellList["fireball"]) // { // victim = CharData.GetCharAtRoom(room3, ch, arg2); // } //} //if (victim == null) //{ // ch.SendText("They aren't here.\r\n"); // return; //} //} //end else } //end else if (ch.IsAffected(Affect.AFFECT_CHARM) && ch.Master == victim) { ch.SendText("You can't do that to your master!.\r\n"); return; } if (Combat.IsSafe(ch, victim)) return; // Combat.IsSafe could wipe out victim, as it calls procs if a boss // check and see that victim is still valid if (!victim) return; Crime.CheckAttemptedMurder(ch, victim); target = new Target(victim); ch.BreakInvisibility(); break; } int beats = 0; // For quick chant if (!ch.IsClass(CharClass.Names.bard) && !ch.IsClass(CharClass.Names.psionicist)) { ch.SendText( "You begin casting...\r\n" ); if( "ventriloquate".Equals(spell.Name, StringComparison.CurrentCultureIgnoreCase )) { if( spell.ValidTargets == TargetType.singleCharacterOffensive || spell.ValidTargets == TargetType.singleCharacterRanged ) SocketConnection.Act( "$n&n begins casting an offensive spell...", ch, null, null, SocketConnection.MessageTarget.room ); else SocketConnection.Act( "$n&n begins casting...", ch, null, null, SocketConnection.MessageTarget.room ); } beats = spell.CastingTime; } else if (ch.IsClass(CharClass.Names.bard)) { ch.SendText( "You begin singing...\r\n" ); SocketConnection.Act( "$n&n starts singing...", ch, null, null, SocketConnection.MessageTarget.room ); beats = 0; } if (!ch.IsClass(CharClass.Names.psionicist) && !ch.IsClass(CharClass.Names.bard)) { // Characters with int of 110 have normal memtimes. // int of 100 worsens casting times by 10% // with an int of 55 casting times are doubled. // This may seem a bit harsh, but keep in mind any // casters are very likely to have an int above 100, as it // is their prime requisite. 120 is the max int for Grey Elves // to start. if (ch.IsClass(CharClass.Names.cleric) || ch.IsClass(CharClass.Names.druid) || ch.IsClass(CharClass.Names.paladin) || ch.IsClass(CharClass.Names.antipaladin)) { beats = ( beats * 110 ) / ch.GetCurrWis(); } else if (ch.IsClass( CharClass.Names.shaman)) { beats = ( beats * 330 ) / ( ch.GetCurrInt() + ( ch.GetCurrWis() * 2 ) ); } else { beats = ( beats * 110 ) / ch.GetCurrInt(); } if( ch.CheckSkill("quick chant", PracticeType.only_on_success) ) { beats = beats / 2; } /* * A check for impossibly long cast times...came about from a player * trying to cast when feebleminded. 100 casting time is arbitrary. */ if( beats > 100 ) { ch.SendText( "Forget it! In your present state you haven't a dream of success.\r\n" ); return; } ch.WaitState( beats ); if( CheckHypnoticPattern( ch ) ) { return; } CastData caster = new CastData(); caster.Who = ch; caster.Eventdata = Event.CreateEvent( Event.EventType.spell_cast, beats, ch, target, spell ); Database.CastList.Add( caster ); ch.SetAffectBit( Affect.AFFECT_CASTING ); } else if (ch.IsClass( CharClass.Names.psionicist)) { ch.WaitState( beats ); if( CheckHypnoticPattern( ch ) ) { return; } ch.PracticeSpell( spell ); int mana = 0; if( !ch.IsImmortal() && !ch.IsNPC() && ch.Level < ( spell.SpellCircle[ (int)ch.CharacterClass.ClassNumber ] * 4 + 1 ) && MUDMath.NumberPercent() > ((PC)ch).SpellAptitude[spell.Name]) { ch.SendText( "You lost your concentration.\r\n" ); SocketConnection.Act( "&+r$n&n&+r's face flushes white for a moment.&n", ch, null, null, SocketConnection.MessageTarget.room ); ch.CurrentMana -= mana / 2; } else { ch.CurrentMana -= mana; string buf = String.Format( "Spell {0} ({1}) being willed by {2}", spell, spell.Name, ch.Name ); Log.Trace( buf ); ch.SetAffectBit( Affect.AFFECT_CASTING ); FinishSpell( ch, spell, target ); } if( ch.CurrentPosition > Position.sleeping && ch.CurrentMana < 0 ) { ch.WaitState( 2 * Event.TICK_PER_SECOND ); ch.SendText( "&+WThat last spe&+wll w&+Las a _bitvector&+l much...&n\r\n" ); ch.CurrentPosition = Position.standing; ch.Fighting = null; SocketConnection.Act( "$n&n collapses from exhaustion&n.", ch, null, null, SocketConnection.MessageTarget.room ); ch.CurrentPosition = Position.sleeping; } if( spell.ValidTargets == TargetType.singleCharacterOffensive && victim && victim.Master != ch && victim != ch && victim.IsAwake() ) { foreach( CharData roomChar in ch.InRoom.People ) { if( victim == roomChar && !victim.Fighting ) { victim.AttackCharacter( ch ); break; } } } } else if( ch.IsClass(CharClass.Names.bard )) { ch.WaitState( 0 ); // Create an event to handle the spell CastData caster = new CastData(); caster.Who = ch; caster.Eventdata = Event.CreateEvent( Event.EventType.bard_song, Event.TICK_SONG, ch, target, spell ); caster.Eventdata = Event.CreateEvent( Event.EventType.bard_song, Event.TICK_SONG * 2, ch, target, spell); caster.Eventdata = Event.CreateEvent( Event.EventType.bard_song, Event.TICK_SONG * 3, ch, target, spell); caster.Eventdata = Event.CreateEvent( Event.EventType.bard_song, Event.TICK_SONG * 4, ch, target, spell); caster.Eventdata = Event.CreateEvent( Event.EventType.bard_song, Event.TICK_SONG * 5, ch, target, spell); Database.CastList.Add( caster ); ch.SetAffectBit( Affect.AFFECT_SINGING ); } return; }