public virtual bool Update() { // calculate target direction! // check if target is gone if (Target != null && (!Target.IsAlive || !Target.IsLinked)) { Target = null; } Projectile.Alpha = 0; if (Target != null && SubProjectiles.Count <= 0) { // special magic for lightning Projectile.LightLevel = 256; TargetCenter = new Vector2(Target.X + (float)Target.Width / 2 + Target.FracX, Target.Y + (float)Target.Height / 2 + Target.FracY); TargetAngle = Projectile.Angle = MapObject.FaceVector(TargetCenter.x - Projectile.ProjectileX, TargetCenter.y - Projectile.ProjectileY); TargetDir = new Vector2(TargetCenter.x - Projectile.ProjectileX, TargetCenter.y - Projectile.ProjectileY); TargetDst = TargetDir.magnitude; TargetDir.Normalize(); // spawn projectiles along the way Density = 0.2f; for (float i = 0; i < TargetDst; i += Density) { MapProjectile visProj = new MapProjectile(Color == 0 ? AllodsProjectile.Lightning : AllodsProjectile.ChainLightning); visProj.LightLevel = 0; visProj.CurrentFrame = Color == 0 ? 0 : (5 * (Color - 1)); visProj.SetPosition(Projectile.ProjectileX + TargetDir.x * i, Projectile.ProjectileY + TargetDir.y * i, 0); // for now MapLogic.Instance.Objects.Add(visProj); SubProjectiles.Add(visProj); } Projectile.CallCallback(); } AnimTime += 0.08f; float hUDiff = MapLogic.Instance.GetHeightAt(TargetCenter.x, TargetCenter.y, 1, 1) / 32f; float htOrig = MapLogic.Instance.GetHeightAt(Projectile.ProjectileX, Projectile.ProjectileY, 1, 1) / 32f; float angleToDst = Mathf.Atan2(TargetDir.y, TargetDir.x); float sinScale = TargetDst / 8 * Mathf.Sin((float)MapLogic.Instance.LevelTime / 2 * TargetDst); Vector2 offs = new Vector2(0, 1); float sinX = Mathf.Cos(angleToDst) * offs.x - Mathf.Sin(angleToDst) * offs.y; float sinY = Mathf.Cos(angleToDst) * offs.y + Mathf.Sin(angleToDst) * offs.x; for (int i = 0; i < SubProjectiles.Count; i++) { float idst = Density * i; // get angle from first to second. calculate sin wave float baseX = Projectile.ProjectileX + TargetDir.x * idst; float baseY = Projectile.ProjectileY + TargetDir.y * idst; float sscale = (Sin10(idst / TargetDst) * sinScale); MapProjectile sub = SubProjectiles[i]; int cframe = (Color == 0 ? 0 : (5 * (Color - 1))); sub.CurrentFrame = (int)(cframe + 4 * AnimTime); sub.LightLevel = 0; if (i % 12 == 0) { sub.LightLevel = (int)(512 * (1f - AnimTime)); } float htDiff = MapLogic.Instance.GetHeightAt(baseX + sinX * sscale, baseY + sinY * sscale, 1, 1) / 32f; float lval = (float)i / SubProjectiles.Count; float htLerp = hUDiff * lval + htOrig * (1f - lval); sub.SetPosition(baseX + sinX * sscale, baseY + sinY * sscale, -htDiff + htLerp); } if (AnimTime > 1) { foreach (MapProjectile sub in SubProjectiles) { sub.Dispose(); } Projectile.Dispose(); return(false); } return(true); }
public override bool Process() { if (NetworkManager.IsClient) { return(false); } int ballDamage = Spell.GetDamage(); // here SpawnProjectile is basically duplicated. except some functions that aren't needed. // following offsets are based on unit's width, height and center float tX, tY; tX = TargetX + 0.5f; tY = TargetY + 0.5f; float cX, cY; if (Spell.User != null) { cX = Spell.User.X + Spell.User.Width * 0.5f + Spell.User.FracX; cY = Spell.User.Y + Spell.User.Height * 0.5f + Spell.User.FracY; Vector2 dir = new Vector2(tX - cX, tY - cY).normalized *((Spell.User.Width + Spell.User.Height) / 2) / 1.5f; cX += dir.x; cY += dir.y; } else { cX = tX; cY = tY; } Server.SpawnProjectileDirectional(AllodsProjectile.FireBall, Spell.User, cX, cY, 0, tX, tY, 0, 10, (MapProjectile fproj) => { //Debug.LogFormat("spell projectile hit!"); // done, make damage DamageFlags spdf = SphereToDamageFlags(Spell); if (Spell.Item == null) { spdf |= DamageFlags.AllowExp; } // spawn explosion. serverside, since projectile hit callback is serverside as well. Server.SpawnProjectileSimple(AllodsProjectile.Explosion, null, fproj.ProjectileX, fproj.ProjectileY, fproj.ProjectileZ); // apply damage over 3x3 cells around the fireball for (int y = fproj.Y - 1; y <= fproj.Y + 1; y++) { for (int x = fproj.X - 1; x <= fproj.X + 1; x++) { if (x < 0 || y < 0 || x >= MapLogic.Instance.Width || y >= MapLogic.Instance.Height) { continue; } int dmg = (int)((2f - (new Vector2(x - fproj.X, y - fproj.Y).magnitude)) * ballDamage); // damage in the center is approximately 1.4 or 1.6 MapNode node = MapLogic.Instance.Nodes[x, y]; for (int i = 0; i < node.Objects.Count; i++) { MapObject mo = node.Objects[i]; if (!(mo is IVulnerable)) { // aoe fire effect: remove cloud effects if any if (!(mo is MapProjectile)) { continue; } MapProjectile mp = (MapProjectile)mo; if (mp.Class == null || mp.Class.ID != (int)AllodsProjectile.PoisonCloud) { continue; } // don't remove if on edge of fire wall if (new Vector2(mp.ProjectileX - fproj.ProjectileX, mp.ProjectileY - fproj.ProjectileY).magnitude > 1.5f) { continue; } mp.Dispose(); i--; continue; } else { IVulnerable mov = (IVulnerable)mo; mov.TakeDamage(spdf, Spell.User, dmg); } } } } fproj.Dispose(); }); return(false); }
public override void OnDisable() { Indicator.Dispose(); }
public override bool Process() { if (NetworkManager.IsClient) { return(false); } // we need to spawn several EoT projectiles at the destination // 5x2 float sourceX = Spell.User.X + Spell.User.FracX + Spell.User.Width / 2f; float sourceY = Spell.User.Y + Spell.User.FracY + Spell.User.Height / 2f; Vector2 direction = new Vector2(TargetX + 0.5f - sourceX, TargetY + 0.5f - sourceY); float angle = Mathf.Atan2(direction.y, direction.x) - (Mathf.PI / 180) * 90; for (int x = -2; x <= 2; x++) { for (int y = 0; y < 2; y++) { // rotate x and y float fx = x; float fy = y; float rx = Mathf.Cos(angle) * x - Mathf.Sin(angle) * y; float ry = Mathf.Cos(angle) * y + Mathf.Sin(angle) * x; Server.SpawnProjectileEOT(AllodsProjectile.FireWall, Spell.User, TargetX + rx + 0.5f, TargetY + ry + 0.5f, 0, (int)(MapLogic.TICRATE * Spell.GetDuration()), 10, 4, 4, 16, proj => { DamageFlags spdf = SphereToDamageFlags(Spell); if (Spell.Item == null) { spdf |= DamageFlags.AllowExp; } // get projectile cells int axFrom = Mathf.Max(0, Mathf.FloorToInt(proj.ProjectileX)); int axTo = Mathf.Min(MapLogic.Instance.Width - 1, Mathf.CeilToInt(proj.ProjectileX)); int ayFrom = Mathf.Max(0, Mathf.FloorToInt(proj.ProjectileY)); int ayTo = Mathf.Min(MapLogic.Instance.Height - 1, Mathf.CeilToInt(proj.ProjectileY)); for (int py = ayFrom; py <= ayTo; py++) { for (int px = axFrom; px <= axTo; px++) { // check how much projectile is on this cell float pdst = 1f - Mathf.Min(1f, new Vector2(px + 0.5f - proj.ProjectileX, py + 0.5f - proj.ProjectileY).magnitude); // 0..1 effect power MapNode node = MapLogic.Instance.Nodes[px, py]; for (int i = 0; i < node.Objects.Count; i++) { MapObject mo = node.Objects[i]; if (!(mo is IVulnerable)) { // aoe fire effect: remove cloud effects if any if (!(mo is MapProjectile)) { continue; } MapProjectile mp = (MapProjectile)mo; if (mp.Class == null || mp.Class.ID != (int)AllodsProjectile.PoisonCloud) { continue; } // don't remove if on edge of fire wall if (new Vector2(mp.ProjectileX - proj.ProjectileX, mp.ProjectileY - proj.ProjectileY).magnitude > 0.8f) { continue; } mp.Dispose(); i--; continue; } else { IVulnerable mov = (IVulnerable)mo; int dmg = (int)(Spell.GetDamage() * pdst); mov.TakeDamage(spdf, Spell.User, dmg); } } } } }); } } return(false); }