public override void OnCollideObject(WorldObject target) { //Console.WriteLine($"{Name}.OnCollideObject({target.Name})"); var player = ProjectileSource as Player; if (Info != null && player != null && player.DebugSpell) { player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{Name}.OnCollideObject({target?.Name} ({target?.Guid}))", ChatMessageType.Broadcast)); player.Session.Network.EnqueueSend(new GameMessageSystemChat(Info.ToString(), ChatMessageType.Broadcast)); } ProjectileImpact(); // ensure valid creature target var creatureTarget = target as Creature; if (creatureTarget == null || target == ProjectileSource) { return; } if (player != null) { player.LastHitSpellProjectile = Spell; } // ensure caster can damage target var sourceCreature = ProjectileSource as Creature; if (sourceCreature != null && !sourceCreature.CanDamage(creatureTarget)) { return; } // if player target, ensure matching PK status var targetPlayer = creatureTarget as Player; var pkError = ProjectileSource?.CheckPKStatusVsTarget(creatureTarget, Spell); if (pkError != null) { if (player != null) { player.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(player.Session, pkError[0], creatureTarget.Name)); } if (targetPlayer != null) { targetPlayer.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(targetPlayer.Session, pkError[1], ProjectileSource.Name)); } return; } var critical = false; var critDefended = false; var overpower = false; var damage = CalculateDamage(ProjectileSource, Caster, creatureTarget, ref critical, ref critDefended, ref overpower); if (damage != null) { // handle void magic DoTs: // instead of instant damage, add DoT to target's enchantment registry if (Spell.School == MagicSchool.VoidMagic && Spell.Duration > 0) { var dot = ProjectileSource.CreateEnchantment(creatureTarget, ProjectileSource, Spell); if (dot.Message != null && player != null) { player.Session.Network.EnqueueSend(dot.Message); } // corruption / corrosion playscript? //target.EnqueueBroadcast(new GameMessageScript(target.Guid, PlayScript.HealthDownVoid)); //target.EnqueueBroadcast(new GameMessageScript(target.Guid, PlayScript.DirtyFightingDefenseDebuff)); } else { DamageTarget(creatureTarget, damage.Value, critical, critDefended, overpower); } if (player != null) { Proficiency.OnSuccessUse(player, player.GetCreatureSkill(Spell.School), Spell.PowerMod); } // handle target procs // note that for untargeted multi-projectile spells, // ProjectileTarget will be null here, so procs will not apply if (sourceCreature != null && ProjectileTarget != null) { // TODO figure out why cross-landblock group operations are happening here. We shouldn't need this code Mag-nus 2021-02-09 bool threadSafe = true; if (LandblockManager.CurrentlyTickingLandblockGroupsMultiThreaded) { // Ok... if we got here, we're likely in the parallel landblock physics processing. if (sourceCreature.CurrentLandblock == null || creatureTarget.CurrentLandblock == null || sourceCreature.CurrentLandblock.CurrentLandblockGroup != creatureTarget.CurrentLandblock.CurrentLandblockGroup) { threadSafe = false; } } if (threadSafe) { // This can result in spell projectiles being added to either sourceCreature or creatureTargets landblock. sourceCreature.TryProcEquippedItems(creatureTarget, false); } else { // sourceCreature and creatureTarget are now in different landblock groups. // What has likely happened is that sourceCreature sent a projectile toward creatureTarget. Before impact, sourceCreature was teleported away. // To perform this fully thread safe, we would enqueue the work onto worldManager. // WorldManager.EnqueueAction(new ActionEventDelegate(() => sourceCreature.TryProcEquippedItems(creatureTarget, false))); // But, to keep it simple, we will just ignore it and not bother with TryProcEquippedItems for this particular impact. } } } // also called on resist if (player != null && targetPlayer == null) { player.OnAttackMonster(creatureTarget); } if (player == null && targetPlayer == null) { // check for faction combat if (sourceCreature != null && creatureTarget != null && (sourceCreature.AllowFactionCombat(creatureTarget) || sourceCreature.PotentialFoe(creatureTarget))) { sourceCreature.MonsterOnAttackMonster(creatureTarget); } } }
public override void OnCollideObject(WorldObject target) { //Console.WriteLine($"{Name}.OnCollideObject({target.Name})"); var player = ProjectileSource as Player; if (Info != null && player != null && player.DebugSpell) { player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{Name}.OnCollideObject({target?.Name} ({target?.Guid}))", ChatMessageType.Broadcast)); player.Session.Network.EnqueueSend(new GameMessageSystemChat(Info.ToString(), ChatMessageType.Broadcast)); } ProjectileImpact(); // ensure valid creature target var creatureTarget = target as Creature; if (creatureTarget == null || target == ProjectileSource) { return; } if (player != null) { player.LastHitSpellProjectile = Spell; } // ensure caster can damage target var sourceCreature = ProjectileSource as Creature; if (sourceCreature != null && !sourceCreature.CanDamage(creatureTarget)) { return; } // if player target, ensure matching PK status var targetPlayer = creatureTarget as Player; var pkError = ProjectileSource?.CheckPKStatusVsTarget(creatureTarget, Spell); if (pkError != null) { if (player != null) { player.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(player.Session, pkError[0], creatureTarget.Name)); } if (targetPlayer != null) { targetPlayer.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(targetPlayer.Session, pkError[1], ProjectileSource.Name)); } return; } var critical = false; var critDefended = false; var overpower = false; var damage = CalculateDamage(ProjectileSource, Caster, creatureTarget, ref critical, ref critDefended, ref overpower); if (damage != null) { // handle void magic DoTs: // instead of instant damage, add DoT to target's enchantment registry if (Spell.School == MagicSchool.VoidMagic && Spell.Duration > 0) { var dot = ProjectileSource.CreateEnchantment(creatureTarget, ProjectileSource, Spell); if (dot.Message != null && player != null) { player.Session.Network.EnqueueSend(dot.Message); } // corruption / corrosion playscript? //target.EnqueueBroadcast(new GameMessageScript(target.Guid, PlayScript.HealthDownVoid)); //target.EnqueueBroadcast(new GameMessageScript(target.Guid, PlayScript.DirtyFightingDefenseDebuff)); } else { DamageTarget(creatureTarget, damage.Value, critical, critDefended, overpower); } if (player != null) { Proficiency.OnSuccessUse(player, player.GetCreatureSkill(Spell.School), Spell.PowerMod); } // handle target procs // note that for untargeted multi-projectile spells, // ProjectileTarget will be null here, so procs will not apply if (sourceCreature != null && ProjectileTarget != null) { // Ok... if we got here, we're likely in the parallel landblock physics processing. // We're currently on the thread for this, but we're wanting to perform some work on sourceCreature which can result in a new spell being created // and added to the sourceCreature's current landblock, which, could be on a separate thread. // Any chance of a cross landblock group work (and thus cross thread), should be enqueued onto the target object to maintain thread safety. if (sourceCreature.CurrentLandblock == null || sourceCreature.CurrentLandblock == CurrentLandblock) { sourceCreature.TryProcEquippedItems(creatureTarget, false); } else { sourceCreature.EnqueueAction(new ActionEventDelegate(() => sourceCreature.TryProcEquippedItems(creatureTarget, false))); } } } // also called on resist if (player != null && targetPlayer == null) { player.OnAttackMonster(creatureTarget); } if (player == null && targetPlayer == null) { // check for faction combat if (sourceCreature != null && creatureTarget != null && sourceCreature.AllowFactionCombat(creatureTarget)) { sourceCreature.MonsterOnAttackMonster(creatureTarget); } } }