diff --git a/MCGalaxy/Blocks/Physics/FireworkPhysics.cs b/MCGalaxy/Blocks/Physics/FireworkPhysics.cs index ec3b9676b..744330520 100644 --- a/MCGalaxy/Blocks/Physics/FireworkPhysics.cs +++ b/MCGalaxy/Blocks/Physics/FireworkPhysics.cs @@ -42,7 +42,7 @@ namespace MCGalaxy.Blocks.Physics { args.Type1 = PhysicsArgs.Wait; args.Value1 = 1; args.Type2 = PhysicsArgs.Dissipate; args.Value2 = 100; - lvl.AddUpdate(bAbove, Block.Fireworks, false); + lvl.AddUpdate(bAbove, Block.Fireworks); lvl.AddUpdate(C.b, Block.StillLava, false, args); args.Data = C.data.Data; C.data = args; diff --git a/MCGalaxy/Commands/Fun/CTF/CmdCtf.cs b/MCGalaxy/Commands/Fun/CTF/CmdCtf.cs index d503cd21a..765d25b6d 100644 --- a/MCGalaxy/Commands/Fun/CTF/CmdCtf.cs +++ b/MCGalaxy/Commands/Fun/CTF/CmdCtf.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.IO; using MCGalaxy.Games; +using MCGalaxy.Maths; namespace MCGalaxy.Commands.Fun { public sealed class CmdCTF : Command { @@ -107,11 +108,31 @@ namespace MCGalaxy.Commands.Fun { cfg.RedSpawnX = p.Pos.X; cfg.RedSpawnY = p.Pos.Y; cfg.RedSpawnZ = p.Pos.Z; Player.Message(p, "Set spawn of red team to your position."); UpdateConfig(p, cfg); + } else if (property.CaselessEq("blueflag")) { + Player.Message(p, "Place or delete a block to set blue team's flag."); + p.MakeSelection(1, null, BlueFlagCallback); + } else if (property.CaselessEq("redflag")) { + Player.Message(p, "Place or delete a block to set red team's flag."); + p.MakeSelection(1, null, RedFlagCallback); } else { Help(p, "set"); } } + static bool BlueFlagCallback(Player p, Vec3S32[] marks, object state, ExtBlock block) { + CTFConfig cfg = RetrieveConfig(p); + cfg.BlueFlagX = marks[0].X; cfg.BlueFlagY = marks[0].Y; cfg.BlueFlagZ = marks[0].Z; + UpdateConfig(p, cfg); + return false; + } + + static bool RedFlagCallback(Player p, Vec3S32[] marks, object state, ExtBlock block) { + CTFConfig cfg = RetrieveConfig(p); + cfg.RedFlagX = marks[0].X; cfg.RedFlagY = marks[0].Y; cfg.RedFlagZ = marks[0].Z; + UpdateConfig(p, cfg); + return false; + } + static CTFConfig RetrieveConfig(Player p) { CTFConfig cfg = new CTFConfig(); cfg.SetDefaults(p.level); diff --git a/MCGalaxy/Commands/building/CmdBezier.cs b/MCGalaxy/Commands/building/CmdBezier.cs index 4e018abfe..3ec6920a1 100644 --- a/MCGalaxy/Commands/building/CmdBezier.cs +++ b/MCGalaxy/Commands/building/CmdBezier.cs @@ -23,7 +23,10 @@ namespace MCGalaxy.Commands.Building { public override string name { get { return "bezier"; } } protected override string PlaceMessage { get { return "Place or break two blocks to determine the endpoints, then another for the control point"; } } public override int MarksCount { get { return 3; } } - + public override CommandAlias[] Aliases { + get { return new[] { new CommandAlias("curve") }; } + } + protected override DrawOp GetDrawOp(DrawArgs dArgs) { return new BezierDrawOp(); } diff --git a/MCGalaxy/Drawing/DrawOps/BezierDrawOp.cs b/MCGalaxy/Drawing/DrawOps/BezierDrawOp.cs index 84e5ea153..47daeaeaf 100644 --- a/MCGalaxy/Drawing/DrawOps/BezierDrawOp.cs +++ b/MCGalaxy/Drawing/DrawOps/BezierDrawOp.cs @@ -20,35 +20,69 @@ using System.Collections.Generic; using MCGalaxy.Drawing.Brushes; using MCGalaxy.Maths; -namespace MCGalaxy.Drawing.Ops { - public class BezierDrawOp : DrawOp { - public override string Name { get { return "Bezier"; } } - public bool WallsMode; - public int MaxLength = int.MaxValue; - - public override long BlocksAffected(Level lvl, Vec3S32[] marks) { - Vec3S32 p0 = marks[0], p2 = marks[1], p1 = marks[2]; - return (long)((p1 - p0).Length + (p1 - p2).Length); - } - - public override void Perform(Vec3S32[] marks, Brush brush, DrawOpOutput output) { - int steps = 20; - Vec3F32 p0 = marks[0], p2 = marks[1], p1 = marks[2]; - steps *= (int)((p1 - p0).Length + (p1 - p2).Length); - - float t = 0, invT = 1, delta = 1.0f / steps; - for (int i = 0; i <= steps; i++) { - Vec3F32 B = invT * invT * p0 + 2 * invT * t * p1 + t * t * p2; - output(Place(Round(B.X), Round(B.Y), Round(B.Z), brush)); - t += delta; invT -= delta; - } - } - - static ushort Round(float value) { - int valueI = (int)value; - int floored = value < valueI ? valueI - 1 : valueI; - float frac = (value % 1.0f); - return (ushort)(floored + (frac > 0.5f ? 1 : 0)); - } - } +namespace MCGalaxy.Drawing.Ops { + public class BezierDrawOp : DrawOp { + public override string Name { get { return "Bezier"; } } + public bool WallsMode; + public int MaxLength = int.MaxValue; + + public override long BlocksAffected(Level lvl, Vec3S32[] marks) { + Vec3S32 p0 = marks[0], p2 = marks[1], p1 = marks[2]; + return (long)((p1 - p0).Length + (p1 - p2).Length); + } + + public override void Perform(Vec3S32[] marks, Brush brush, DrawOpOutput output) { + points.Add(marks[0]); + TesselateCurve(marks[0], marks[2], marks[1], 0); + + List buffer = new List(); + for (int i = 0; i < points.Count - 1; i++) { + LineDrawOp.DrawLine(points[i].X, points[i].Y, points[i].Z, 1000000, + points[i+1].X, points[i+1].Y, points[i+1].Z, buffer); + + foreach (Vec3S32 P in buffer) { + output(Place((ushort)P.X, (ushort)P.Y, (ushort)P.Z, brush)); + } + } + } + + List points = new List(); + const float objspace_flatness_squared = 0.35f * 0.35f; + + // Based off stbtt__tesselate_curve from https://github.com/nothings/stb/blob/master/stb_truetype.h + void TesselateCurve(Vec3F32 p0, Vec3F32 p1, Vec3F32 p2, int n) { + // midpoint + Vec3F32 m; + m.X = (p0.X + 2 * p1.X + p2.X) * 0.25f; + m.Y = (p0.Y + 2 * p1.Y + p2.Y) * 0.25f; + m.Z = (p0.Z + 2 * p1.Z + p2.Z) * 0.25f; + + // versus directly drawn line + Vec3F32 d; + d.X = (p0.X + p2.X) * 0.5f - m.X; + d.Y = (p0.Y + p2.Y) * 0.5f - m.Y; + d.Z = (p0.Z + p2.Z) * 0.5f - m.Z; + + if (n > 16) return; // 65536 segments on one curve better be enough! + + // half-pixel error allowed... need to be smaller if AA + if (d.X * d.X + d.Y * d.Y + d.Z * d.Z > objspace_flatness_squared) { + Vec3F32 p0_p1 = new Vec3F32((p0.X + p1.X) * 0.5f, (p0.Y + p1.Y) * 0.5f, (p0.Z + p1.Z) * 0.5f); + TesselateCurve(p0, p0_p1, m, n + 1); + + Vec3F32 p1_p2 = new Vec3F32((p1.X + p2.X) * 0.5f, (p1.Y + p2.Y) * 0.5f, (p1.Z + p2.Z) * 0.5f); + TesselateCurve(m, p1_p2, p2, n + 1); + } else { + // TODO: do we need to round properly here or not + points.Add(new Vec3S32((int)p2.X, (int)p2.Y, (int)p2.Z)); + } + } + + /*static ushort Round(float value) { + int valueI = (int)value; + int floored = value < valueI ? valueI - 1 : valueI; + float frac = (value % 1.0f); + return (ushort)(floored + (frac > 0.5f ? 1 : 0)); + }*/ + } } diff --git a/MCGalaxy/Server/Tasks/ServerTasks.cs b/MCGalaxy/Server/Tasks/ServerTasks.cs index 46ac1e51e..463ff31a6 100644 --- a/MCGalaxy/Server/Tasks/ServerTasks.cs +++ b/MCGalaxy/Server/Tasks/ServerTasks.cs @@ -96,7 +96,7 @@ namespace MCGalaxy.Tasks { Player[] players = PlayerInfo.Online.Items; foreach (Player p in players) { if (p.hasTwoWayPing) { - p.Send(Packet.TwoWayPing(true, p.Ping.NextTwoWayPingData())); + p.Send(Packet.TwoWayPing(true, p.Ping.NextTwoWayPingData())); } else { p.Send(Packet.Ping()); }