/// <summary> /// Creates a projectile starting at the given location with the given rotation in radians /// </summary> /// <param name="name"></param> /// <param name="origin"></param> /// <param name="rotation"></param> /// <returns></returns> public Projectile CreateProjectile(string name, Vec2 origin, float rotation, ProjectileSource source) { var definition = _weaponDefinitions.FirstOrDefault(x => x.Name.ToLower().Equals(name.ToLower())); if (definition == null) { throw new Exception($"No WeaponDefinition found for {name}"); } var texture = _contentManager.Load <Texture2D>(definition.SpriteSheet); var shapeDef = new PolygonDef(); var physicsSize = GameUtils.PhysicsVec(new Vector2(definition.Width, definition.Height)); shapeDef.Vertices = new Vec2[4]; shapeDef.Vertices[0] = new Vec2(-(physicsSize.X / 2), -(physicsSize.Y / 2)); shapeDef.Vertices[1] = new Vec2((physicsSize.X / 2), -(physicsSize.Y / 2)); shapeDef.Vertices[2] = new Vec2((physicsSize.X / 2), (physicsSize.Y / 2)); shapeDef.Vertices[3] = new Vec2(-(physicsSize.X / 2), (physicsSize.Y / 2)); shapeDef.VertexCount = 4; shapeDef.Density = definition.Density; shapeDef.Friction = definition.Friction; //projectiles cannot collide with eachother if (source == ProjectileSource.Player) { shapeDef.Filter.CategoryBits = CollisionCategory.PlayerProjectile; shapeDef.Filter.MaskBits = (ushort)(CollisionCategory.Alien | CollisionCategory.AlienProjectile | CollisionCategory.Wall); } else { shapeDef.Filter.CategoryBits = CollisionCategory.AlienProjectile; shapeDef.Filter.MaskBits = (ushort)(CollisionCategory.Player | CollisionCategory.PlayerProjectile | CollisionCategory.Wall); } var bodyDef = new BodyDef(); bodyDef.IsBullet = true; bodyDef.Position.Set(origin.X, origin.Y); var body = _physicsWorld.CreateBody(bodyDef); var shape = body.CreateShape(shapeDef); body.SetMassFromShapes(); var velocityVector = GameUtils.RotationToVec2((float)(rotation * 180.0f / System.Math.PI)); body.SetLinearVelocity(velocityVector * definition.Velocity); var gameObject = new Projectile(_physicsWorld , definition , texture , shape , body , origin , rotation , GameData , GameUtils); GameWorld.AddGameObject(gameObject); return(gameObject); }
protected void SpawnProjectile(string projectileName, ProjectileSource source) { var slop = 20; //spawn the projectile just outside the players bounding box in the direction the player is facing. var offset = GameUtils.RotationToVec2((float)(RigidBody.GetAngle() * 180 / System.Math.PI)); var offsetLength = GameUtils.PhysicsVec(new Vector2(0, (Texture.Height + slop) / 2)); offset = offset * offsetLength.Length(); WeaponFactory.Instance.CreateProjectile(projectileName, RigidBody.GetPosition() + offset, RigidBody.GetAngle(), source); }
public override void OnCollideObject(WorldObject _target) { //Console.WriteLine($"{Name}.OnCollideObject({_target.Name})"); var player = ProjectileSource as Player; if (player != null) { player.LastHitSpellProjectile = Spell; } // ensure valid creature target // non-target objects will be excluded beforehand from collision detection var target = _target as Creature; if (target == null) { OnCollideEnvironment(); return; } ProjectileImpact(); // if player target, ensure matching PK status var targetPlayer = target as Player; var checkPKStatusVsTarget = CheckPKStatusVsTarget(player, targetPlayer, Spell); if (checkPKStatusVsTarget != null && checkPKStatusVsTarget == false) { player.Session.Network.EnqueueSend(new GameEventWeenieError(player.Session, WeenieError.InvalidPkStatus)); return; } var critical = false; var damage = CalculateDamage(ProjectileSource, target, ref critical); // null damage -> target resisted; damage of -1 -> target already dead if (damage != null && damage != -1) { // 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(target, ProjectileSource, Spell); if (dot.message != null && player != null) { player.Session.Network.EnqueueSend(dot.message); } // corruption / corrosion playscript? //target.EnqueueBroadcast(new GameMessageScript(target.Guid, ACE.Entity.Enum.PlayScript.HealthDownVoid)); //target.EnqueueBroadcast(new GameMessageScript(target.Guid, ACE.Entity.Enum.PlayScript.DirtyFightingDefenseDebuff)); } else { DamageTarget(target, damage, critical); } if (player != null) { Proficiency.OnSuccessUse(player, player.GetCreatureSkill(Spell.School), Spell.PowerMod); } } // also called on resist if (player != null && targetPlayer == null) { player.OnAttackMonster(target); } }
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 (player != null) { player.LastHitSpellProjectile = Spell; } // ensure valid creature target // non-target objects will be excluded beforehand from collision detection var target = _target as Creature; if (target == null || player == target) { OnCollideEnvironment(); return; } ProjectileImpact(); // for untargeted multi-projectile war spells launched by monsters, // ensure monster can damage target var sourceCreature = ProjectileSource as Creature; if (sourceCreature != null && !sourceCreature.CanDamage(target)) { return; } // if player target, ensure matching PK status var targetPlayer = target as Player; var pkError = CheckPKStatusVsTarget(player, targetPlayer, Spell); if (pkError != null) { if (player != null) { player.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(player.Session, pkError[0], target.Name)); } if (targetPlayer != null) { targetPlayer.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(targetPlayer.Session, pkError[1], ProjectileSource.Name)); } return; } var critical = false; var damage = CalculateDamage(ProjectileSource, target, ref critical); // null damage -> target resisted; damage of -1 -> target already dead if (damage != null && damage != -1) { // 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(target, ProjectileSource, Spell); if (dot.Message != null && player != null) { player.Session.Network.EnqueueSend(dot.Message); } // corruption / corrosion playscript? //target.EnqueueBroadcast(new GameMessageScript(target.Guid, ACE.Entity.Enum.PlayScript.HealthDownVoid)); //target.EnqueueBroadcast(new GameMessageScript(target.Guid, ACE.Entity.Enum.PlayScript.DirtyFightingDefenseDebuff)); } else { DamageTarget(target, damage, critical); } 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) { sourceCreature.TryProcEquippedItems(target, false); } } // also called on resist if (player != null && targetPlayer == null) { player.OnAttackMonster(target); } }
public ParabolicProjectile(Enemy enemy, GameWorld map, ProjectileSource currentProjectileSource) { this.CurrentProjectileSource = currentProjectileSource; this.Enemy = enemy; switch (currentProjectileSource) { case ProjectileSource.Snake: Texture = ContentHelper.LoadTexture("Projectiles/venom_dark"); CollRectangle = new Rectangle(enemy.GetCollRectangle().X, enemy.GetCollRectangle().Y, 32, 32); // animation = new Animation(Texture, collRectangle, 200, 0, AnimationType.Loop); if (!enemy.IsFacingRight) { Velocity = new Vector2(-10, -15); //animation.isFlipped = true; } else Velocity = new Vector2(10, -15); break; } }
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 = CheckPKStatusVsTarget(player, targetPlayer, 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) { sourceCreature.TryProcEquippedItems(creatureTarget, false); } } // also called on resist if (player != null && targetPlayer == null) { player.OnAttackMonster(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 = CheckPKStatusVsTarget(player, targetPlayer, 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); } }