public void satisfy(float pdt)
        {
            tensionDirection.Set((float)(p1.Pos.X - p2.Pos.X), (float)(p1.Pos.Y - p2.Pos.Y), (float)(p1.Pos.Z - p2.Pos.Z));

            springLength = tensionDirection.Length();

            if (springLength == 0)
            {
                tensionDirection.Set((float)Rand.NextDouble() / 100f - 1 / 50f, (float)Rand.NextDouble() / 100f - 1 / 50f, (float)Rand.NextDouble() / 100f - 1 / 50f);
                springLength = tensionDirection.Length();
            }

            extension = springLength - rest_length;
            double tension = StretchStiffness * (extension * inverse_length);

            tensionDirection *= (float)(tension / springLength);

            p2.Tension.Add(tensionDirection);
            p1.Tension.Sub(tensionDirection);

            p2.TensionDirection.Set(tensionDirection);
            p1.TensionDirection.Set(-tensionDirection.X, -tensionDirection.Y, -tensionDirection.Z);

            p1.extension = extension;
            p2.extension = extension;
        }
Пример #2
0
        public override void DoRender3DOpaque(float dt, bool isShadowPass)
        {
            if (isSpectator)
            {
                return;
            }

            if (HeadControl)
            {
                bool isSelf = capi.World.Player.Entity.EntityId == entity.EntityId;
                loadModelMatrixForPlayer(entity, isSelf);
                if (isSelf)
                {
                    OriginPos.Set(0, 0, 0);
                }
            }
            else
            {
                loadModelMatrix(entity);
                Vec3d camPos = capi.World.Player.Entity.CameraPos;
                OriginPos.Set((float)(entity.Pos.X - camPos.X), (float)(entity.Pos.Y - camPos.Y), (float)(entity.Pos.Z - camPos.Z));
            }

            if (DoRenderHeldItem)
            {
                RenderHeldItem(isShadowPass);
            }
        }
Пример #3
0
        public override void DoRender3DOpaque(float dt, bool isShadowPass)
        {
            if (isSpectator)
            {
                return;
            }

            if (player != null)
            {
                bool isSelf = capi.World.Player.Entity.EntityId == entity.EntityId;
                loadModelMatrixForPlayer(entity, isSelf, dt);
                if (isSelf)
                {
                    OriginPos.Set(0, 0, 0);
                }
            }
            else
            {
                loadModelMatrix(entity, dt, isShadowPass);
                Vec3d camPos = capi.World.Player.Entity.CameraPos;
                OriginPos.Set((float)(entity.Pos.X - camPos.X), (float)(entity.Pos.Y - camPos.Y), (float)(entity.Pos.Z - camPos.Z));
            }

            if (isShadowPass)
            {
                DoRender3DAfterOIT(dt, true);
            }

            // This was rendered in DoRender3DAfterOIT() - WHY? It makes torches render in front of water
            if (DoRenderHeldItem && !entity.AnimManager.ActiveAnimationsByAnimCode.ContainsKey("lie") && !isSpectator)
            {
                RenderHeldItem(dt, isShadowPass, false);
                RenderHeldItem(dt, isShadowPass, true);
            }
        }
Пример #4
0
        public override void DoRender3DOpaque(float dt, bool isShadowPass)
        {
            if (isSpectator)
            {
                return;
            }

            if (player != null)
            {
                bool isSelf = capi.World.Player.Entity.EntityId == entity.EntityId;
                loadModelMatrixForPlayer(entity, isSelf, dt);
                if (isSelf)
                {
                    bool immersiveFpMode = capi.Settings.Bool["immersiveFpMode"];
                    if (!isShadowPass && capi.Render.CameraType == EnumCameraMode.FirstPerson && !immersiveFpMode)
                    {
                        return;
                    }

                    OriginPos.Set(0, 0, 0);

                    if (capi.Render.CameraType != EnumCameraMode.FirstPerson || !immersiveFpMode || isShadowPass)
                    {
                        skipRenderJointId  = -2;
                        skipRenderJointId2 = -2;
                    }
                    else
                    {
                        skipRenderJointId = entity.AnimManager.HeadController.HeadElement.JointId;

                        if (entity.AnimManager.HeadController.NeckElement != null)
                        {
                            skipRenderJointId2 = entity.AnimManager.HeadController.NeckElement.JointId;
                        }
                    }
                }
            }
            else
            {
                loadModelMatrix(entity, dt, isShadowPass);
                Vec3d camPos = capi.World.Player.Entity.CameraPos;
                OriginPos.Set((float)(entity.Pos.X - camPos.X), (float)(entity.Pos.Y - camPos.Y), (float)(entity.Pos.Z - camPos.Z));
            }

            if (isShadowPass)
            {
                DoRender3DAfterOIT(dt, true);
            }

            // This was rendered in DoRender3DAfterOIT() - WHY? It makes torches render in front of water
            if (DoRenderHeldItem && !entity.AnimManager.ActiveAnimationsByAnimCode.ContainsKey("lie") && !isSpectator)
            {
                RenderHeldItem(dt, isShadowPass, false);
                RenderHeldItem(dt, isShadowPass, true);
            }
        }
Пример #5
0
 /// <summary>
 /// Clears the transformation values.
 /// </summary>
 public void Clear()
 {
     Rotation.Set(0, 0, 0);
     Translation.Set(0, 0, 0);
     Origin.Set(0, 0, 0);
     Scale = 1f;
 }
Пример #6
0
        private float gnoise(Vec3f p)
        {
            int ix = (int)p.X;
            int iy = (int)p.Y;
            int iz = (int)p.Z;

            i.Set(ix, iy, iz);
            f.Set(p.X - ix, p.Y - iy, p.Z - iz);

            float ux = f.X * f.X * (3.0f - 2.0f * f.X);
            float uy = f.Y * f.Y * (3.0f - 2.0f * f.Y);
            float uz = f.Z * f.Z * (3.0f - 2.0f * f.Z);

            float ab1 = ghashDot(i, f, 0, 0, 0);
            float ab2 = ghashDot(i, f, 0, 0, 1);
            float at1 = ghashDot(i, f, 0, 1, 0);
            float at2 = ghashDot(i, f, 0, 1, 1);
            float bb1 = ghashDot(i, f, 1, 0, 0);
            float bb2 = ghashDot(i, f, 1, 0, 1);
            float bt1 = ghashDot(i, f, 1, 1, 0);
            float bt2 = ghashDot(i, f, 1, 1, 1);

            float rg1 = mix(mix(ab1, bb1, ux), mix(at1, bt1, ux), uy);
            float rg2 = mix(mix(ab2, bb2, ux), mix(at2, bt2, ux), uy);

            return(1.2f * mix(rg1, rg2, uz));
        }
Пример #7
0
 public Vec3f GetVelocity(Vec3d pos)
 {
     tmpVelo.Set(
         MinVelocity.X + AddVelocity.X * (float)rand.NextDouble(),
         MinVelocity.Y + AddVelocity.Y * (float)rand.NextDouble(),
         MinVelocity.Z + AddVelocity.Z * (float)rand.NextDouble()
         );
     return(tmpVelo);
 }
Пример #8
0
        private void SpawnBeeParticles(float dt)
        {
            float dayLightStrength = Api.World.Calendar.GetDayLightStrength(Pos.X, Pos.Z);

            if (Api.World.Rand.NextDouble() > 2 * dayLightStrength - 0.5)
            {
                return;
            }

            Random rand = Api.World.Rand;

            Bees.MinQuantity = actvitiyLevel;

            // Leave hive
            if (Api.World.Rand.NextDouble() > 0.5)
            {
                startPos.Set(Pos.X + 0.5f, Pos.Y + 0.5f, Pos.Z + 0.5f);
                minVelo.Set((float)rand.NextDouble() * 3 - 1.5f, (float)rand.NextDouble() * 1 - 0.5f, (float)rand.NextDouble() * 3 - 1.5f);

                Bees.MinPos               = startPos;
                Bees.MinVelocity          = minVelo;
                Bees.LifeLength           = 1f;
                Bees.WithTerrainCollision = false;
            }

            // Go back to hive
            else
            {
                startPos.Set(Pos.X + rand.NextDouble() * 5 - 2.5, Pos.Y + rand.NextDouble() * 2 - 1f, Pos.Z + rand.NextDouble() * 5 - 2.5f);
                endPos.Set(Pos.X + 0.5f, Pos.Y + 0.5f, Pos.Z + 0.5f);

                minVelo.Set((float)(endPos.X - startPos.X), (float)(endPos.Y - startPos.Y), (float)(endPos.Z - startPos.Z));
                minVelo /= 2;

                Bees.MinPos               = startPos;
                Bees.MinVelocity          = minVelo;
                Bees.WithTerrainCollision = true;
            }

            Api.World.SpawnParticles(Bees);
        }
Пример #9
0
        protected virtual void UpdateCustomFloatBuffer()
        {
            Vec3d pos = capi.World.Player.Entity.CameraPos;

            int i = 0;

            foreach (var dev in renderedDevices.Values)
            {
                tmp.Set((float)(dev.Position.X - pos.X), (float)(dev.Position.Y - pos.Y), (float)(dev.Position.Z - pos.Z));  //double precision int-double subtraction is needed here (even though the desired result is a float).  It's needed to have enough significant figures in the result, as the integer size could be large e.g. 50000 but the difference should be small (can easily be less than 5)

                UpdateLightAndTransformMatrix(i, tmp, dev.AngleRad % GameMath.TWOPI, dev);
                i++;
            }
        }
Пример #10
0
        private void SpawnBeeParticles(float dt)
        {
            if (api.World.Rand.NextDouble() > 2 * api.World.Calendar.DayLightStrength - 0.5)
            {
                return;
            }

            Random rand = api.World.Rand;

            // Leave hive
            if (api.World.Rand.NextDouble() > 0.5)
            {
                startPos.Set(pos.X + 0.5f, pos.Y + 0.5f, pos.Z + 0.5f);
                minVelo.Set((float)rand.NextDouble() * 3 - 1.5f, (float)rand.NextDouble() * 1 - 0.5f, (float)rand.NextDouble() * 3 - 1.5f);

                Bees.minPos               = startPos;
                Bees.minVelocity          = minVelo;
                Bees.lifeLength           = 1f;
                Bees.WithTerrainCollision = false;
            }

            // Go back to hive
            else
            {
                startPos.Set(pos.X + rand.NextDouble() * 5 - 2.5, pos.Y + rand.NextDouble() * 2 - 1f, pos.Z + rand.NextDouble() * 5 - 2.5f);
                endPos.Set(pos.X + 0.5f, pos.Y + 0.5f, pos.Z + 0.5f);

                minVelo.Set((float)(endPos.X - startPos.X), (float)(endPos.Y - startPos.Y), (float)(endPos.Z - startPos.Z));
                minVelo /= 2;

                Bees.minPos               = startPos;
                Bees.minVelocity          = minVelo;
                Bees.WithTerrainCollision = true;
            }

            api.World.SpawnParticles(Bees);
        }
Пример #11
0
        private void UpdateCustomFloatBuffer()
        {
            Vec3d pos = capi.World.Player.Entity.CameraPos;

            testRot.Z = -(capi.World.ElapsedMilliseconds / 900f) % GameMath.TWOPI;

            for (int i = 0; i < renderedDevices.Count; i++)
            {
                IMechanicalPowerDeviceVS dev = renderedDevices[i];

                tmp.Set((float)(dev.Position.X - pos.X), (float)(dev.Position.Y - pos.Y), (float)(dev.Position.Z - pos.Z));

                calcs[(int)dev.Type](i, tmp, testRot, dev.LightRgba);
            }
        }
        protected void UpdateCustomFloatBuffer()
        {
            Vec3d pos = capi.World.Player.Entity.CameraPos;


            for (int i = 0; i < renderedDevices.Count; i++)
            {
                IMechanicalPowerNode dev = renderedDevices[i];

                tmp.Set((float)(dev.Position.X - pos.X), (float)(dev.Position.Y - pos.Y), (float)(dev.Position.Z - pos.Z));

                testRot[2] = dev.Angle; // -(capi.World.ElapsedMilliseconds / 900f) % GameMath.TWOPI;

                UpdateLightAndTransformMatrix(i, tmp, testRot, dev);
            }
        }
Пример #13
0
        /// <summary>
        /// Renders the model.
        /// </summary>
        /// <param name="playerpos">The position of the Player</param>
        /// <param name="originUniformName"></param>
        /// <param name="frustumCullMode">The culling mode.  Default is CulHideDelay.</param>
        public void Render(Vec3d playerpos, string originUniformName, EnumFrustumCullMode frustumCullMode = EnumFrustumCullMode.CullNormal)
        {
            for (int i = 0; i < pools.Count; i++)
            {
                MeshDataPool pool = pools[i];
                pool.FrustumCull(frustumCuller, frustumCullMode);

                capi.Render.CurrentActiveShader.Uniform(originUniformName, tmp.Set(
                                                            (float)(pool.poolOrigin.X - playerpos.X),
                                                            (float)(pool.poolOrigin.Y - playerpos.Y),
                                                            (float)(pool.poolOrigin.Z - playerpos.Z)
                                                            ));

                capi.Render.RenderMesh(pool.modelRef, pool.indicesStartsByte, pool.indicesSizes, pool.indicesGroupsCount);
            }
        }
        public override bool ContinueExecute(float dt)
        {
            Vec3f targetVec = new Vec3f();

            targetVec.Set(
                (float)(targetEntity.ServerPos.X - entity.ServerPos.X),
                (float)(targetEntity.ServerPos.Y - entity.ServerPos.Y),
                (float)(targetEntity.ServerPos.Z - entity.ServerPos.Z)
                );

            float desiredYaw = (float)Math.Atan2(targetVec.X, targetVec.Z);

            float yawDist = GameMath.AngleRadDistance(entity.ServerPos.Yaw, desiredYaw);

            entity.ServerPos.Yaw += GameMath.Clamp(yawDist, -curTurnRadPerSec * dt, curTurnRadPerSec * dt);
            entity.ServerPos.Yaw  = entity.ServerPos.Yaw % GameMath.TWOPI;

            return(Math.Abs(yawDist) > 0.01);
        }
        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;
                }
            }
        }
Пример #16
0
        protected virtual void UpdateCustomFloatBuffer()
        {
            Vec3d camera            = capi.World.Player.Entity.CameraPos;
            float windSpeed         = API.Config.GlobalConstants.CurrentWindSpeedClient.X;
            float windWaveIntensity = 1.0f;
            float div = 30 * 3.5f;  //weakwave

            DefaultShaderUniforms shUniforms = capi.Render.ShaderUniforms;

            float wwaveHighFreq = shUniforms.WindWaveCounterHighFreq;
            float counter       = shUniforms.WindWaveCounter;

            int i = 0;

            foreach (var fruit in positions)
            {
                Vec3d  pos  = fruit.Key;
                Vec3f  rot  = fruit.Value.rotation;
                double posX = pos.X;
                double posY = pos.Y;
                double posZ = pos.Z;
                float  rotY = rot.Y;
                float  rotX = rot.X;
                float  rotZ = rot.Z;

                if (onGround)
                {
                    BlockPos blockPos = fruit.Value.behavior.Blockentity.Pos;
                    posY  = blockPos.Y - 0.0625;
                    posX += 1.1 * (posX - blockPos.X - 0.5);  //fruit on ground positioned further out from the plant center
                    posZ += 1.1 * (posZ - blockPos.Z - 0.5);
                    rot   = noRotation;
                    rotY  = (float)((posX + posZ) * 40 % 90); //some random rotation
                }
                else
                {
                    // Apply windwave

                    // Precisely replicate the effects of vertexwarp.vsh ... except where noted in comments below!
                    double x          = posX;
                    double y          = posY;
                    double z          = posZ;
                    float  heightBend = 0.7f * (0.5f + (float)y - (int)y);
                    double strength   = windWaveIntensity * (1 + windSpeed) * (0.5 + (posY - fruit.Value.behavior.Blockentity.Pos.Y)) / 2.0; // reduce the strength for lower fruit

                    v.Set((float)x % 4096f / 10, (float)z % 4096f / 10, counter % 1024f / 4);
                    float bendNoise = windSpeed * 0.2f + 1.4f * gnoise(v);

                    float bend = windSpeed * (0.8f + bendNoise) * heightBend * windWaveIntensity;
                    bend = Math.Min(4, bend) * 0.2857143f / 2.8f;    //no idea why this reduction by a factor of approximately 10 is needed, but it looks right

                    x        += wwaveHighFreq;
                    y        += wwaveHighFreq;
                    z        += wwaveHighFreq;
                    strength *= 0.25f;  // reduced strength because it looks right (fruits are less wobbly and closer to the center of the plant, compared with the foliage texture vertex tips)
                    double dx = strength * (Math.Sin(x * 10) / 120 + (2 * Math.Sin(x / 2) + Math.Sin(x + y) + Math.Sin(0.5 + 4 * x + 2 * y) + Math.Sin(1 + 6 * x + 3 * y) / 3) / div);
                    double dz = strength * ((2 * Math.Sin(z / 4) + Math.Sin(z + 3 * y) + Math.Sin(0.5 + 4 * z + 2 * y) + Math.Sin(1 + 6 * z + y) / 3) / div);
                    posX += dx;
                    posY += strength * (Math.Sin(5 * y) / 15 + Math.Cos(10 * x) / 10 + Math.Sin(3 * z) / 2 + Math.Cos(x * 2) / 2.2) / div;
                    posZ += dz;

                    // Also apply a small wind effect to the rotation, otherwise the fruits look 'stiff' because they remain upright
                    rotX += (float)(dz * 6 + bend / 2);
                    rotZ += (float)(dx * 6 + bend / 2);
                    posX += bend;
                }

                //double precision subtraction is needed here (even though the desired result is a float).
                // It's needed to have enough significant figures in the result, as the integer size could be large e.g. 50000 but the difference should be small (can easily be less than 5)
                tmp.Set((float)(posX - camera.X), (float)(posY - camera.Y), (float)(posZ - camera.Z));

                UpdateLightAndTransformMatrix(matrixAndLightFloats.Values, i, tmp, fruit.Value.behavior.LightRgba, rotX, rotY, rotZ);
                i++;
            }
        }
Пример #17
0
        public override void OnInteract(EntityAgent byEntity, ItemSlot slot, Vec3d hitPosition, EnumInteractMode mode)
        {
            EnumHandling handled = EnumHandling.PassThrough;

            foreach (EntityBehavior behavior in SidedProperties.Behaviors)
            {
                behavior.OnInteract(byEntity, slot, hitPosition, mode, ref handled);
                if (handled == EnumHandling.PreventSubsequent)
                {
                    break;
                }
            }

            if (handled == EnumHandling.PreventDefault || handled == EnumHandling.PreventSubsequent)
            {
                return;
            }

            if (mode == EnumInteractMode.Attack)
            {
                float damage     = slot.Itemstack == null ? 0.5f : slot.Itemstack.Collectible.GetAttackPower(slot.Itemstack);
                int   damagetier = slot.Itemstack == null ? 0 : slot.Itemstack.Collectible.ToolTier;

                damage *= byEntity.Stats.GetBlended("meleeWeaponsDamage");

                if (Attributes.GetBool("isMechanical", false))
                {
                    damage *= byEntity.Stats.GetBlended("mechanicalsDamage");
                }

                IPlayer byPlayer = null;

                if (byEntity is EntityPlayer && !IsActivityRunning("invulnerable"))
                {
                    byPlayer = (byEntity as EntityPlayer).Player;

                    World.PlaySoundAt(new AssetLocation("sounds/player/slap"), ServerPos.X, ServerPos.Y, ServerPos.Z, byPlayer);
                    slot?.Itemstack?.Collectible.OnAttackingWith(byEntity.World, byEntity, this, slot);
                }

                if (Api.Side == EnumAppSide.Client && damage > 1 && !IsActivityRunning("invulnerable") && Properties.Attributes?["spawnDamageParticles"].AsBool() == true)
                {
                    Vec3d pos    = SidedPos.XYZ + hitPosition;
                    Vec3d minPos = pos.AddCopy(-0.15, -0.15, -0.15);
                    Vec3d maxPos = pos.AddCopy(0.15, 0.15, 0.15);

                    int   textureSubId = this.Properties.Client.FirstTexture.Baked.TextureSubId;
                    Vec3f tmp          = new Vec3f();

                    for (int i = 0; i < 10; i++)
                    {
                        int color = (Api as ICoreClientAPI).EntityTextureAtlas.GetRandomColor(textureSubId);

                        tmp.Set(
                            1f - 2 * (float)World.Rand.NextDouble(),
                            2 * (float)World.Rand.NextDouble(),
                            1f - 2 * (float)World.Rand.NextDouble()
                            );

                        World.SpawnParticles(
                            1, color, minPos, maxPos,
                            tmp, tmp, 1.5f, 1f, 0.25f + (float)World.Rand.NextDouble() * 0.25f,
                            EnumParticleModel.Cube, byPlayer
                            );
                    }
                }

                DamageSource dmgSource = new DamageSource()
                {
                    Source       = (byEntity as EntityPlayer).Player == null ? EnumDamageSource.Entity : EnumDamageSource.Player,
                    SourceEntity = byEntity,
                    Type         = EnumDamageType.BluntAttack,
                    HitPosition  = hitPosition,
                    DamageTier   = damagetier
                };

                if (ReceiveDamage(dmgSource, damage))
                {
                    byEntity.DidAttack(dmgSource, this);
                }
            }
        }
Пример #18
0
        public void update(float dt)
        {
            if (pinned)
            {
                if (pinnedTo != null)
                {
                    if (pinnedTo.ShouldDespawn && pinnedTo.DespawnReason?.reason != EnumDespawnReason.Unload)
                    {
                        UnPin();
                        return;
                    }

                    // New ideas:
                    // don't apply force onto the player/entity on compression
                    // apply huge forces onto the player on strong extension (to prevent massive stretching) (just set player motion to 0 or so. or we add a new countermotion field thats used in EntityControlledPhysics?)

                    float weight = pinnedTo.Properties.Weight;
                    float counterTensionStrength = GameMath.Clamp(50f / weight, 0.1f, 2f);

                    bool  extraResist           = (pinnedTo as EntityAgent)?.Controls.Sneak == true || pinnedTo.AnimManager?.IsAnimationActive("sit") == true || pinnedTo.AnimManager?.IsAnimationActive("sleep") == true;
                    float tensionResistStrength = weight / 10f * (extraResist ? 200 : 0);

                    pinOffsetTransform.Identity();
                    pinOffsetTransform.RotateY(pinnedTo.SidedPos.Yaw - pinnedToOffsetStartYaw);
                    tmpvec.Set(pinnedToOffset.X, pinnedToOffset.Y, pinnedToOffset.Z, 1);
                    Vec4f outvec = pinOffsetTransform.TransformVector(tmpvec);

                    EntityPos pos = pinnedTo.SidedPos;
                    Pos.Set(pos.X + outvec.X, pos.Y + outvec.Y, pos.Z + outvec.Z);

                    bool pushable = !(pinnedTo is EntityPlayer eplr && eplr.Player.WorldData.CurrentGameMode == EnumGameMode.Creative);

                    if (pushable && extension > 0) // Do not act on compressive force
                    {
                        float f = counterTensionStrength * dt * 0.003f;
                        pos.Motion.Add(
                            GameMath.Clamp(Math.Abs(TensionDirection.X) - tensionResistStrength, 0, 400) * f * Math.Sign(TensionDirection.X),
                            GameMath.Clamp(Math.Abs(TensionDirection.Y) - tensionResistStrength, 0, 400) * f * Math.Sign(TensionDirection.Y),
                            GameMath.Clamp(Math.Abs(TensionDirection.Z) - tensionResistStrength, 0, 400) * f * Math.Sign(TensionDirection.Z)
                            );
                    }

                    Velocity.Set(0, 0, 0);
                }
                else
                {
                    Velocity.Set(0, 0, 0);

                    accum1s += dt;

                    if (accum1s >= 1)
                    {
                        accum1s = 0;
                        Block block = cs.api.World.BlockAccessor.GetBlock(PinnedToBlockPos);
                        if (!block.HasBehavior <BlockBehaviorRopeTieable>())
                        {
                            UnPin();
                        }
                    }
                }

                return;
            }

            // Calculate the force on this ball
            Vec3f force = Tension.Clone();

            force.Y -= GravityStrength * 10;

            // Calculate the acceleration
            Vec3f acceleration = force * (float)InvMass;

            if (CollideFlags == 0)
            {
                acceleration.X += (float)cs.windSpeed.X * InvMass;
            }


            // Update velocity
            Vec3f nextVelocity = Velocity + (acceleration * dt);

            // Damp the velocity
            nextVelocity *= dampFactor;

            // Collision detection
            float size = 0.1f;

            cs.pp.HandleBoyancy(Pos, nextVelocity, cs.boyant, GravityStrength, dt, size);
            CollideFlags = cs.pp.UpdateMotion(Pos, nextVelocity, size);

            dt *= 0.99f;
            Pos.Add(nextVelocity.X * dt, nextVelocity.Y * dt, nextVelocity.Z * dt);


            Velocity.Set(nextVelocity);
            Tension.Set(0, 0, 0);
        }
        public override void OnGameTick(float dt)
        {
            if (!Active)
            {
                return;
            }

            int   wayPointIndex = Math.Min(waypoints.Count - 1, waypointToReachIndex);
            Vec3d target        = waypoints[wayPointIndex];

            // Due to the nature of gravity and going down slope we sometimes end up at the next waypoint. So lets also test for the next waypoint
            // Doesn't seem to fully fix the issue though
            int   nextwayPointIndex = Math.Min(waypoints.Count - 1, waypointToReachIndex + 1);
            Vec3d nexttarget        = waypoints[nextwayPointIndex];

            // For land dwellers only check horizontal distance
            double sqDistToTarget    = Math.Min(target.SquareDistanceTo(entity.ServerPos.X, entity.ServerPos.Y, entity.ServerPos.Z), target.SquareDistanceTo(entity.ServerPos.X, entity.ServerPos.Y + 1, entity.ServerPos.Z));    // One block above is also ok
            double horsqDistToTarget = target.HorizontalSquareDistanceTo(entity.ServerPos.X, entity.ServerPos.Z);

            double sqDistToNextTarget = Math.Min(nexttarget.SquareDistanceTo(entity.ServerPos.X, entity.ServerPos.Y, entity.ServerPos.Z), nexttarget.SquareDistanceTo(entity.ServerPos.X, entity.ServerPos.Y + 1, entity.ServerPos.Z));       // One block above is also ok

            bool nearHorizontally = horsqDistToTarget < 1;
            bool nearAllDirs      = sqDistToTarget < targetDistance * targetDistance;

            bool nearAllDirsNext = sqDistToNextTarget < targetDistance * targetDistance;

            //float speedMul = 1;// entity.Properties.Habitat == API.Common.EnumHabitat.Land && waypointToReachIndex >= waypoints.Count - 1 ? Math.Min(1, GameMath.Sqrt(horsqDistToTarget)) : 1;
            //Console.WriteLine(speedMul);



            if (nearAllDirs || nearAllDirsNext)
            {
                waypointToReachIndex++;
                if (nearAllDirsNext && wayPointIndex != nextwayPointIndex)
                {
                    waypointToReachIndex++;
                }

                lastWaypointIncTotalMs = entity.World.ElapsedMilliseconds;

                if (waypointToReachIndex >= waypoints.Count)
                {
                    Stop();
                    OnGoalReached?.Invoke();
                    return;
                }
                else
                {
                    target = waypoints[waypointToReachIndex];
                }
            }

            bool stuck =
                (entity.CollidedVertically && entity.Controls.IsClimbing) ||
                (entity.ServerPos.SquareDistanceTo(prevPos) < 0.005 * 0.005) ||  // This used to test motion, but that makes no sense, we want to test if the entity moved, not if it had motion
                (entity.CollidedHorizontally && entity.ServerPos.Motion.Y <= 0) ||
                (nearHorizontally && !nearAllDirs && entity.Properties.Habitat == API.Common.EnumHabitat.Land) ||
                (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)
            ;

            prevPos.Set(entity.ServerPos.X, entity.ServerPos.Y, entity.ServerPos.Z);

            stuckCounter = stuck ? (stuckCounter + 1) : 0;

            if (GlobalConstants.OverallSpeedMultiplier > 0 && stuckCounter > 60 / 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)
                );

            float desiredYaw = 0;

            if (sqDistToTarget >= 0.01)
            {
                desiredYaw = (float)Math.Atan2(targetVec.X, targetVec.Z);
            }



            float yawDist = GameMath.AngleRadDistance(entity.ServerPos.Yaw, desiredYaw);

            entity.ServerPos.Yaw += GameMath.Clamp(yawDist, -curTurnRadPerSec * dt * GlobalConstants.OverallSpeedMultiplier, curTurnRadPerSec * dt * GlobalConstants.OverallSpeedMultiplier);
            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(movingSpeed * GlobalConstants.OverallSpeedMultiplier);// * speedMul);

            // 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.ClimbingOnFace != null && entity.Alive)
            {
                BlockFacing facing = entity.ClimbingOnFace;
                if (Math.Sign(facing.Normali.X) == Math.Sign(controls.WalkVector.X))
                {
                    controls.WalkVector.X = 0;
                }

                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.Swimming)
            {
                controls.FlyVector.Set(controls.WalkVector);

                Vec3d pos                 = entity.Pos.XYZ;
                Block inblock             = entity.World.BlockAccessor.GetBlock((int)pos.X, (int)(pos.Y), (int)pos.Z);
                Block aboveblock          = entity.World.BlockAccessor.GetBlock((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;
                }
            }
        }
 /// <summary>
 /// Gets the velocity of the particle.
 /// </summary>
 public Vec3f GetVelocity(Vec3d pos)
 {
     tmpVelo.Set(baseVelocity.X + Velocity[0].nextFloat(), baseVelocity.Y + Velocity[1].nextFloat(), baseVelocity.Z + Velocity[2].nextFloat());
     return(tmpVelo);
 }
Пример #21
0
        public int UpdateMesh(MeshData updateMesh, float dt)
        {
            var   cfloats = updateMesh.CustomFloats;
            Vec3d campos  = capi.World.Player.Entity.CameraPos;
            int   basep   = cfloats.Count;


            Vec4f lightRgba = api.World.BlockAccessor.GetLightRGBs(Constraints[Constraints.Count / 2].Point1.Pos.AsBlockPos);

            if (clothType == EnumClothType.Rope)
            {
                for (int i = 0; i < Constraints.Count; i++)
                {
                    ClothConstraint cc = Constraints[i];

                    Vec3d start = cc.Point1.Pos;
                    Vec3d end   = cc.Point2.Pos;

                    double dX = start.X - end.X;
                    double dY = start.Y - end.Y;
                    double dZ = start.Z - end.Z;


                    float yaw   = (float)Math.Atan2(dX, dZ) + GameMath.PIHALF;
                    float pitch = (float)Math.Atan2(Math.Sqrt(dZ * dZ + dX * dX), dY) + GameMath.PIHALF;

                    double nowx = start.X + (start.X - end.X) / 2;
                    double nowy = start.Y + (start.Y - end.Y) / 2;
                    double nowz = start.Z + (start.Z - end.Z) / 2;

                    cc.renderCenterPos.X += (nowx - cc.renderCenterPos.X) * dt * 20;
                    cc.renderCenterPos.Y += (nowy - cc.renderCenterPos.Y) * dt * 20;
                    cc.renderCenterPos.Z += (nowz - cc.renderCenterPos.Z) * dt * 20;

                    distToCam.Set(
                        (float)(cc.renderCenterPos.X - campos.X),
                        (float)(cc.renderCenterPos.Y - campos.Y),
                        (float)(cc.renderCenterPos.Z - campos.Z)
                        );

                    Mat4f.Identity(tmpMat);

                    Mat4f.Translate(tmpMat, tmpMat, 0, 1 / 32f, 0);

                    Mat4f.Translate(tmpMat, tmpMat, distToCam.X, distToCam.Y, distToCam.Z);
                    Mat4f.RotateY(tmpMat, tmpMat, yaw);
                    Mat4f.RotateZ(tmpMat, tmpMat, pitch);

                    float roll = i / 5f;
                    Mat4f.RotateX(tmpMat, tmpMat, roll);

                    Mat4f.Scale(tmpMat, tmpMat, new float[] { (float)cc.SpringLength, 1, 1 }); // + (float)Math.Sin(api.World.ElapsedMilliseconds / 1000f) * 0.1f
                    Mat4f.Translate(tmpMat, tmpMat, -1.5f, -1 / 32f, -0.5f);                   // not sure why the -1.5 here instead of -0.5



                    int j = basep + i * 20;
                    cfloats.Values[j++] = lightRgba.R;
                    cfloats.Values[j++] = lightRgba.G;
                    cfloats.Values[j++] = lightRgba.B;
                    cfloats.Values[j++] = lightRgba.A;

                    for (int k = 0; k < 16; k++)
                    {
                        cfloats.Values[j + k] = tmpMat[k];
                    }
                }
            }

            return(Constraints.Count);
        }
Пример #22
0
        public override void OnGameTick(float dt)
        {
            if (!Active)
            {
                return;
            }


            // For land dwellers only check horizontal distance
            double sqDistToTarget =
                entity.Properties.Habitat == API.Common.EnumHabitat.Land ?
                target.SquareDistanceTo(entity.ServerPos.X, target.Y, entity.ServerPos.Z) :
                target.SquareDistanceTo(entity.ServerPos.X, entity.ServerPos.Y, entity.ServerPos.Z)
            ;


            if (sqDistToTarget < targetDistance * targetDistance)
            {
                Stop();
                OnGoalReached?.Invoke();
                return;
            }

            bool stuck =
                (entity.CollidedVertically && entity.Controls.IsClimbing) ||
                (entity.ServerPos.SquareDistanceTo(prevPos) < 0.005 * 0.005) ||  // This used to test motion, but that makes no sense, we want to test if the entity moved, not if it had motion
                (entity.CollidedHorizontally && entity.ServerPos.Motion.Y <= 0)
            ;

            prevPos.Set(entity.ServerPos.X, entity.ServerPos.Y, entity.ServerPos.Z);

            stuckCounter = stuck ? (stuckCounter + 1) : 0;

            if (GlobalConstants.OverallSpeedMultiplier > 0 && stuckCounter > 20 / 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)
                );

            float desiredYaw = 0;

            if (sqDistToTarget >= 0.01)
            {
                desiredYaw = (float)Math.Atan2(targetVec.X, targetVec.Z);
            }



            float yawDist = GameMath.AngleRadDistance(entity.ServerPos.Yaw, desiredYaw);

            entity.ServerPos.Yaw += GameMath.Clamp(yawDist, -curTurnRadPerSec * dt * GlobalConstants.OverallSpeedMultiplier, curTurnRadPerSec * dt * GlobalConstants.OverallSpeedMultiplier);
            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(movingSpeed * 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.ClimbingOnFace != null && entity.Alive)
            {
                BlockFacing facing = entity.ClimbingOnFace;
                if (Math.Sign(facing.Normali.X) == Math.Sign(controls.WalkVector.X))
                {
                    controls.WalkVector.X = 0;
                }

                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.Swimming)
            {
                controls.FlyVector.Set(controls.WalkVector);
                controls.FlyVector.X *= 0.85f;
                controls.FlyVector.Z *= 0.85f;

                Vec3d pos                 = entity.Pos.XYZ;
                Block inblock             = entity.World.BlockAccessor.GetBlock((int)pos.X, (int)(pos.Y), (int)pos.Z);
                Block aboveblock          = entity.World.BlockAccessor.GetBlock((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.075f);
                controls.FlyVector.Y  = GameMath.Clamp(controls.FlyVector.Y, 0.002f, 0.004f) * swimlineSubmergedness;


                if (entity.CollidedHorizontally)
                {
                    controls.FlyVector.Y = 0.05f;
                }
            }
        }
Пример #23
0
        public override bool ContinueExecute(float dt)
        {
            Vec3f targetVec = new Vec3f();

            targetVec.Set(
                (float)(targetEntity.ServerPos.X - entity.ServerPos.X),
                (float)(targetEntity.ServerPos.Y - entity.ServerPos.Y),
                (float)(targetEntity.ServerPos.Z - entity.ServerPos.Z)
                );

            float desiredYaw = (float)Math.Atan2(targetVec.X, targetVec.Z);

            float yawDist = GameMath.AngleRadDistance(entity.ServerPos.Yaw, desiredYaw);

            entity.ServerPos.Yaw += GameMath.Clamp(yawDist, -curTurnRadPerSec * dt, curTurnRadPerSec * dt);
            entity.ServerPos.Yaw  = entity.ServerPos.Yaw % GameMath.TWOPI;

            if (Math.Abs(yawDist) > 0.02)
            {
                return(true);
            }

            if (animMeta != null)
            {
                animMeta.EaseInSpeed  = 1f;
                animMeta.EaseOutSpeed = 1f;
                entity.AnimManager.StartAnimation(animMeta);
            }

            accum += dt;

            if (accum > releaseAtMs / 1000f && !didThrow)
            {
                didThrow = true;

                EntityProperties type     = entity.World.GetEntityType(new AssetLocation("thrownstone-granite"));
                Entity           entitypr = entity.World.ClassRegistry.CreateEntity(type);
                ((EntityThrownStone)entitypr).FiredBy         = entity;
                ((EntityThrownStone)entitypr).Damage          = 1;
                ((EntityThrownStone)entitypr).ProjectileStack = new ItemStack(entity.World.GetItem(new AssetLocation("stone-granite")));
                ((EntityThrownStone)entitypr).NonCollectible  = true;

                Vec3d pos      = entity.ServerPos.XYZ.Add(0, entity.LocalEyePos.Y, 0);
                Vec3d aheadPos = targetEntity.ServerPos.XYZ.Add(0, targetEntity.LocalEyePos.Y, 0);

                double distf    = Math.Pow(pos.SquareDistanceTo(aheadPos), 0.1);
                Vec3d  velocity = (aheadPos - pos).Normalize() * GameMath.Clamp(distf - 1f, 0.1f, 1f);

                entitypr.ServerPos.SetPos(
                    entity.ServerPos.BehindCopy(0.21).XYZ.Add(0, entity.LocalEyePos.Y, 0)
                    );

                entitypr.ServerPos.Motion.Set(velocity);

                entitypr.Pos.SetFrom(entitypr.ServerPos);
                entitypr.World = entity.World;
                entity.World.SpawnEntity(entitypr);
            }

            return(accum < durationMs / 1000f);
        }