private void MainPanel_Paint(object sender, PaintEventArgs e) { if (obj == null) { return; } e.Graphics.Clear(Color.Black); image = new Bitmap(e.ClipRectangle.Width, e.ClipRectangle.Height); provider = new BitmapProvider(image); graphics = new MyGraphics(provider); //graphics.Fill(Color.Black); int Width = e.ClipRectangle.Width; int Height = e.ClipRectangle.Height; Vec3f zero = new Vec3f(0, 0, 0); Vec3f ligth = new Vec3f( ((float)e.ClipRectangle.Width / 2 - x) / (Width / 2), (y - (float)e.ClipRectangle.Height / 2) / (Height / 2), z); ligth = zero - ligth; ligth.Normalize(); //graphics.DrawLine(new Vec2i(200, 200), new Vec2i(x, y), Color.White); graphics.DrawObject(obj, Color.White, ligth, c); graphics.DrawLight(ligth, Width / 2); e.Graphics.DrawImage(image, 0, 0); }
public void InitQuads() { quadTilesRef?.Dispose(); float height = 200; MeshData mesh = new MeshData(4, 6, false, true, true, false); float x = 0, y = 0, z = 0; Random rnd = new Random(); for (int i = 0; i < 15; i++) { Vec3f dir = new Vec3f((float)rnd.NextDouble() * 20 - 10, (float)rnd.NextDouble() * 5 - 3, (float)rnd.NextDouble() * 20 - 10); dir.Normalize(); x = (float)rnd.NextDouble() * 800 - 400; y = (float)rnd.NextDouble() * 80 - 40; z = (float)rnd.NextDouble() * 800 - 400; for (int j = 0; j < 100; j++) { float lngx = (float)rnd.NextDouble() * 5 + 20; float lngy = (float)rnd.NextDouble() * 4 + 4; float lngz = (float)rnd.NextDouble() * 5 + 20; x += dir.X * lngx; y += dir.Y * lngy; z += dir.Z * lngz; //float width = 20 + (float)rnd.NextDouble() * 5; //MeshData quad = QuadMeshUtil.GetCustomQuad(z, 20, x, x - prevx, height, 255, 255, 255, 255); //mesh.AddMeshData(quad); int lastelement = mesh.VerticesCount; mesh.AddVertex(x, y + height, z, j % 2, 1); mesh.AddVertex(x, y, z, j % 2, 0); if (j > 0 && j < 19) { mesh.AddIndex(lastelement + 0); mesh.AddIndex(lastelement + 1); mesh.AddIndex(lastelement + 2); mesh.AddIndex(lastelement + 1); mesh.AddIndex(lastelement + 3); mesh.AddIndex(lastelement + 2); } } } quadTilesRef = capi.Render.UploadMesh(mesh); }
//private Matrix DoubleArrayToMatrix(double[] m) //{ // return new Matrix(new double[,] { // { m[00], m[04], m[08], m[12] }, // { m[01], m[05], m[09], m[13] }, // { m[02], m[06], m[10], m[14] }, // { m[03], m[07], m[11], m[15] } // }); //} //public Matrix GetGLModelMatrix() //{ // double[] m = new double[16]; // GL.GetDouble(GetPName.ModelviewMatrix, m); // return DoubleArrayToMatrix(m); //} //public Matrix GetGLProjectionMatrix() //{ // double[] m = new double[16]; // GL.GetDouble(GetPName.ProjectionMatrix, m); // return DoubleArrayToMatrix(m); //} #endregion public void Set(Vec3f tar, Quatf rot, float dist) { rot = rot.Normalize(); this.qrot = rot; this.dist = dist; Vec3f _forward = rot.Rotate(Vec3f.Y); Vec3f _up = rot.Rotate(Vec3f.Z); Position = tar - _forward * dist; Scale = 50.0f / dist; Forward = Vec3f.Normalize(_forward); Target = tar; Up = _up; }
public void DrawObject(Object3D obj, Color color, Vec3f lightDirection, float c = 5) { var Width = graphicsProvider.Width; var Height = graphicsProvider.Height; var vertexesFromMem = new Vec3f[3]; var vertexes = new Vec3i[3]; for (int i = 0; i < graphicsProvider.Width; i++) { for (int j = 0; j < graphicsProvider.Height; j++) { zbuffer[i, j] = Int32.MinValue; } } foreach (var face in obj.Faces) { //Работает медленно vertexesFromMem[0] = (obj.Vertexes.ElementAt(face.v1.v - 1)); vertexesFromMem[1] = (obj.Vertexes.ElementAt(face.v2.v - 1)); vertexesFromMem[2] = (obj.Vertexes.ElementAt(face.v3.v - 1)); for (int i = 0; i < vertexes.Length; i++) { Vec3f v = vertexesFromMem[i]; v *= c; vertexes[i] = new Vec3i(new Vec3f(Width / 2, Height / 2, 0) - new Vec3f(-v.X, v.Y, -v.Z)); } //color = Color.FromArgb(random.Next(255), random.Next(255), random.Next(255)); Vec3f normal = Vec3f.VecMul(vertexesFromMem[2] - vertexesFromMem[0], vertexesFromMem[1] - vertexesFromMem[0]); normal.Normalize(); float intensity = normal * lightDirection; if (intensity > 1.0f) { //Костыль intensity = 1.0f; } if (intensity > 0) { DrawTriangle(vertexes[0], vertexes[1], vertexes[2], Color.FromArgb( (int)(color.R * intensity), (int)(color.G * intensity), (int)(color.B * intensity) )); } } }
public override bool OnHeldInteractStep(float secondsUsed, IItemSlot slot, IEntityAgent byEntity, BlockSelection blockSel, EntitySelection entitySel) { if (blockSel == null) { return(false); } if (byEntity.World is IClientWorldAccessor) { ModelTransform tf = new ModelTransform(); tf.EnsureDefaultValues(); float offset = GameMath.Clamp(secondsUsed * 3, 0, 2f); tf.Translation.Set(-offset, offset / 4f, 0); byEntity.Controls.UsingHeldItemTransformBefore = tf; } SimpleParticleProperties bees = BlockEntityBeehive.Bees; BlockPos pos = blockSel.Position; Random rand = byEntity.World.Rand; Vec3d startPos = new Vec3d(pos.X + rand.NextDouble(), pos.Y + rand.NextDouble() * 0.25f, pos.Z + rand.NextDouble()); Vec3d endPos = new Vec3d(byEntity.LocalPos.X, byEntity.LocalPos.Y + byEntity.EyeHeight - 0.2f, byEntity.LocalPos.Z); Vec3f minVelo = new Vec3f((float)(endPos.X - startPos.X), (float)(endPos.Y - startPos.Y), (float)(endPos.Z - startPos.Z)); minVelo.Normalize(); minVelo *= 2; bees.minPos = startPos; bees.minVelocity = minVelo; bees.WithTerrainCollision = true; IPlayer byPlayer = null; if (byEntity is IEntityPlayer) { byPlayer = byEntity.World.PlayerByUid(((IEntityPlayer)byEntity).PlayerUID); } byEntity.World.SpawnParticles(bees, byPlayer); return(secondsUsed < 4); }
public ContactData(Vec3f worldPointA, Vec3f localPointA, Vec3f worldPointB, Vec3f localPointB, Vec3f normal, float depth) { this.localPointA = localPointA; this.localPointB = localPointB; this.worldPointA = worldPointA; this.worldPointB = worldPointB; this.depth = depth; this.normal = normal; if (normal.x >= 0.57735f) { tang1 = new Vec3f(normal.y, -normal.x, 0); } else { tang1 = new Vec3f(0.0f, normal.z, -normal.y); } tang1.Normalize(); tang2 = Vec3f.Cross(normal, tang1); }
/*public void OrbitTargetUpDownNatural( double theta_degrees ) * { * float fn = Forward.Val % NaturalUp.Val; * if( ( theta_degrees < 0.0 && fn >= 0.95f ) || ( theta_degrees > 0.0 && fn <= -0.95f ) ) return; * * double theta = theta_degrees * Math.PI / 180.0; * Vec3f posreltar = Position.Val - Target.Val; * Vec3f right = Forward.Val ^ NaturalUp.Val; * float dist = (Target.Val - Position.Val).Length; * * Properties.DeferPropertyChanged = true; * bIgnoreChanges = true; * Position.Set( Target.Val + Vec3f.Normalize( VecExtensions.RotateVectorAroundAxis( posreltar, right, (float) theta ) ) * dist ); * Up.Set( VecExtensions.RotateVectorAroundAxis( Up.Val, right, (float) theta ) ); * bIgnoreChanges = false; * Target.Set( Target.Val ); * Properties.DeferPropertyChanged = false; * }*/ public void OrbitTargetUpDown(double theta_degrees, bool clampflips) { double theta = theta_degrees * Math.PI / 180.0; Quatf newrot = qrot * Quatf.AxisAngleToQuatf(GetRight(), (float)theta); if (clampflips) { // prevent camera from flipping over! Vec3f newup = Vec3f.Normalize(newrot.Rotate(Vec3f.Y)); float dot = FMath.PI / 2.0f - Vec3f.AngleBetween(newup, NaturalUp.Val); if (dot < 0) { Vec3f newforward = Vec3f.Normalize(newrot.Rotate(-Vec3f.Z)); Vec3f newright = newforward ^ newup; float sign = -Math.Sign(newforward % NaturalUp.Val); newrot = newrot * Quatf.RotAxisAngleToQuatf(newright, dot * sign); } } Set(Target.Val, newrot, dist); }
public void Set(Vec3f tar, Quatf rot, float dist) { rot = rot.Normalize(); /*if( AlwaysUp ) * { * // prevent camera from rolling * Vec3f newright = Vec3f.Normalize( rot.Rotate( Vec3f.X ) ); * Vec3f newup = Vec3f.Normalize( rot.Rotate( Vec3f.Y ) ); * Vec3f newforward = Vec3f.Normalize( rot.Rotate( -Vec3f.Z ) ); * * if( Math.Abs( newforward % NaturalUp ) < 0.95f ) { * Vec3f goodright = Vec3f.Normalize( newforward ^ Vec3f.Z ); * float dot = Vec3f.AngleBetween( goodright, newright ); * System.Console.WriteLine( newup.ToStringFormatted() + " " + newright.ToStringFormatted() + " " + goodright.ToStringFormatted() ); * float sign = -Math.Sign( newright % NaturalUp ); * //rot = rot * Quatf.RotAxisAngleToQuatf( newforward, dot * sign ); * } * }*/ this.qrot = rot; this.dist = dist; Properties.DeferPropertyChanged = true; bIgnoreChanges = true; Vec3f fwd = rot.Rotate(-Vec3f.Z); Vec3f up = rot.Rotate(Vec3f.Y); Position.Set(tar - fwd * dist); Scale.Set(50.0f / dist); Target.Set(tar); Forward.Set(Vec3f.Normalize(fwd)); Up.Set(up); bIgnoreChanges = false; Properties.DeferPropertyChanged = false; }
public SnapshotModel(string sFilename) { int nverts = 0; int nfaces = 0; int nvertsel = 0; //int nedgesel = 0; //int nfacesel = 0; int nmods = 0; Vec3f[] coords; int[] selected; Modifier[] modifiers; using (Stream s = new FileStream(sFilename, FileMode.Open)) { string plyline = FileIOFunctions.ReadTextString(s); if (plyline != "ply") { throw new ArgumentException("TVModel.LoadObjectFromPLY: Specified file is not .ply file"); } bool header = true; while (header) { string cmd = FileIOFunctions.ReadTextString(s); switch (cmd) { case "format": case "comment": case "property": while (s.ReadByte() != 10) { ; // ignore the rest of the line } break; case "element": string variable = FileIOFunctions.ReadTextString(s); int val = FileIOFunctions.ReadTextInteger(s); switch (variable) { case "vertex": nverts = val; break; case "face": nfaces = val; break; case "selected": nvertsel = val; FileIOFunctions.ReadTextInteger(s); // nedgesel = FileIOFunctions.ReadTextInteger(s); // nfacesel = break; case "modifiers": nmods = val; break; default: throw new Exception("TVModel.LoadObjectFromPLY: Unhandled element type " + variable); } break; case "end_header": header = false; break; default: throw new Exception("TVModel.LoadObjectFromPLY: Unhandled command type " + cmd); } } coords = new Vec3f[nverts]; selected = new int[nvertsel]; modifiers = new Modifier[nmods]; groups = new List <GroupInfo>[] { new List <GroupInfo>(nverts), new List <GroupInfo>(nfaces), new List <GroupInfo>(nfaces), new List <GroupInfo>(nfaces) }; groupselected = new List <bool>[] { new List <bool>(nverts), new List <bool>(nverts), new List <bool>(nverts), new List <bool>(nverts) }; facenormals = new List <Vec3f>[] { new List <Vec3f>(), new List <Vec3f>() }; // read vert locations and visibility for (int i = 0; i < nverts; i++) { coords[i] = new Vec3f(FileIOFunctions.ReadTextFloat(s), FileIOFunctions.ReadTextFloat(s), FileIOFunctions.ReadTextFloat(s)); bool v = (FileIOFunctions.ReadTextInteger(s) == 1); groups[0].Add(new GroupInfo(new int[] { i }, v)); } // read inds of edges and faces for (int i = 0; i < nfaces; i++) { int count = FileIOFunctions.ReadTextInteger(s); int[] inds = new int[count]; for (int j = 0; j < count; j++) { inds[j] = FileIOFunctions.ReadTextInteger(s); } bool v = (FileIOFunctions.ReadTextInteger(s) == 1); bool gsel = (FileIOFunctions.ReadTextInteger(s) == 1); groups[count - 1].Add(new GroupInfo(inds, v)); groupselected[count - 1].Add(gsel); if (count == 3 || count == 4) { Vec3f v0 = Vec3f.Normalize(coords[inds[0]] - coords[inds[1]]); Vec3f v1 = Vec3f.Normalize(coords[inds[2]] - coords[inds[1]]); Vec3f vn = v1 ^ v0; if (vn.LengthSqr > 0.00001f) { facenormals[count - 3].Add(Vec3f.Normalize(vn)); } else { facenormals[count - 3].Add(new Vec3f()); } } } groups[1].TrimExcess(); groups[2].TrimExcess(); groups[3].TrimExcess(); groupselected[1].TrimExcess(); groupselected[2].TrimExcess(); groupselected[3].TrimExcess(); // read inds of selected verts for (int i = 0; i < nvertsel; i++) { selected[i] = FileIOFunctions.ReadTextInteger(s); } // read modifiers for (int i = 0; i < nmods; i++) { string modifier = FileIOFunctions.ReadTextString(s); switch (modifier) { case "mirror": modifiers[i] = new ModifierMirror() { name = FileIOFunctions.ReadTextQuotedString(s), usex = (FileIOFunctions.ReadTextInteger(s) == 1), usey = (FileIOFunctions.ReadTextInteger(s) == 1), usez = (FileIOFunctions.ReadTextInteger(s) == 1), mergethreshold = FileIOFunctions.ReadTextFloat(s) }; break; /*case "subdiv": * modifiers[i] = new ModifierSubDiv() { * name = FileIOFunctions.ReadTextQuotedString(s), * levels = FileIOFunctions.ReadTextInteger(s) * }; * break; * case "boolop": * modifiers[i] = new ModifierBoolOp * ( * FileIOFunctions.ReadTextQuotedString(s), * ModifierBoolOp.StringToBoolOp( FileIOFunctions.ReadTextString(s) ), * FileIOFunctions.ReadTextString(s) * ); * break; * case "solidify": * modifiers[i] = new ModifierSolidify * ( * FileIOFunctions.ReadTextString(s), * FileIOFunctions.ReadTextFloat(s), * FileIOFunctions.ReadTextFloat(s), * FileIOFunctions.ReadTextFloat(s), * FileIOFunctions.ReadTextFloat(s), * FileIOFunctions.ReadTextFloat(s), * ( FileIOFunctions.ReadTextInteger(s) == 1 ), * ( FileIOFunctions.ReadTextInteger(s) == 1 ), * ( FileIOFunctions.ReadTextInteger(s) == 1 ) * ); * break;*/ default: throw new Exception("TVModel.LoadObjectFromPLY: Unknown modifier " + modifier); } } } this.verts = coords; this.selinds = selected; this.modifiers = modifiers; this.nverts = nverts; FillIsVertSelected(); CalcVertexNormals(); }
/// <summary> /// Loads the meta information for each block in the schematic. /// </summary> /// <param name="blockAccessor"></param> /// <param name="worldForResolve"></param> /// <param name="fileNameForLogging"></param> public void LoadMetaInformationAndValidate(IBlockAccessor blockAccessor, IWorldAccessor worldForResolve, string fileNameForLogging) { List <BlockPos> undergroundPositions = new List <BlockPos>(); Queue <BlockPos> pathwayPositions = new Queue <BlockPos>(); HashSet <AssetLocation> missingBlocks = new HashSet <AssetLocation>(); for (int i = 0; i < Indices.Count; i++) { uint index = Indices[i]; int storedBlockid = BlockIds[i]; int dx = (int)(index & 0x1ff); int dy = (int)((index >> 20) & 0x1ff); int dz = (int)((index >> 10) & 0x1ff); AssetLocation blockCode = BlockCodes[storedBlockid]; Block newBlock = blockAccessor.GetBlock(blockCode); if (newBlock == null) { missingBlocks.Add(blockCode); } if (newBlock != pathwayBlock && newBlock != undergroundBlock) { continue; } BlockPos pos = new BlockPos(dx, dy, dz); if (newBlock == pathwayBlock) { pathwayPositions.Enqueue(pos); } else { undergroundPositions.Add(pos); } } if (missingBlocks.Count > 0) { worldForResolve.Logger.Warning("Block schematic file {0} uses blocks that could no longer be found. These will turn into air blocks! (affected: {1})", fileNameForLogging, string.Join(",", missingBlocks)); } HashSet <AssetLocation> missingItems = new HashSet <AssetLocation>(); foreach (var val in ItemCodes) { if (worldForResolve.GetItem(val.Value) == null) { missingItems.Add(val.Value); } } if (missingItems.Count > 0) { worldForResolve.Logger.Warning("Block schematic file {0} uses items that could no longer be found. These will turn into unknown items! (affected: {1})", fileNameForLogging, string.Join(",", missingItems)); } UndergroundCheckPositions = undergroundPositions.ToArray(); List <List <BlockPos> > pathwayslist = new List <List <BlockPos> >(); if (pathwayPositions.Count == 0) { this.PathwayStarts = new BlockPos[0]; this.PathwayOffsets = new BlockPos[0][]; this.PathwaySides = new BlockFacing[0]; return; } while (pathwayPositions.Count > 0) { List <BlockPos> pathway = new List <BlockPos>() { pathwayPositions.Dequeue() }; pathwayslist.Add(pathway); int i = pathwayPositions.Count; while (i-- > 0) { BlockPos pos = pathwayPositions.Dequeue(); bool found = false; for (int j = 0; j < pathway.Count; j++) { BlockPos ppos = pathway[j]; int distance = Math.Abs(pos.X - ppos.X) + Math.Abs(pos.Y - ppos.Y) + Math.Abs(pos.Z - ppos.Z); if (distance == 1) { found = true; pathway.Add(pos); break; } } if (!found) { pathwayPositions.Enqueue(pos); } else { i = pathwayPositions.Count; } } } PathwayStarts = new BlockPos[pathwayslist.Count]; PathwayOffsets = new BlockPos[pathwayslist.Count][]; PathwaySides = new BlockFacing[pathwayslist.Count]; for (int i = 0; i < PathwayStarts.Length; i++) { // Concept to determine on which side the door is: // 1. Iterate over every pathway block // 2. Calculate the vector between the schematic center point an the pathway block // 3. Get the average vector by summing up + divide by count // => this is now basically the centerpoint of the door! // 4. This final vector can now be used to determine the block facing Vec3f dirToMiddle = new Vec3f(); List <BlockPos> pathway = pathwayslist[i]; for (int j = 0; j < pathway.Count; j++) { BlockPos pos = pathway[j]; dirToMiddle.X += pos.X - SizeX / 2f; dirToMiddle.Y += pos.Y - SizeY / 2f; dirToMiddle.Z += pos.Z - SizeZ / 2f; } dirToMiddle.Normalize(); PathwaySides[i] = BlockFacing.FromNormal(dirToMiddle); BlockPos start = PathwayStarts[i] = pathwayslist[i][0].Copy(); PathwayOffsets[i] = new BlockPos[pathwayslist[i].Count]; for (int j = 0; j < pathwayslist[i].Count; j++) { PathwayOffsets[i][j] = pathwayslist[i][j].Sub(start); } } }
public void InitQuads() { quadTilesRef?.Dispose(); float height = 200; MeshData mesh = new MeshData(4, 6, false, true, true, false); mesh.CustomFloats = new CustomMeshDataPartFloat(4); mesh.CustomFloats.InterleaveStride = 4; mesh.CustomFloats.InterleaveOffsets = new int[] { 0 }; mesh.CustomFloats.InterleaveSizes = new int[] { 1 }; float x, y, z; Random rnd = new Random(); float resolution = 1.5f; float spread = 1.5f; float parts = 20 * resolution; float advance = 1f / resolution; for (int i = 0; i < 15; i++) { Vec3f dir = new Vec3f((float)rnd.NextDouble() * 20 - 10, (float)rnd.NextDouble() * 5 - 3, (float)rnd.NextDouble() * 20 - 10); dir.Normalize(); dir.Mul(advance); x = spread * ((float)rnd.NextDouble() * 800 - 400); y = spread * ((float)rnd.NextDouble() * 80 - 40); z = spread * ((float)rnd.NextDouble() * 800 - 400); for (int j = 0; j < parts + 2; j++) { float lngx = (float)rnd.NextDouble() * 5 + 20; float lngy = (float)rnd.NextDouble() * 4 + 4; float lngz = (float)rnd.NextDouble() * 5 + 20; x += dir.X * lngx; y += dir.Y * lngy; z += dir.Z * lngz; int lastelement = mesh.VerticesCount; mesh.AddVertex(x, y + height, z, j % 2, 1); mesh.AddVertex(x, y, z, j % 2, 0); float f = j / (parts - 1); mesh.CustomFloats.Add(f, f); if (j > 0 && j < (parts - 1)) { mesh.AddIndex(lastelement + 1); mesh.AddIndex(lastelement + 3); mesh.AddIndex(lastelement + 2); mesh.AddIndex(lastelement + 0); mesh.AddIndex(lastelement + 1); mesh.AddIndex(lastelement + 2); } } } quadTilesRef = capi.Render.UploadMesh(mesh); }
private static Vec3f NormalizeVector(Vec3f newvec, Vec3f oldvec) { return(Vec3f.Normalize(newvec)); }
public override void OnGameTick(float dt) { if (asyncSearchObject != null) { if (!asyncSearchObject.Finished) { return; } AfterFoundPath(); } if (!Active) { return; } bool nearHorizontally = false; int offset = 0; bool nearAllDirs = IsNearTarget(offset++, ref nearHorizontally) || IsNearTarget(offset++, ref nearHorizontally) || IsNearTarget(offset++, ref nearHorizontally) ; if (nearAllDirs) { waypointToReachIndex += offset; lastWaypointIncTotalMs = entity.World.ElapsedMilliseconds; } target = waypoints[Math.Min(waypoints.Count - 1, waypointToReachIndex)]; bool onlastWaypoint = waypointToReachIndex == waypoints.Count - 1; if (waypointToReachIndex >= waypoints.Count) { Stop(); OnGoalReached?.Invoke(); return; } bool stuck = (entity.CollidedVertically && entity.Controls.IsClimbing) || (entity.CollidedHorizontally && entity.ServerPos.Motion.Y <= 0) || (nearHorizontally && !nearAllDirs && entity.Properties.Habitat == EnumHabitat.Land) || (entity.CollidedHorizontally && waypoints.Count > 1 && waypointToReachIndex < waypoints.Count && entity.World.ElapsedMilliseconds - lastWaypointIncTotalMs > 2000) // If it takes more than 2 seconds to reach next waypoint (waypoints are always 1 block apart) //|| (entity.Swimming && false) ; // This used to test motion, but that makes no sense, we want to test if the entity moved, not if it had motion double distsq = prevPrevPos.SquareDistanceTo(prevPos); stuck |= (distsq < 0.01 * 0.01) ? (entity.World.Rand.NextDouble() < GameMath.Clamp(1 - distsq * 1.2, 0.1, 0.9)) : false; // Test movement progress between two points in 150 millisecond intervalls prevPosAccum += dt; if (prevPosAccum > 0.2) { prevPosAccum = 0; prevPrevPos.Set(prevPos); prevPos.Set(entity.ServerPos.X, entity.ServerPos.Y, entity.ServerPos.Z); } // Long duration tests to make sure we're not just wobbling around in the same spot distCheckAccum += dt; if (distCheckAccum > 2) { distCheckAccum = 0; if (sqDistToTarget - lastDistToTarget < 0.1) { stuck = true; stuckCounter += 30; } else if (!stuck) { stuckCounter = 0; // Only reset the stuckCounter in same tick as doing this test; otherwise the stuckCounter gets set to 0 every 2 or 3 ticks even if the entity collided horizontally (because motion vecs get set to 0 after the collision, so won't collide in the successive tick) } lastDistToTarget = sqDistToTarget; } if (stuck) { stuckCounter++; } if (GlobalConstants.OverallSpeedMultiplier > 0 && stuckCounter > 40 / GlobalConstants.OverallSpeedMultiplier) { //entity.World.SpawnParticles(10, ColorUtil.WhiteArgb, prevPos, prevPos, new Vec3f(0, 0, 0), new Vec3f(0, -1, 0), 1, 1); Stop(); OnStuck?.Invoke(); return; } EntityControls controls = entity.MountedOn == null ? entity.Controls : entity.MountedOn.Controls; if (controls == null) { return; } targetVec.Set( (float)(target.X - entity.ServerPos.X), (float)(target.Y - entity.ServerPos.Y), (float)(target.Z - entity.ServerPos.Z) ); targetVec.Normalize(); float desiredYaw = 0; if (sqDistToTarget >= 0.01) { desiredYaw = (float)Math.Atan2(targetVec.X, targetVec.Z); } float nowMoveSpeed = movingSpeed; if (sqDistToTarget < 1) { nowMoveSpeed = Math.Max(0.005f, movingSpeed * Math.Max(sqDistToTarget, 0.2f)); } float yawDist = GameMath.AngleRadDistance(entity.ServerPos.Yaw, desiredYaw); float turnSpeed = curTurnRadPerSec * dt * GlobalConstants.OverallSpeedMultiplier * movingSpeed; entity.ServerPos.Yaw += GameMath.Clamp(yawDist, -turnSpeed, turnSpeed); entity.ServerPos.Yaw = entity.ServerPos.Yaw % GameMath.TWOPI; double cosYaw = Math.Cos(entity.ServerPos.Yaw); double sinYaw = Math.Sin(entity.ServerPos.Yaw); controls.WalkVector.Set(sinYaw, GameMath.Clamp(targetVec.Y, -1, 1), cosYaw); controls.WalkVector.Mul(nowMoveSpeed * GlobalConstants.OverallSpeedMultiplier); // Make it walk along the wall, but not walk into the wall, which causes it to climb if (entity.Properties.RotateModelOnClimb && entity.Controls.IsClimbing && entity.ClimbingIntoFace != null && entity.Alive) { BlockFacing facing = entity.ClimbingIntoFace; if (Math.Sign(facing.Normali.X) == Math.Sign(controls.WalkVector.X)) { controls.WalkVector.X = 0; } if (Math.Sign(facing.Normali.Y) == Math.Sign(controls.WalkVector.Y)) { controls.WalkVector.Y = -controls.WalkVector.Y; } if (Math.Sign(facing.Normali.Z) == Math.Sign(controls.WalkVector.Z)) { controls.WalkVector.Z = 0; } } // entity.World.SpawnParticles(0.3f, ColorUtil.WhiteAhsl, target, target, new Vec3f(), new Vec3f(), 0.1f, 0.1f, 3f, EnumParticleModel.Cube); if (entity.Properties.Habitat == EnumHabitat.Underwater) { controls.FlyVector.Set(controls.WalkVector); Vec3d pos = entity.Pos.XYZ; Block inblock = entity.World.BlockAccessor.GetLiquidBlock((int)pos.X, (int)(pos.Y), (int)pos.Z); Block aboveblock = entity.World.BlockAccessor.GetLiquidBlock((int)pos.X, (int)(pos.Y + 1), (int)pos.Z); float waterY = (int)pos.Y + inblock.LiquidLevel / 8f + (aboveblock.IsLiquid() ? 9 / 8f : 0); float bottomSubmergedness = waterY - (float)pos.Y; // 0 = at swim line 1 = completely submerged float swimlineSubmergedness = GameMath.Clamp(bottomSubmergedness - ((float)entity.SwimmingOffsetY), 0, 1); swimlineSubmergedness = 1f - Math.Min(1f, swimlineSubmergedness + 0.5f); if (swimlineSubmergedness > 0f) { //Push the fish back underwater if part is poking out ... (may need future adaptation for sharks[?], probably by changing SwimmingOffsetY) controls.FlyVector.Y = GameMath.Clamp(controls.FlyVector.Y, -0.04f, -0.02f) * (1f - swimlineSubmergedness); } else { float factor = movingSpeed * GlobalConstants.OverallSpeedMultiplier / (float)Math.Sqrt(targetVec.X * targetVec.X + targetVec.Z * targetVec.Z); controls.FlyVector.Y = targetVec.Y * factor; } if (entity.CollidedHorizontally) { //TODO } } else if (entity.Swimming) { controls.FlyVector.Set(controls.WalkVector); Vec3d pos = entity.Pos.XYZ; Block inblock = entity.World.BlockAccessor.GetLiquidBlock((int)pos.X, (int)(pos.Y), (int)pos.Z); Block aboveblock = entity.World.BlockAccessor.GetLiquidBlock((int)pos.X, (int)(pos.Y + 1), (int)pos.Z); float waterY = (int)pos.Y + inblock.LiquidLevel / 8f + (aboveblock.IsLiquid() ? 9 / 8f : 0); float bottomSubmergedness = waterY - (float)pos.Y; // 0 = at swim line // 1 = completely submerged float swimlineSubmergedness = GameMath.Clamp(bottomSubmergedness - ((float)entity.SwimmingOffsetY), 0, 1); swimlineSubmergedness = Math.Min(1, swimlineSubmergedness + 0.5f); controls.FlyVector.Y = GameMath.Clamp(controls.FlyVector.Y, 0.02f, 0.04f) * swimlineSubmergedness; if (entity.CollidedHorizontally) { controls.FlyVector.Y = 0.05f; } } }