//  This method realy initiates/starts the missile
        //  IMPORTANT: Direction vector must be normalized!
        // Projectile count multiplier - when real rate of fire it 45, but we shoot only 10 projectiles as optimization count multiplier will be 4.5
        public void Start(MyProjectileAmmoDefinition ammoDefinition, MyEntity ignoreEntity, Vector3D origin, Vector3 initialVelocity, Vector3 directionNormalized, MyEntity weapon)
        {
            VRageRender.MyRenderProxy.GetRenderProfiler().StartProfilingBlock("Projectile.Start");

            m_projectileAmmoDefinition = ammoDefinition;
            m_state        = MyProjectileStateEnum.ACTIVE;
            m_ignoreEntity = ignoreEntity;
            m_origin       = origin + 0.1 * (Vector3D)directionNormalized;
            m_position     = m_origin;
            m_weapon       = weapon;

            if (ammoDefinition.ProjectileTrailProbability >= MyUtils.GetRandomFloat(0, 1))
            {
                LengthMultiplier = 40;
            }
            else
            {
                LengthMultiplier = 0;
            }

            /*
             * if (MyConstants.EnableAimCorrection)
             * {
             * if (m_ammoProperties.AllowAimCorrection) // Autoaim only available for player
             * {
             * VRageRender.MyRenderProxy.GetRenderProfiler().StartProfilingBlock("Projectile.Start autoaim generic");
             * //Intersection ignores children of "ignoreEntity", thus we must not hit our own barrels
             * correctedDirection = MyEntities.GetDirectionFromStartPointToHitPointOfNearestObject(ignoreEntity, m_weapon, origin, m_ammoProperties.MaxTrajectory);
             * VRageRender.MyRenderProxy.GetRenderProfiler().EndProfilingBlock();
             * }
             * }             */

            m_directionNormalized = directionNormalized;
            m_speed         = ammoDefinition.DesiredSpeed * (ammoDefinition.SpeedVar > 0.0f ? MyUtils.GetRandomFloat(1 - ammoDefinition.SpeedVar, 1 + ammoDefinition.SpeedVar) : 1.0f);
            m_velocity      = initialVelocity + m_directionNormalized * m_speed;;
            m_maxTrajectory = ammoDefinition.MaxTrajectory * MyUtils.GetRandomFloat(0.8f, 1.2f); // +/- 20%

            m_checkIntersectionIndex  = checkIntersectionCounter % CHECK_INTERSECTION_INTERVAL;
            checkIntersectionCounter += 3;
            m_positionChecked         = false;

            VRageRender.MyRenderProxy.GetRenderProfiler().EndProfilingBlock();
        }
Example #2
0
        //  This method realy initiates/starts the missile
        //  IMPORTANT: Direction vector must be normalized!
        public void Start(MyAmmoProperties ammoProperties, MyEntity ignoreEntity, Vector3 origin, Vector3 initialVelocity, Vector3 directionNormalized,
                          bool groupStart, float thicknessMultiplier, MyEntity weapon
                          )
        {
            if (MySession.Is25DSector)
            {
                directionNormalized.Y = 0;
                directionNormalized.Normalize();
                initialVelocity.Y = 0;
            }

            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("Projectile.Start");
            m_ammoProperties   = ammoProperties;
            m_state            = MyProjectileStateEnum.ACTIVE;
            m_ignorePhysObject = ignoreEntity;
            m_origin           = origin;
            m_position         = origin;
            m_externalAddition = 1.0f;
            m_weapon           = weapon;

            IsDummy = weapon != null && weapon.IsDummy;

            LengthMultiplier       = 1;
            FrontBillboardMaterial = null;
            FrontBillboardSize     = 1;
            BlendByCameraDirection = false;

            Vector3?correctedDirection = null;

            if (MyGameplayConstants.GameplayDifficultyProfile.EnableAimCorrection)
            {
                MyEntity entityToCheck;
                if (MyGuiScreenGamePlay.Static.ControlledEntity is MyPrefabLargeWeapon)
                {
                    entityToCheck = (MyGuiScreenGamePlay.Static.ControlledEntity as MyPrefabLargeWeapon).GetGun();
                }
                else
                {
                    entityToCheck = MyGuiScreenGamePlay.Static.ControlledEntity;
                }
                // TODO: Make proper test that source off projectile is player ship, testing ignore object is STUPID!
                if (m_ammoProperties.AllowAimCorrection && (ignoreEntity == entityToCheck)) // Autoaim only available for player
                {
                    MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("Projectile.Start autoaim generic");
                    //Intersection ignores children of "ignoreEntity", thus we must not hit our own barrels
                    correctedDirection = MyEntities.GetDirectionFromStartPointToHitPointOfNearestObject(ignoreEntity, origin, m_ammoProperties.MaxTrajectory);
                    MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock();
                }
            }

            if (correctedDirection != null)
            {
                m_directionNormalized = correctedDirection.Value;
            }
            else
            {
                m_directionNormalized = directionNormalized;
            }

            m_speed            = ammoProperties.DesiredSpeed * (ammoProperties.SpeedVar > 0.0f ? MyMwcUtils.GetRandomFloat(1 - ammoProperties.SpeedVar, 1 + ammoProperties.SpeedVar) : 1.0f);
            m_externalVelocity = initialVelocity;
            m_velocity         = m_directionNormalized * m_speed;
            m_maxTrajectory    = ammoProperties.MaxTrajectory * MyMwcUtils.GetRandomFloat(0.8f, 1.2f); // +/- 20%


            m_thicknessMultiplier = thicknessMultiplier;

            m_checkIntersectionIndex  = checkIntersectionCounter % CHECK_INTERSECTION_INTERVAL;
            checkIntersectionCounter += 3;
            m_positionChecked         = false;
            m_groupStart              = groupStart;

            if (groupStart)
            {
                m_trailEffect             = MyParticlesManager.CreateParticleEffect((int)MyParticleEffectsIDEnum.Trail_Shotgun);
                m_trailEffect.AutoDelete  = false;
                m_trailEffect.WorldMatrix = Matrix.CreateTranslation(m_position);
            }

            if (groupStart)
            {
                LastProjectileGroup = m_ownGroup;
                m_ownGroup.Killed   = false;
            }

            if (LastProjectileGroup != null && LastProjectileGroup.Killed == false)
            {
                m_sharedGroup = LastProjectileGroup;
            }
            else
            {
                m_sharedGroup = null;
            }

            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock();
        }
        //  Draw the projectile but only if desired polyline trail distance can fit in the trajectory (otherwise we will see polyline growing from the origin and it's ugly).
        //  Or draw if this is last draw of this projectile (useful for short-distance shots).
        public void Draw()
        {
            const float PROJECTILE_POLYLINE_DESIRED_LENGTH = 120;

            //var velPerFrame = m_velocity * VRage.Game.MyEngineConstants.PHYSICS_STEP_SIZE_IN_SECONDS;
            //for (int i = 0; i < 70; i += 5)
            //{
            //    Color col = new Color(255, 0, i * 5, 255);
            //    VRageRender.MyRenderProxy.DebugDrawLine3D(m_position + i * velPerFrame, m_position + i * velPerFrame + 5 * velPerFrame, col, Color.Yellow, false);
            //}

            double trajectoryLength = Vector3D.Distance(m_position, m_origin);
            if ((trajectoryLength > 0) || (m_state == MyProjectileStateEnum.KILLED))
            {
                if (m_state == MyProjectileStateEnum.KILLED)
                {
                    m_state = MyProjectileStateEnum.KILLED_AND_DRAWN;
                }

                if (!m_positionChecked)
                    return;
             
                //  If we calculate previous position using normalized direction (insted of velocity), projectile trails will 
                //  look like coming from cannon, and that is desired. Even during fast movement, acceleration, rotation or changes in movement directions.
                //Vector3 previousPosition = m_position - m_directionNormalized * projectileTrailLength * 1.05f;
                Vector3D previousPosition = m_position - m_directionNormalized * PROJECTILE_POLYLINE_DESIRED_LENGTH * VRage.Game.MyEngineConstants.UPDATE_STEP_SIZE_IN_SECONDS;
                //Vector3 previousPosition = m_previousPosition;
                //Vector3 previousPosition = m_initialSunWindPosition - MyMwcUtils.Normalize(m_desiredVelocity) * projectileTrailLength;

                Vector3D direction = Vector3D.Normalize(m_position - previousPosition);

                double projectileTrailLength = LengthMultiplier * m_projectileAmmoDefinition.ProjectileTrailScale;// PROJECTILE_POLYLINE_DESIRED_LENGTH;

                projectileTrailLength *= MyUtils.GetRandomFloat(0.6f, 0.8f);

                if (trajectoryLength < projectileTrailLength)
                {
                    projectileTrailLength = trajectoryLength;
                }

                previousPosition = m_position - projectileTrailLength * direction;


                //float color = MyMwcUtils.GetRandomFloat(1, 2);
                float color = MyUtils.GetRandomFloat(1, 2);
                float thickness = MyUtils.GetRandomFloat(0.2f, 0.3f) * m_projectileAmmoDefinition.ProjectileTrailScale;

                //  Line particles (polyline) don't look good in distance. Start and end aren't rounded anymore and they just
                //  look like a pieces of paper. Especially when zoom-in.
                thickness *= MathHelper.Lerp(0.2f, 0.8f, MySector.MainCamera.Zoom.GetZoomLevel());

                float alphaCone = 1;
                
                if (projectileTrailLength > 0)
                {
                    if (m_projectileAmmoDefinition.ProjectileTrailMaterial != null)
                    {
                        MyTransparentGeometry.AddLineBillboard(m_projectileAmmoDefinition.ProjectileTrailMaterial, new Vector4(m_projectileAmmoDefinition.ProjectileTrailColor, 1),
                            previousPosition, direction, (float)projectileTrailLength, thickness);
                    }
                    else
                    {
                        MyTransparentGeometry.AddLineBillboard("ProjectileTrailLine", new Vector4(m_projectileAmmoDefinition.ProjectileTrailColor * color, 1) * alphaCone,
                            previousPosition, direction, (float)projectileTrailLength, thickness);
                    }
                }                
            }
        }
        //  Update position, check collisions, etc.
        //  Return false if projectile dies/timeouts in this tick.
        public bool Update()
        {
            //  Projectile was killed , but still not last time drawn, so we don't need to do update (we are waiting for last draw)
            if (m_state == MyProjectileStateEnum.KILLED)
                return true;
            //  Projectile was killed and last time drawn, so we can finally remove it from buffer
            if (m_state == MyProjectileStateEnum.KILLED_AND_DRAWN)
            {
                StopEffect();
                return false;
            }

            Vector3D position = m_position;
            m_position += m_velocity * VRage.Game.MyEngineConstants.UPDATE_STEP_SIZE_IN_SECONDS * MyFakes.SIMULATION_SPEED;

            //  Distance timeout
            Vector3 positionDelta = m_position - m_origin;
            if (Vector3.Dot(positionDelta, positionDelta) >= m_maxTrajectory * m_maxTrajectory)
            {
                StopEffect();
                m_state = MyProjectileStateEnum.KILLED;
                return true;
            }

            m_checkIntersectionIndex = ++m_checkIntersectionIndex % CHECK_INTERSECTION_INTERVAL;
            if (m_checkIntersectionIndex != 0 && m_positionChecked) //check only each n-th intersection
                return true;

            //  Calculate hit point, create decal and throw debris particles
            Vector3D lineEndPosition = position + CHECK_INTERSECTION_INTERVAL * (m_velocity * VRage.Game.MyEngineConstants.UPDATE_STEP_SIZE_IN_SECONDS * MyFakes.SIMULATION_SPEED);

            LineD line = new LineD(m_positionChecked ? position : m_origin, lineEndPosition);
            m_positionChecked = true;

            IMyEntity entity;
            Vector3D hitPosition;
            Vector3 hitNormal;
            MyCharacterHitInfo charHitInfo;
            GetHitEntityAndPosition(line, out entity, out hitPosition, out hitNormal, out charHitInfo);
            if (entity == null || entity == m_ignoreEntity || entity.Physics == null)
                return true;

            if (IsIgnoredEntity(entity))
            {
                return true; // prevent player shooting himself
            }

            ProfilerShort.Begin("Projectile.Update");

            bool headShot = false;
            MyCharacter hitCharacter = entity as MyCharacter;
            if (hitCharacter != null)
            {
                IStoppableAttackingTool stoppableTool = hitCharacter.CurrentWeapon as IStoppableAttackingTool;
                if (stoppableTool != null)
                    stoppableTool.StopShooting(OwnerEntity);

                headShot = charHitInfo.HitHead && m_projectileAmmoDefinition.HeadShot; // allow head shots only for ammo supporting it in definition
            }

            m_position = hitPosition;

            bool isProjectileGroupKilled = false;

            if (!isProjectileGroupKilled)
            {
                MySurfaceImpactEnum surfaceImpact;
                MyStringHash materialType;
                GetSurfaceAndMaterial(entity, ref  hitPosition, out surfaceImpact, out materialType);

                PlayHitSound(materialType, entity, hitPosition, m_projectileAmmoDefinition.PhysicalMaterial);

                MyHitInfo hitInfo = new MyHitInfo();
                hitInfo.Normal = hitNormal;
                hitInfo.Position = hitPosition;
                hitInfo.Velocity = m_velocity;

                float damage = headShot ? m_projectileAmmoDefinition.ProjectileHeadShotDamage : m_projectileAmmoDefinition.ProjectileMassDamage;
                DoDamage(damage, hitInfo, charHitInfo, entity);

                //particle effect defined in materialProperties.sbc
                Vector3D particleHitPosition = hitPosition + line.Direction * -0.2;
                if (MyMaterialPropertiesHelper.Static.TryCreateCollisionEffect(MyMaterialPropertiesHelper.CollisionType.Hit, particleHitPosition, hitNormal, m_projectileAmmoDefinition.PhysicalMaterial, materialType) == false)
                {
                    //default effect when none other was found
                    if (surfaceImpact != MySurfaceImpactEnum.CHARACTER)
                        MyParticleEffects.CreateBasicHitParticles(m_projectileAmmoDefinition.ProjectileOnHitEffectName, ref hitPosition, ref hitNormal, ref line.Direction, entity, m_weapon, 1, OwnerEntity);
                }

                CreateDecal(materialType);

                if (m_weapon == null || (entity.GetTopMostParent() != m_weapon.GetTopMostParent()))
                    ApplyProjectileForce(entity, hitPosition, m_directionNormalized, false, m_projectileAmmoDefinition.ProjectileHitImpulse * m_impulseMultiplier);

                StopEffect();
                m_state = MyProjectileStateEnum.KILLED;
            }
            ProfilerShort.End();
            return true;
        }
        //  This method realy initiates/starts the missile
        //  IMPORTANT: Direction vector must be normalized!
        // Projectile count multiplier - when real rate of fire it 45, but we shoot only 10 projectiles as optimization count multiplier will be 4.5
        public void Start(MyProjectileAmmoDefinition ammoDefinition, MyEntity ignoreEntity, Vector3D origin, Vector3 initialVelocity, Vector3 directionNormalized, MyEntity weapon)
        {
            VRageRender.MyRenderProxy.GetRenderProfiler().StartProfilingBlock("Projectile.Start");

            m_projectileAmmoDefinition = ammoDefinition;
            m_state = MyProjectileStateEnum.ACTIVE;
            m_ignoreEntity = ignoreEntity;
            m_origin = origin + 0.1 * (Vector3D)directionNormalized;
            m_position = m_origin;
            m_weapon = weapon;

            if (ammoDefinition.ProjectileTrailProbability >= MyUtils.GetRandomFloat(0, 1))
                LengthMultiplier = 40;
            else
                LengthMultiplier = 0;
                        /*
            if (MyConstants.EnableAimCorrection)
            {
                if (m_ammoProperties.AllowAimCorrection) // Autoaim only available for player
                {
                    VRageRender.MyRenderProxy.GetRenderProfiler().StartProfilingBlock("Projectile.Start autoaim generic");
                    //Intersection ignores children of "ignoreEntity", thus we must not hit our own barrels
                    correctedDirection = MyEntities.GetDirectionFromStartPointToHitPointOfNearestObject(ignoreEntity, m_weapon, origin, m_ammoProperties.MaxTrajectory);
                    VRageRender.MyRenderProxy.GetRenderProfiler().EndProfilingBlock();
                }
            }             */

            m_directionNormalized = directionNormalized;
            m_speed = ammoDefinition.DesiredSpeed * (ammoDefinition.SpeedVar > 0.0f ? MyUtils.GetRandomFloat(1 - ammoDefinition.SpeedVar, 1 + ammoDefinition.SpeedVar) : 1.0f);
            m_velocity = initialVelocity + m_directionNormalized * m_speed; 
            m_maxTrajectory = ammoDefinition.MaxTrajectory * MyUtils.GetRandomFloat(0.8f, 1.2f); // +/- 20%

            m_checkIntersectionIndex = checkIntersectionCounter % CHECK_INTERSECTION_INTERVAL;
            checkIntersectionCounter += 3;
            m_positionChecked = false;

            LineD line = new LineD(m_origin, m_origin + m_directionNormalized * m_maxTrajectory);

            if (m_entityRaycastResult == null)
            {
                m_entityRaycastResult = new List<MyLineSegmentOverlapResult<MyEntity>>(16);
            }
            else
            {
                m_entityRaycastResult.Clear();
            }
            MyGamePruningStructure.GetAllEntitiesInRay(ref line, m_entityRaycastResult, MyEntityQueryType.Static);

            foreach (var entity in m_entityRaycastResult)
            {
                MyVoxelPhysics planetPhysics = entity.Element as MyVoxelPhysics;
                if (planetPhysics != null)
                {
                    planetPhysics.PrefetchShapeOnRay(ref line);
                }
            }

            VRageRender.MyRenderProxy.GetRenderProfiler().EndProfilingBlock();
        }
Example #6
0
        //  This method realy initiates/starts the missile
        //  IMPORTANT: Direction vector must be normalized!
        // Projectile count multiplier - when real rate of fire it 45, but we shoot only 10 projectiles as optimization count multiplier will be 4.5
        public void Start(MyProjectileAmmoDefinition ammoDefinition, MyEntity ignoreEntity, Vector3D origin, Vector3 initialVelocity, Vector3 directionNormalized, MyEntity weapon)
        {
            VRageRender.MyRenderProxy.GetRenderProfiler().StartProfilingBlock("Projectile.Start");

            m_projectileAmmoDefinition = ammoDefinition;
            m_state = MyProjectileStateEnum.ACTIVE;
            m_ignoreEntity = ignoreEntity;
            m_origin = origin + 0.1 * (Vector3D)directionNormalized;
            m_position = m_origin;
            m_weapon = weapon;

            if (ammoDefinition.ProjectileTrailProbability >= MyUtils.GetRandomFloat(0, 1))
                LengthMultiplier = 40;
            else
                LengthMultiplier = 0;
                        /*
            if (MyConstants.EnableAimCorrection)
            {
                if (m_ammoProperties.AllowAimCorrection) // Autoaim only available for player
                {
                    VRageRender.MyRenderProxy.GetRenderProfiler().StartProfilingBlock("Projectile.Start autoaim generic");
                    //Intersection ignores children of "ignoreEntity", thus we must not hit our own barrels
                    correctedDirection = MyEntities.GetDirectionFromStartPointToHitPointOfNearestObject(ignoreEntity, m_weapon, origin, m_ammoProperties.MaxTrajectory);
                    VRageRender.MyRenderProxy.GetRenderProfiler().EndProfilingBlock();
                }
            }             */

            m_directionNormalized = directionNormalized;
            m_speed = ammoDefinition.DesiredSpeed * (ammoDefinition.SpeedVar > 0.0f ? MyUtils.GetRandomFloat(1 - ammoDefinition.SpeedVar, 1 + ammoDefinition.SpeedVar) : 1.0f);
            m_velocity = initialVelocity + m_directionNormalized * m_speed; ;
            m_maxTrajectory = ammoDefinition.MaxTrajectory * MyUtils.GetRandomFloat(0.8f, 1.2f); // +/- 20%

            m_checkIntersectionIndex = checkIntersectionCounter % CHECK_INTERSECTION_INTERVAL;
            checkIntersectionCounter += 3;
            m_positionChecked = false;

            VRageRender.MyRenderProxy.GetRenderProfiler().EndProfilingBlock();
        }
Example #7
0
        //  Update position, check collisions, etc.
        //  Return false if projectile dies/timeouts in this tick.
        public bool Update()
        {
            //  Projectile was killed , but still not last time drawn, so we don't need to do update (we are waiting for last draw)
            if (m_state == MyProjectileStateEnum.KILLED)
                return true;
            //  Projectile was killed and last time drawn, so we can finally remove it from buffer
            if (m_state == MyProjectileStateEnum.KILLED_AND_DRAWN)
            {
                StopEffect();
                return false;
            }

            Vector3D position = m_position;
            m_position += m_velocity * MyEngineConstants.UPDATE_STEP_SIZE_IN_SECONDS;

            //  Distance timeout
            Vector3 positionDelta = m_position - m_origin;
            if (Vector3.Dot(positionDelta, positionDelta) >= m_maxTrajectory * m_maxTrajectory)
            {
                StopEffect();
                m_state = MyProjectileStateEnum.KILLED;
                return true;
            }

            m_checkIntersectionIndex = ++m_checkIntersectionIndex % CHECK_INTERSECTION_INTERVAL;
            if (m_checkIntersectionIndex != 0 && m_positionChecked) //check only each n-th intersection
                return true;

            //  Calculate hit point, create decal and throw debris particles
            Vector3D lineEndPosition = position + CHECK_INTERSECTION_INTERVAL * (m_velocity * MyEngineConstants.UPDATE_STEP_SIZE_IN_SECONDS);

            LineD line = new LineD(m_positionChecked ? position : m_origin, lineEndPosition);
            m_positionChecked = true;

            IMyEntity entity;
            Vector3D hitPosition;
            Vector3 hitNormal;
            bool headShot;

            GetHitEntityAndPosition(line, out entity, out hitPosition, out hitNormal, out headShot);
            if (entity == null || entity == m_ignoreEntity || entity.Physics == null)
                return true;

            ProfilerShort.Begin("Projectile.Update");

            {
                MyCharacter hitCharacter = entity as MyCharacter;
                if (hitCharacter != null)
                {
                    IStoppableAttackingTool stoppableTool = hitCharacter.CurrentWeapon as IStoppableAttackingTool;
                    if (stoppableTool != null)
                        stoppableTool.StopShooting(OwnerEntity);
                }
            }

            m_position = hitPosition;

            bool isProjectileGroupKilled = false;

            if (!isProjectileGroupKilled)
            {
                MySurfaceImpactEnum surfaceImpact;
                MyStringHash materialType;
                GetSurfaceAndMaterial(entity, ref  hitPosition, out surfaceImpact, out materialType);

                PlayHitSound(materialType, entity, hitPosition);
                DoDamage(headShot ? m_projectileAmmoDefinition.ProjectileHeadShotDamage : m_projectileAmmoDefinition.ProjectileMassDamage, hitPosition, entity);
                //  Create smoke and debris particle at the place of voxel/model hit

                if (surfaceImpact != MySurfaceImpactEnum.CHARACTER)
                    m_projectileAmmoDefinition.ProjectileOnHitParticles(ref hitPosition, ref hitNormal, ref line.Direction, entity, m_weapon, 1, OwnerEntity);
                m_projectileAmmoDefinition.ProjectileOnHitMaterialParticles(ref hitPosition, ref hitNormal, ref line.Direction, entity, surfaceImpact, m_weapon, 1);

                CreateDecal(materialType);

                if (m_weapon == null || (entity.GetTopMostParent() != m_weapon.GetTopMostParent()))
                    ApplyProjectileForce(entity, hitPosition, m_directionNormalized, false, m_projectileAmmoDefinition.ProjectileHitImpulse * m_impulseMultiplier);

                StopEffect();
                m_state = MyProjectileStateEnum.KILLED;
            }
            ProfilerShort.End();
            return true;
        }
Example #8
0
        //  This method realy initiates/starts the missile
        //  IMPORTANT: Direction vector must be normalized!
        public void Start(MyAmmoProperties ammoProperties, MyEntity ignoreEntity, Vector3 origin, Vector3 initialVelocity, Vector3 directionNormalized, 
            bool groupStart, float thicknessMultiplier, MyEntity weapon
            )
        {
            if (MySession.Is25DSector)
            {
                directionNormalized.Y = 0;
                directionNormalized.Normalize();
                initialVelocity.Y = 0;
            }

            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("Projectile.Start");
            m_ammoProperties = ammoProperties;
            m_state = MyProjectileStateEnum.ACTIVE;
            m_ignorePhysObject = ignoreEntity;
            m_origin = origin;
            m_position = origin;
            m_externalAddition = 1.0f;
            m_weapon = weapon;

            IsDummy = weapon != null && weapon.IsDummy;

            LengthMultiplier = 1;
            FrontBillboardMaterial = null;
            FrontBillboardSize = 1;
            BlendByCameraDirection = false;

            Vector3? correctedDirection = null;

            if (MyGameplayConstants.GameplayDifficultyProfile.EnableAimCorrection)
            {
                MyEntity entityToCheck;
                if (MyGuiScreenGamePlay.Static.ControlledEntity is MyPrefabLargeWeapon)
                {
                    entityToCheck = (MyGuiScreenGamePlay.Static.ControlledEntity as MyPrefabLargeWeapon).GetGun();
                }
                else
                {
                    entityToCheck = MyGuiScreenGamePlay.Static.ControlledEntity;
                }
                // TODO: Make proper test that source off projectile is player ship, testing ignore object is STUPID!
                if (m_ammoProperties.AllowAimCorrection && (ignoreEntity == entityToCheck)) // Autoaim only available for player
                {
                    MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("Projectile.Start autoaim generic");
                    //Intersection ignores children of "ignoreEntity", thus we must not hit our own barrels
                    correctedDirection = MyEntities.GetDirectionFromStartPointToHitPointOfNearestObject(ignoreEntity, origin, m_ammoProperties.MaxTrajectory);
                    MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock();
                }
            }

            if (correctedDirection != null)
            {
                m_directionNormalized = correctedDirection.Value;
            }
            else
            {
                m_directionNormalized = directionNormalized;
            }

            m_speed = ammoProperties.DesiredSpeed * (ammoProperties.SpeedVar > 0.0f ? MyMwcUtils.GetRandomFloat(1 - ammoProperties.SpeedVar, 1 + ammoProperties.SpeedVar) : 1.0f);
            m_externalVelocity = initialVelocity;
            m_velocity = m_directionNormalized * m_speed;
            m_maxTrajectory = ammoProperties.MaxTrajectory * MyMwcUtils.GetRandomFloat(0.8f, 1.2f); // +/- 20%

           
            m_thicknessMultiplier = thicknessMultiplier;

            m_checkIntersectionIndex = checkIntersectionCounter % CHECK_INTERSECTION_INTERVAL;
            checkIntersectionCounter += 3;
            m_positionChecked = false;
            m_groupStart = groupStart;

            if (groupStart)
            {
                m_trailEffect = MyParticlesManager.CreateParticleEffect((int)MyParticleEffectsIDEnum.Trail_Shotgun);
                m_trailEffect.AutoDelete = false;
                m_trailEffect.WorldMatrix = Matrix.CreateTranslation(m_position);
            }

            if (groupStart)
            {
                LastProjectileGroup = m_ownGroup;
                m_ownGroup.Killed = false;
            }

            if (LastProjectileGroup != null && LastProjectileGroup.Killed == false)
            {
                m_sharedGroup = LastProjectileGroup;
            }
            else
                m_sharedGroup = null;

            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock();
        }
Example #9
0
        //  Update position, check collisions, etc.
        //  Return false if projectile dies/timeouts in this tick.
        public bool Update()
        {
            //  Projectile was killed , but still not last time drawn, so we don't need to do update (we are waiting for last draw)
            if (m_state == MyProjectileStateEnum.KILLED)
            {
                return(true);
            }
            //  Projectile was killed and last time drawn, so we can finally remove it from buffer
            if (m_state == MyProjectileStateEnum.KILLED_AND_DRAWN)
            {
                StopEffect();
                return(false);
            }

            Vector3D position = m_position;

            m_position += m_velocity * VRage.Game.MyEngineConstants.UPDATE_STEP_SIZE_IN_SECONDS * MyFakes.SIMULATION_SPEED;

            //  Distance timeout
            Vector3 positionDelta = m_position - m_origin;

            if (Vector3.Dot(positionDelta, positionDelta) >= m_maxTrajectory * m_maxTrajectory)
            {
                StopEffect();
                m_state = MyProjectileStateEnum.KILLED;
                return(true);
            }

            m_checkIntersectionIndex = ++m_checkIntersectionIndex % CHECK_INTERSECTION_INTERVAL;
            if (m_checkIntersectionIndex != 0 && m_positionChecked) //check only each n-th intersection
            {
                return(true);
            }

            //  Calculate hit point, create decal and throw debris particles
            Vector3D lineEndPosition = position + CHECK_INTERSECTION_INTERVAL * (m_velocity * VRage.Game.MyEngineConstants.UPDATE_STEP_SIZE_IN_SECONDS * MyFakes.SIMULATION_SPEED);

            LineD line = new LineD(m_positionChecked ? position : m_origin, lineEndPosition);

            m_positionChecked = true;

            IMyEntity entity;
            MyHitInfo hitInfo;
            object    customdata;

            GetHitEntityAndPosition(line, out entity, out hitInfo, out customdata);
            if (entity == null || entity.Physics == null)
            {
                return(true);
            }

            if (IsIgnoredEntity(entity))
            {
                return(true); // prevent player shooting himself
            }

            ProfilerShort.Begin("Projectile.Update");

            bool        headShot     = false;
            MyCharacter hitCharacter = entity as MyCharacter;

            if (hitCharacter != null)
            {
                IStoppableAttackingTool stoppableTool = hitCharacter.CurrentWeapon as IStoppableAttackingTool;
                if (stoppableTool != null)
                {
                    stoppableTool.StopShooting(OwnerEntity);
                }

                headShot = (customdata as MyCharacterHitInfo).HitHead && m_projectileAmmoDefinition.HeadShot; // allow head shots only for ammo supporting it in definition
            }

            m_position = hitInfo.Position;

            bool isProjectileGroupKilled = false;

            if (!isProjectileGroupKilled)
            {
                MySurfaceImpactEnum surfaceImpact;
                MyStringHash        materialType;
                GetSurfaceAndMaterial(entity, ref line, ref hitInfo.Position, out surfaceImpact, out materialType);

                PlayHitSound(materialType, entity, hitInfo.Position, m_projectileAmmoDefinition.PhysicalMaterial);

                hitInfo.Velocity = m_velocity;

                float damage = entity is IMyCharacter ? (headShot ? m_projectileAmmoDefinition.ProjectileHeadShotDamage : m_projectileAmmoDefinition.ProjectileHealthDamage) : m_projectileAmmoDefinition.ProjectileMassDamage;
                DoDamage(damage, hitInfo, customdata, entity);

                MyDecals.HandleAddDecal(entity, hitInfo, materialType, m_projectileAmmoDefinition.PhysicalMaterial, (customdata as MyCharacterHitInfo), damage);

                //particle effect defined in materialProperties.sbc
                Vector3D particleHitPosition = hitInfo.Position + (Vector3D)line.Direction * -0.2;
                bool     createdEffect       = MyMaterialPropertiesHelper.Static.TryCreateCollisionEffect(MyMaterialPropertiesHelper.CollisionType.Hit, particleHitPosition, hitInfo.Normal, m_projectileAmmoDefinition.PhysicalMaterial, materialType);
                if (!createdEffect && surfaceImpact != MySurfaceImpactEnum.CHARACTER)
                {
                    MyParticleEffects.CreateBasicHitParticles(m_projectileAmmoDefinition.ProjectileOnHitEffectName, ref hitInfo.Position, ref hitInfo.Normal, ref line.Direction, entity, m_weapon, 1, OwnerEntity);
                }

                CreateDecal(materialType);

                if (m_weapon == null || (entity.GetTopMostParent() != m_weapon.GetTopMostParent()))
                {
                    ApplyProjectileForce(entity, hitInfo.Position, m_directionNormalized, false, m_projectileAmmoDefinition.ProjectileHitImpulse * m_impulseMultiplier);
                }

                StopEffect();
                m_state = MyProjectileStateEnum.KILLED;
            }
            ProfilerShort.End();
            return(true);
        }
Example #10
0
        //  This method realy initiates/starts the missile
        //  IMPORTANT: Direction vector must be normalized!
        // Projectile count multiplier - when real rate of fire it 45, but we shoot only 10 projectiles as optimization count multiplier will be 4.5
        public void Start(MyProjectileAmmoDefinition ammoDefinition, MyEntity[] ignoreEntities, Vector3D origin, Vector3 initialVelocity, Vector3 directionNormalized, MyEntity weapon)
        {
            VRageRender.MyRenderProxy.GetRenderProfiler().StartProfilingBlock("Projectile.Start");

            m_projectileAmmoDefinition = ammoDefinition;
            m_state           = MyProjectileStateEnum.ACTIVE;
            m_ignoredEntities = ignoreEntities;  // store ref to ignored entities
            m_origin          = origin + 0.1 * (Vector3D)directionNormalized;
            m_position        = m_origin;
            m_weapon          = weapon;

            if (ammoDefinition.ProjectileTrailProbability >= MyUtils.GetRandomFloat(0, 1))
            {
                LengthMultiplier = 40;
            }
            else
            {
                LengthMultiplier = 0;
            }

            /*
             * if (MyConstants.EnableAimCorrection)
             * {
             * if (m_ammoProperties.AllowAimCorrection) // Autoaim only available for player
             * {
             * VRageRender.MyRenderProxy.GetRenderProfiler().StartProfilingBlock("Projectile.Start autoaim generic");
             * //Intersection ignores children of "ignoreEntity", thus we must not hit our own barrels
             * correctedDirection = MyEntities.GetDirectionFromStartPointToHitPointOfNearestObject(ignoreEntity, m_weapon, origin, m_ammoProperties.MaxTrajectory);
             * VRageRender.MyRenderProxy.GetRenderProfiler().EndProfilingBlock();
             * }
             * }             */

            m_directionNormalized = directionNormalized;
            m_speed         = ammoDefinition.DesiredSpeed * (ammoDefinition.SpeedVar > 0.0f ? MyUtils.GetRandomFloat(1 - ammoDefinition.SpeedVar, 1 + ammoDefinition.SpeedVar) : 1.0f);
            m_velocity      = initialVelocity + m_directionNormalized * m_speed;
            m_maxTrajectory = ammoDefinition.MaxTrajectory * MyUtils.GetRandomFloat(0.8f, 1.2f); // +/- 20%

            m_checkIntersectionIndex  = checkIntersectionCounter % CHECK_INTERSECTION_INTERVAL;
            checkIntersectionCounter += 3;
            m_positionChecked         = false;

            LineD line = new LineD(m_origin, m_origin + m_directionNormalized * m_maxTrajectory);

            if (m_entityRaycastResult == null)
            {
                m_entityRaycastResult = new List <MyLineSegmentOverlapResult <MyEntity> >(16);
            }
            else
            {
                m_entityRaycastResult.Clear();
            }
            MyGamePruningStructure.GetAllEntitiesInRay(ref line, m_entityRaycastResult, MyEntityQueryType.Static);

            foreach (var entity in m_entityRaycastResult)
            {
                MyVoxelPhysics planetPhysics = entity.Element as MyVoxelPhysics;
                if (planetPhysics != null)
                {
                    planetPhysics.PrefetchShapeOnRay(ref line);
                }
            }

            VRageRender.MyRenderProxy.GetRenderProfiler().EndProfilingBlock();
        }
        //  Update position, check collisions, etc.
        //  Return false if projectile dies/timeouts in this tick.
        public bool Update()
        {
            //  Projectile was killed , but still not last time drawn, so we don't need to do update (we are waiting for last draw)
            if (m_state == MyProjectileStateEnum.KILLED)
            {
                return(true);
            }
            //  Projectile was killed and last time drawn, so we can finally remove it from buffer
            if (m_state == MyProjectileStateEnum.KILLED_AND_DRAWN)
            {
                StopEffect();
                return(false);
            }

            Vector3D position = m_position;

            m_position += m_velocity * MyEngineConstants.UPDATE_STEP_SIZE_IN_SECONDS;

            //  Distance timeout
            Vector3 positionDelta = m_position - m_origin;

            if (Vector3.Dot(positionDelta, positionDelta) >= m_maxTrajectory * m_maxTrajectory)
            {
                StopEffect();
                m_state = MyProjectileStateEnum.KILLED;
                return(true);
            }

            m_checkIntersectionIndex = ++m_checkIntersectionIndex % CHECK_INTERSECTION_INTERVAL;
            if (m_checkIntersectionIndex != 0 && m_positionChecked) //check only each n-th intersection
            {
                return(true);
            }

            //  Calculate hit point, create decal and throw debris particles
            Vector3D lineEndPosition = position + CHECK_INTERSECTION_INTERVAL * (m_velocity * MyEngineConstants.UPDATE_STEP_SIZE_IN_SECONDS);

            LineD line = new LineD(m_positionChecked ? position : m_origin, lineEndPosition);

            m_positionChecked = true;

            IMyEntity entity;
            Vector3D  hitPosition;
            Vector3   hitNormal;

            GetHitEntityAndPosition(line, out entity, out hitPosition, out hitNormal);
            if (entity == null || entity == m_ignoreEntity || entity.Physics == null)
            {
                return(true);
            }
            ProfilerShort.Begin("Projectile.Update");
            m_position = hitPosition;

            bool isProjectileGroupKilled = false;

            if (!isProjectileGroupKilled)
            {
                MySurfaceImpactEnum surfaceImpact;
                MyStringHash        materialType;
                GetSurfaceAndMaterial(entity, out surfaceImpact, out materialType);

                PlayHitSound(materialType, entity, hitPosition);
                DoDamage(hitPosition, entity);
                //  Create smoke and debris particle at the place of voxel/model hit

                if (surfaceImpact != MySurfaceImpactEnum.CHARACTER)
                {
                    m_projectileAmmoDefinition.ProjectileOnHitParticles(ref hitPosition, ref hitNormal, ref line.Direction, entity, m_weapon, 1, OwnerEntity);
                }
                m_projectileAmmoDefinition.ProjectileOnHitMaterialParticles(ref hitPosition, ref hitNormal, ref line.Direction, entity, surfaceImpact, m_weapon, 1);

                CreateDecal(materialType);

                if (m_weapon == null || (entity.GetTopMostParent() != m_weapon.GetTopMostParent()))
                {
                    ApplyProjectileForce(entity, hitPosition, m_directionNormalized, false, m_projectileAmmoDefinition.ProjectileHitImpulse * m_impulseMultiplier);
                }

                StopEffect();
                m_state = MyProjectileStateEnum.KILLED;
            }
            ProfilerShort.End();
            return(true);
        }
Example #12
0
        //  Draw the projectile but only if desired polyline trail distance can fit in the trajectory (otherwise we will see polyline growing from the origin and it's ugly).
        //  Or draw if this is last draw of this projectile (useful for short-distance shots).
        public void Draw()
        {
            const float PROJECTILE_POLYLINE_DESIRED_LENGTH = 120;

            float trajectoryLength = Vector3.Distance(m_position, m_origin);
            if ((trajectoryLength > 0) || (m_state == MyProjectileStateEnum.KILLED))
            {
                if (m_state == MyProjectileStateEnum.KILLED)
                {
                    m_state = MyProjectileStateEnum.KILLED_AND_DRAWN;
                }

                if (!m_positionChecked)
                    return;
                
              
                    
                //  If we calculate previous position using normalized direction (insted of velocity), projectile trails will 
                //  look like coming from cannon, and that is desired. Even during fast movement, acceleration, rotation or changes in movement directions.
                //Vector3 previousPosition = m_position - m_directionNormalized * projectileTrailLength * 1.05f;
                Vector3 previousPosition = m_position - m_directionNormalized * PROJECTILE_POLYLINE_DESIRED_LENGTH * MyConstants.PHYSICS_STEP_SIZE_IN_SECONDS;
                //Vector3 previousPosition = m_previousPosition;
                //Vector3 previousPosition = m_initialSunWindPosition - MyMwcUtils.Normalize(m_desiredVelocity) * projectileTrailLength;

                Vector3 direction = Vector3.Normalize(m_position - previousPosition);

                float projectileTrailLength = 40 * LengthMultiplier;// PROJECTILE_POLYLINE_DESIRED_LENGTH;

                projectileTrailLength *= MyMwcUtils.GetRandomFloat(0.6f, 0.8f);

                if (trajectoryLength < projectileTrailLength)
                {
                    projectileTrailLength = trajectoryLength;
                }

                previousPosition = m_position - projectileTrailLength * direction;
                if (m_externalAddition >= 1.0f)
                    m_externalAddition = 0.5f;


                //float color = MyMwcUtils.GetRandomFloat(1, 2);
                float color = MyMwcUtils.GetRandomFloat(1, 2);
                float thickness = m_thicknessMultiplier * MyMwcUtils.GetRandomFloat(0.2f, 0.3f);

                //  Line particles (polyline) don't look good in distance. Start and end aren't rounded anymore and they just
                //  look like a pieces of paper. Especially when zoom-in.
                thickness *= MathHelper.Lerp(0.2f, 0.8f, MyCamera.Zoom.GetZoomLevel());

                float alphaCone = 1;
                float alphaGlare = 1;

                if (BlendByCameraDirection)
                {
                    float angle = 1 - Math.Abs(Vector3.Dot(MyMwcUtils.Normalize(MyCamera.ForwardVector), direction));
                    alphaGlare = (float)Math.Pow(1 - angle, 2);
                    alphaCone = (1 - (float)Math.Pow(1 - angle, 30));
                }

                MyTransparentGeometry.AddLineBillboard(MyTransparentMaterialEnum.ProjectileTrailLine, new Vector4(m_ammoProperties.TrailColor * color, 1) * alphaCone,
                    previousPosition, direction, projectileTrailLength, thickness);

                if (FrontBillboardMaterial.HasValue)
                {
                    MyTransparentGeometry.AddPointBillboard(FrontBillboardMaterial.Value, new Vector4(m_ammoProperties.TrailColor * color, 1) * alphaGlare, m_position, 0.8f * FrontBillboardSize, 0);
                }                   
            }
        }
Example #13
0
        //  Update position, check collisions, etc.
        //  Return false if projectile dies/timeouts in this tick.
        public bool Update()
        {
            //  Projectile was killed , but still not last time drawn, so we don't need to do update (we are waiting for last draw)
            if (m_state == MyProjectileStateEnum.KILLED) 
                return true;

            //  Projectile was killed and last time drawn, so we can finally remove it from buffer
            if (m_state == MyProjectileStateEnum.KILLED_AND_DRAWN)
            {
                if (m_trailEffect != null)
                {
                    // stop the trail effect
                    m_trailEffect.Stop();
                    m_trailEffect = null;
                }

                return false;
            }

            Vector3 position = m_position;
            m_position += m_velocity * MyConstants.PHYSICS_STEP_SIZE_IN_SECONDS;
            m_velocity = m_externalVelocity * m_externalAddition + m_directionNormalized * m_speed;
            if (m_externalAddition < 1.0f)
                m_externalAddition *= 0.5f;

            //  Distance timeout
            float trajectoryLength = Vector3.Distance(m_position, m_origin);
            if (trajectoryLength >= m_maxTrajectory)
            {
                if (m_trailEffect != null)
                {
                    // stop the trail effect
                    m_trailEffect.Stop();
                    m_trailEffect = null;
                }

                m_state = MyProjectileStateEnum.KILLED;
                return true;
            }

            if (m_trailEffect != null)
                m_trailEffect.WorldMatrix = Matrix.CreateTranslation(m_position);

            m_checkIntersectionIndex++;
            m_checkIntersectionIndex = m_checkIntersectionIndex % CHECK_INTERSECTION_INTERVAL;
                                 
            //check only each n-th intersection
            if (m_checkIntersectionIndex != 0)
                return true;

            //  Calculate hit point, create decal and throw debris particles
            Vector3 lineEndPosition = position + CHECK_INTERSECTION_INTERVAL * (m_velocity * MyConstants.PHYSICS_STEP_SIZE_IN_SECONDS);

            MyLine line = new MyLine(m_positionChecked ? position : m_origin, lineEndPosition, true);
            m_positionChecked = true;

            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("MyEntities.GetIntersectionWithLine()");
            MyIntersectionResultLineTriangleEx? intersection = MyEntities.GetIntersectionWithLine(ref line, m_ignorePhysObject, null, false);
            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock();

            MyEntity physObject = intersection != null ? intersection.Value.Entity : null;
            if (physObject != null)
            {
                while (physObject.Physics == null && physObject.Parent != null)
                {
                    physObject = physObject.Parent;
                }
            }

            if ((intersection != null) && (physObject != null) && (physObject.Physics.CollisionLayer != MyConstants.COLLISION_LAYER_UNCOLLIDABLE) && m_ignorePhysObject != physObject)
            {
                MyIntersectionResultLineTriangleEx intersectionValue = intersection.Value;

                bool isPlayerShip = MySession.PlayerShip == physObject;

                MyMaterialType materialType = isPlayerShip ? MyMaterialType.PLAYERSHIP : physObject.Physics.MaterialType;

                //material properties
                MyMaterialTypeProperties materialProperties = MyMaterialsConstants.GetMaterialProperties(materialType);

                bool isProjectileGroupKilled = false;

                if (m_sharedGroup != null)
                {
                    isProjectileGroupKilled = m_sharedGroup.Killed;
                    m_sharedGroup.Killed = true;
                }

                if (!isProjectileGroupKilled)
                {
                    //  Play bullet hit cue 
                    MyAudio.AddCue3D(m_ammoProperties.IsExplosive ? materialProperties.ExpBulletHitCue : materialProperties.BulletHitCue, intersectionValue.IntersectionPointInWorldSpace, Vector3.Zero, Vector3.Zero, Vector3.Zero);
                }

                float decalAngle = MyMwcUtils.GetRandomRadian();

                //  If we hit the glass of a miner ship, we need to create special bullet hole decals
                //  drawn from inside the cockpit and change phys object so rest of the code will think we hit the parent
                //  IMPORTANT: Intersection between projectile and glass is calculated only for mining ship in which player sits. So for enemies this will be never calculated.
                if (intersection.Value.Entity is MyCockpitGlassEntity)
                {
                    if (!isProjectileGroupKilled)
                    {
                        MyCockpitGlassDecalTexturesEnum bulletHoleDecalTexture;
                        float bulletHoleDecalSize;

                        if (MyMwcUtils.GetRandomBool(3))
                        {
                            bulletHoleDecalTexture = MyCockpitGlassDecalTexturesEnum.BulletHoleOnGlass;
                            bulletHoleDecalSize = 0.25f;
                        }
                        else
                        {
                            bulletHoleDecalTexture = MyCockpitGlassDecalTexturesEnum.BulletHoleSmallOnGlass;
                            bulletHoleDecalSize = 0.1f;
                        }

                        //  Place bullet hole decal on player's cockpit glass (seen from inside the ship)
                        MyCockpitGlassDecals.Add(bulletHoleDecalTexture, bulletHoleDecalSize, decalAngle, 1.0f, ref intersectionValue, false);

                        //  Create hit particles throwed into the cockpit (it's simulation of broken glass particles)
                        //  IMPORTANT: This particles will be relative to miner ship, so we create them in object space coordinates and update them by object WorldMatrix every time we draw them
                        //MyParticleEffects.CreateHitParticlesGlass(ref intersectionValue.IntersectionPointInObjectSpace, ref intersectionValue.NormalInWorldSpace, ref line.Direction, physObject.Parent);
                    }
                }

                //  If this was "mine", it must explode
                else if (physObject is MyMineBase)
                {
                    m_state = MyProjectileStateEnum.KILLED;
                    if (!IsDummy)
                        (physObject as MyAmmoBase).Explode();
                    return true;
                }

                //  If this was missile, cannon shot, it must explode if it is not mine missile
                else if (physObject is MyAmmoBase)
                {
                    if (((MyAmmoBase)physObject).OwnerEntity == m_ignorePhysObject)
                    {
                        m_state = MyProjectileStateEnum.KILLED;
                        if (!IsDummy)
                            (physObject as MyAmmoBase).Explode();
                        return true;
                    }
                }

                else if (this.OwnerEntity is MySmallShip && (MySmallShip)this.OwnerEntity == MySession.PlayerShip && physObject is MyStaticAsteroid && !physObject.IsDestructible)
                {
                    if (this.m_ammoProperties.IsExplosive || (this.m_ammoProperties.AmmoType == MyAmmoType.Explosive && this.m_weapon is Weapons.MyShotGun))
                    {
                        HUD.MyHud.ShowIndestructableAsteroidNotification();
                    }
                }

                else if (!isProjectileGroupKilled && !isPlayerShip)
                {
                    //  Create smoke and debris particle at the place of voxel/model hit
                    m_ammoProperties.OnHitParticles(ref intersectionValue.IntersectionPointInWorldSpace, ref intersectionValue.Triangle.InputTriangleNormal, ref line.Direction, physObject, m_weapon, OwnerEntity);

                    MySurfaceImpactEnum surfaceImpact;
                    if (intersectionValue.Entity is MyVoxelMap)
                    {
                        var voxelMap = intersectionValue.Entity as MyVoxelMap;
                        var voxelCoord = voxelMap.GetVoxelCenterCoordinateFromMeters(ref intersectionValue.IntersectionPointInWorldSpace);
                        var material = voxelMap.GetVoxelMaterial(ref voxelCoord);
                        if (material == MyMwcVoxelMaterialsEnum.Indestructible_01 ||
                            material == MyMwcVoxelMaterialsEnum.Indestructible_02 ||
                            material == MyMwcVoxelMaterialsEnum.Indestructible_03 ||
                            material == MyMwcVoxelMaterialsEnum.Indestructible_04 ||
                            material == MyMwcVoxelMaterialsEnum.Indestructible_05_Craters_01)
                            surfaceImpact = MySurfaceImpactEnum.INDESTRUCTIBLE;
                        else
                            surfaceImpact = MySurfaceImpactEnum.DESTRUCTIBLE;
                    }
                    else if (intersectionValue.Entity is MyStaticAsteroid)
                        surfaceImpact = MySurfaceImpactEnum.INDESTRUCTIBLE;
                    else surfaceImpact = MySurfaceImpactEnum.METAL;

                    m_ammoProperties.OnHitMaterialSpecificParticles(ref intersectionValue.IntersectionPointInWorldSpace, ref intersectionValue.Triangle.InputTriangleNormal, ref line.Direction, physObject, surfaceImpact, m_weapon);
                }

                if (!(physObject is MyExplosionDebrisBase) && physObject != MySession.PlayerShip)
                {
                    //  Decal size depends on material. But for mining ship create smaller decal as original size looks to large on the ship.
                    float decalSize = MyMwcUtils.GetRandomFloat(materialProperties.BulletHoleSizeMin,
                                                                materialProperties.BulletHoleSizeMax);

                    //  Place bullet hole decal
                    float randomColor = MyMwcUtils.GetRandomFloat(0.5f, 1.0f);

                    MyDecals.Add(
                        materialProperties.BulletHoleDecal,
                        decalSize,
                        decalAngle,
                        new Vector4(randomColor, randomColor, randomColor, 1),
                        false,
                        ref intersectionValue, 
                        0.0f,
                        m_ammoProperties.DecalEmissivity, MyDecalsConstants.DECAL_OFFSET_BY_NORMAL);
                }

                if (!(physObject is MyVoxelMap) && !IsDummy)
                {
                    ApplyProjectileForce(physObject, intersectionValue.IntersectionPointInWorldSpace, m_directionNormalized, isPlayerShip);
                }


                //  If this object is miner ship, then shake his head little bit
                if (physObject is MySmallShip && !IsDummy)
                {
                    MySmallShip minerShip = (MySmallShip)physObject;
                    minerShip.IncreaseHeadShake(MyHeadShakeConstants.HEAD_SHAKE_AMOUNT_AFTER_PROJECTILE_HIT);
                }



                //Handle damage

                MyEntity damagedObject = intersectionValue.Entity;

                // not a very nice way to damage actual prefab associated with the large ship weapon (if MyPrefabLargeWeapon is reworked, it might change)
                if (damagedObject is MyLargeShipBarrelBase)
                {
                    damagedObject = damagedObject.Parent;
                }
                if (damagedObject is MyLargeShipGunBase)
                {
                    MyLargeShipGunBase physObj = damagedObject as MyLargeShipGunBase;
                    if (physObj.PrefabParent != null)
                        damagedObject = physObj.PrefabParent;
                }

                //  Decrease health of stricken object
                if (!IsDummy)
                {
                    damagedObject.DoDamage(m_ammoProperties.HealthDamage, m_ammoProperties.ShipDamage, m_ammoProperties.EMPDamage, m_ammoProperties.DamageType, m_ammoProperties.AmmoType, m_ignorePhysObject);
                    if (MyMultiplayerGameplay.IsRunning)
                    {
                        var ammo = MyAmmoConstants.FindAmmo(m_ammoProperties);
                        MyMultiplayerGameplay.Static.ProjectileHit(damagedObject, intersectionValue.IntersectionPointInWorldSpace, this.m_directionNormalized, ammo, this.OwnerEntity);
                    }
                }

                if (m_trailEffect != null)
                {
                    // stop the trail effect
                    m_trailEffect.Stop();
                    m_trailEffect = null;
                }

                //  Kill this projectile (set the position to intersection point, so we draw trail polyline only up to this point)
                m_position = intersectionValue.IntersectionPointInWorldSpace;
                m_state = MyProjectileStateEnum.KILLED;

                return true;
            }
                
            return true;
        }
Example #14
0
        //  Update position, check collisions, etc.
        //  Return false if projectile dies/timeouts in this tick.
        public bool Update()
        {
            //  Projectile was killed , but still not last time drawn, so we don't need to do update (we are waiting for last draw)
            if (m_state == MyProjectileStateEnum.KILLED)
            {
                return(true);
            }

            //  Projectile was killed and last time drawn, so we can finally remove it from buffer
            if (m_state == MyProjectileStateEnum.KILLED_AND_DRAWN)
            {
                if (m_trailEffect != null)
                {
                    // stop the trail effect
                    m_trailEffect.Stop();
                    m_trailEffect = null;
                }

                return(false);
            }

            Vector3 position = m_position;

            m_position += m_velocity * MyConstants.PHYSICS_STEP_SIZE_IN_SECONDS;
            m_velocity  = m_externalVelocity * m_externalAddition + m_directionNormalized * m_speed;
            if (m_externalAddition < 1.0f)
            {
                m_externalAddition *= 0.5f;
            }

            //  Distance timeout
            float trajectoryLength = Vector3.Distance(m_position, m_origin);

            if (trajectoryLength >= m_maxTrajectory)
            {
                if (m_trailEffect != null)
                {
                    // stop the trail effect
                    m_trailEffect.Stop();
                    m_trailEffect = null;
                }

                m_state = MyProjectileStateEnum.KILLED;
                return(true);
            }

            if (m_trailEffect != null)
            {
                m_trailEffect.WorldMatrix = Matrix.CreateTranslation(m_position);
            }

            m_checkIntersectionIndex++;
            m_checkIntersectionIndex = m_checkIntersectionIndex % CHECK_INTERSECTION_INTERVAL;

            //check only each n-th intersection
            if (m_checkIntersectionIndex != 0)
            {
                return(true);
            }

            //  Calculate hit point, create decal and throw debris particles
            Vector3 lineEndPosition = position + CHECK_INTERSECTION_INTERVAL * (m_velocity * MyConstants.PHYSICS_STEP_SIZE_IN_SECONDS);

            MyLine line = new MyLine(m_positionChecked ? position : m_origin, lineEndPosition, true);

            m_positionChecked = true;

            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("MyEntities.GetIntersectionWithLine()");
            MyIntersectionResultLineTriangleEx?intersection = MyEntities.GetIntersectionWithLine(ref line, m_ignorePhysObject, null, false);

            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock();

            MyEntity physObject = intersection != null ? intersection.Value.Entity : null;

            if (physObject != null)
            {
                while (physObject.Physics == null && physObject.Parent != null)
                {
                    physObject = physObject.Parent;
                }
            }

            if ((intersection != null) && (physObject != null) && (physObject.Physics.CollisionLayer != MyConstants.COLLISION_LAYER_UNCOLLIDABLE) && m_ignorePhysObject != physObject)
            {
                MyIntersectionResultLineTriangleEx intersectionValue = intersection.Value;

                bool isPlayerShip = MySession.PlayerShip == physObject;

                MyMaterialType materialType = isPlayerShip ? MyMaterialType.PLAYERSHIP : physObject.Physics.MaterialType;

                //material properties
                MyMaterialTypeProperties materialProperties = MyMaterialsConstants.GetMaterialProperties(materialType);

                bool isProjectileGroupKilled = false;

                if (m_sharedGroup != null)
                {
                    isProjectileGroupKilled = m_sharedGroup.Killed;
                    m_sharedGroup.Killed    = true;
                }

                if (!isProjectileGroupKilled)
                {
                    //  Play bullet hit cue
                    MyAudio.AddCue3D(m_ammoProperties.IsExplosive ? materialProperties.ExpBulletHitCue : materialProperties.BulletHitCue, intersectionValue.IntersectionPointInWorldSpace, Vector3.Zero, Vector3.Zero, Vector3.Zero);
                }

                float decalAngle = MyMwcUtils.GetRandomRadian();

                //  If we hit the glass of a miner ship, we need to create special bullet hole decals
                //  drawn from inside the cockpit and change phys object so rest of the code will think we hit the parent
                //  IMPORTANT: Intersection between projectile and glass is calculated only for mining ship in which player sits. So for enemies this will be never calculated.
                if (intersection.Value.Entity is MyCockpitGlassEntity)
                {
                    if (!isProjectileGroupKilled)
                    {
                        MyCockpitGlassDecalTexturesEnum bulletHoleDecalTexture;
                        float bulletHoleDecalSize;

                        if (MyMwcUtils.GetRandomBool(3))
                        {
                            bulletHoleDecalTexture = MyCockpitGlassDecalTexturesEnum.BulletHoleOnGlass;
                            bulletHoleDecalSize    = 0.25f;
                        }
                        else
                        {
                            bulletHoleDecalTexture = MyCockpitGlassDecalTexturesEnum.BulletHoleSmallOnGlass;
                            bulletHoleDecalSize    = 0.1f;
                        }

                        //  Place bullet hole decal on player's cockpit glass (seen from inside the ship)
                        MyCockpitGlassDecals.Add(bulletHoleDecalTexture, bulletHoleDecalSize, decalAngle, 1.0f, ref intersectionValue, false);

                        //  Create hit particles throwed into the cockpit (it's simulation of broken glass particles)
                        //  IMPORTANT: This particles will be relative to miner ship, so we create them in object space coordinates and update them by object WorldMatrix every time we draw them
                        //MyParticleEffects.CreateHitParticlesGlass(ref intersectionValue.IntersectionPointInObjectSpace, ref intersectionValue.NormalInWorldSpace, ref line.Direction, physObject.Parent);
                    }
                }

                //  If this was "mine", it must explode
                else if (physObject is MyMineBase)
                {
                    m_state = MyProjectileStateEnum.KILLED;
                    if (!IsDummy)
                    {
                        (physObject as MyAmmoBase).Explode();
                    }
                    return(true);
                }

                //  If this was missile, cannon shot, it must explode if it is not mine missile
                else if (physObject is MyAmmoBase)
                {
                    if (((MyAmmoBase)physObject).OwnerEntity == m_ignorePhysObject)
                    {
                        m_state = MyProjectileStateEnum.KILLED;
                        if (!IsDummy)
                        {
                            (physObject as MyAmmoBase).Explode();
                        }
                        return(true);
                    }
                }

                else if (this.OwnerEntity is MySmallShip && (MySmallShip)this.OwnerEntity == MySession.PlayerShip && physObject is MyStaticAsteroid && !physObject.IsDestructible)
                {
                    if (this.m_ammoProperties.IsExplosive || (this.m_ammoProperties.AmmoType == MyAmmoType.Explosive && this.m_weapon is Weapons.MyShotGun))
                    {
                        HUD.MyHud.ShowIndestructableAsteroidNotification();
                    }
                }

                else if (!isProjectileGroupKilled && !isPlayerShip)
                {
                    //  Create smoke and debris particle at the place of voxel/model hit
                    m_ammoProperties.OnHitParticles(ref intersectionValue.IntersectionPointInWorldSpace, ref intersectionValue.Triangle.InputTriangleNormal, ref line.Direction, physObject, m_weapon, OwnerEntity);

                    MySurfaceImpactEnum surfaceImpact;
                    if (intersectionValue.Entity is MyVoxelMap)
                    {
                        var voxelMap   = intersectionValue.Entity as MyVoxelMap;
                        var voxelCoord = voxelMap.GetVoxelCenterCoordinateFromMeters(ref intersectionValue.IntersectionPointInWorldSpace);
                        var material   = voxelMap.GetVoxelMaterial(ref voxelCoord);
                        if (material == MyMwcVoxelMaterialsEnum.Indestructible_01 ||
                            material == MyMwcVoxelMaterialsEnum.Indestructible_02 ||
                            material == MyMwcVoxelMaterialsEnum.Indestructible_03 ||
                            material == MyMwcVoxelMaterialsEnum.Indestructible_04 ||
                            material == MyMwcVoxelMaterialsEnum.Indestructible_05_Craters_01)
                        {
                            surfaceImpact = MySurfaceImpactEnum.INDESTRUCTIBLE;
                        }
                        else
                        {
                            surfaceImpact = MySurfaceImpactEnum.DESTRUCTIBLE;
                        }
                    }
                    else if (intersectionValue.Entity is MyStaticAsteroid)
                    {
                        surfaceImpact = MySurfaceImpactEnum.INDESTRUCTIBLE;
                    }
                    else
                    {
                        surfaceImpact = MySurfaceImpactEnum.METAL;
                    }

                    m_ammoProperties.OnHitMaterialSpecificParticles(ref intersectionValue.IntersectionPointInWorldSpace, ref intersectionValue.Triangle.InputTriangleNormal, ref line.Direction, physObject, surfaceImpact, m_weapon);
                }

                if (!(physObject is MyExplosionDebrisBase) && physObject != MySession.PlayerShip)
                {
                    //  Decal size depends on material. But for mining ship create smaller decal as original size looks to large on the ship.
                    float decalSize = MyMwcUtils.GetRandomFloat(materialProperties.BulletHoleSizeMin,
                                                                materialProperties.BulletHoleSizeMax);

                    //  Place bullet hole decal
                    float randomColor = MyMwcUtils.GetRandomFloat(0.5f, 1.0f);

                    MyDecals.Add(
                        materialProperties.BulletHoleDecal,
                        decalSize,
                        decalAngle,
                        new Vector4(randomColor, randomColor, randomColor, 1),
                        false,
                        ref intersectionValue,
                        0.0f,
                        m_ammoProperties.DecalEmissivity, MyDecalsConstants.DECAL_OFFSET_BY_NORMAL);
                }

                if (!(physObject is MyVoxelMap) && !IsDummy)
                {
                    ApplyProjectileForce(physObject, intersectionValue.IntersectionPointInWorldSpace, m_directionNormalized, isPlayerShip);
                }


                //  If this object is miner ship, then shake his head little bit
                if (physObject is MySmallShip && !IsDummy)
                {
                    MySmallShip minerShip = (MySmallShip)physObject;
                    minerShip.IncreaseHeadShake(MyHeadShakeConstants.HEAD_SHAKE_AMOUNT_AFTER_PROJECTILE_HIT);
                }



                //Handle damage

                MyEntity damagedObject = intersectionValue.Entity;

                // not a very nice way to damage actual prefab associated with the large ship weapon (if MyPrefabLargeWeapon is reworked, it might change)
                if (damagedObject is MyLargeShipBarrelBase)
                {
                    damagedObject = damagedObject.Parent;
                }
                if (damagedObject is MyLargeShipGunBase)
                {
                    MyLargeShipGunBase physObj = damagedObject as MyLargeShipGunBase;
                    if (physObj.PrefabParent != null)
                    {
                        damagedObject = physObj.PrefabParent;
                    }
                }

                //  Decrease health of stricken object
                if (!IsDummy)
                {
                    damagedObject.DoDamage(m_ammoProperties.HealthDamage, m_ammoProperties.ShipDamage, m_ammoProperties.EMPDamage, m_ammoProperties.DamageType, m_ammoProperties.AmmoType, m_ignorePhysObject);
                    if (MyMultiplayerGameplay.IsRunning)
                    {
                        var ammo = MyAmmoConstants.FindAmmo(m_ammoProperties);
                        MyMultiplayerGameplay.Static.ProjectileHit(damagedObject, intersectionValue.IntersectionPointInWorldSpace, this.m_directionNormalized, ammo, this.OwnerEntity);
                    }
                }

                if (m_trailEffect != null)
                {
                    // stop the trail effect
                    m_trailEffect.Stop();
                    m_trailEffect = null;
                }

                //  Kill this projectile (set the position to intersection point, so we draw trail polyline only up to this point)
                m_position = intersectionValue.IntersectionPointInWorldSpace;
                m_state    = MyProjectileStateEnum.KILLED;

                return(true);
            }

            return(true);
        }
Example #15
0
        //  Draw the projectile but only if desired polyline trail distance can fit in the trajectory (otherwise we will see polyline growing from the origin and it's ugly).
        //  Or draw if this is last draw of this projectile (useful for short-distance shots).
        public void Draw()
        {
            const float PROJECTILE_POLYLINE_DESIRED_LENGTH = 120;

            //var velPerFrame = m_velocity * VRage.Game.MyEngineConstants.PHYSICS_STEP_SIZE_IN_SECONDS;
            //for (int i = 0; i < 70; i += 5)
            //{
            //    Color col = new Color(255, 0, i * 5, 255);
            //    VRageRender.MyRenderProxy.DebugDrawLine3D(m_position + i * velPerFrame, m_position + i * velPerFrame + 5 * velPerFrame, col, Color.Yellow, false);
            //}

            double trajectoryLength = Vector3D.Distance(m_position, m_origin);

            if ((trajectoryLength > 0) || (m_state == MyProjectileStateEnum.KILLED))
            {
                if (m_state == MyProjectileStateEnum.KILLED)
                {
                    m_state = MyProjectileStateEnum.KILLED_AND_DRAWN;
                }

                if (!m_positionChecked)
                {
                    return;
                }

                //  If we calculate previous position using normalized direction (insted of velocity), projectile trails will
                //  look like coming from cannon, and that is desired. Even during fast movement, acceleration, rotation or changes in movement directions.
                //Vector3 previousPosition = m_position - m_directionNormalized * projectileTrailLength * 1.05f;
                Vector3D previousPosition = m_position - m_directionNormalized * PROJECTILE_POLYLINE_DESIRED_LENGTH * VRage.Game.MyEngineConstants.UPDATE_STEP_SIZE_IN_SECONDS;
                //Vector3 previousPosition = m_previousPosition;
                //Vector3 previousPosition = m_initialSunWindPosition - MyMwcUtils.Normalize(m_desiredVelocity) * projectileTrailLength;

                Vector3D direction = Vector3D.Normalize(m_position - previousPosition);

                double projectileTrailLength = LengthMultiplier * m_projectileAmmoDefinition.ProjectileTrailScale;// PROJECTILE_POLYLINE_DESIRED_LENGTH;

                projectileTrailLength *= MyParticlesManager.Paused ? 0.6f : MyUtils.GetRandomFloat(0.6f, 0.8f);

                if (trajectoryLength < projectileTrailLength)
                {
                    projectileTrailLength = trajectoryLength;
                }

                if (m_state == MyProjectileStateEnum.ACTIVE || trajectoryLength * trajectoryLength >= (m_velocity.LengthSquared() * VRage.Game.MyEngineConstants.UPDATE_STEP_SIZE_IN_SECONDS * CHECK_INTERSECTION_INTERVAL))
                {
                    previousPosition = m_position - projectileTrailLength * direction;
                }
                else
                {
                    previousPosition = m_position - ((trajectoryLength - projectileTrailLength) * MyUtils.GetRandomFloat(0, 1) + projectileTrailLength) * direction;
                }

                //float color = MyMwcUtils.GetRandomFloat(1, 2);
                float color     = MyParticlesManager.Paused ? 1 : MyUtils.GetRandomFloat(1, 2);
                float thickness = (MyParticlesManager.Paused ? 0.2f : MyUtils.GetRandomFloat(0.2f, 0.3f)) * m_projectileAmmoDefinition.ProjectileTrailScale;

                //  Line particles (polyline) don't look good in distance. Start and end aren't rounded anymore and they just
                //  look like a pieces of paper. Especially when zoom-in.
                thickness *= MathHelper.Lerp(0.2f, 0.8f, MySector.MainCamera.Zoom.GetZoomLevel());

                float alphaCone = 1;

                if (projectileTrailLength > 0)
                {
                    if (m_projectileAmmoDefinition.ProjectileTrailMaterial != null)
                    {
                        MyTransparentGeometry.AddLineBillboard(m_projectileAmmoDefinition.ProjectileTrailMaterial, new Vector4(m_projectileAmmoDefinition.ProjectileTrailColor, 1),
                                                               previousPosition, direction, (float)projectileTrailLength, thickness);
                    }
                    else
                    {
                        MyTransparentGeometry.AddLineBillboard("ProjectileTrailLine", new Vector4(m_projectileAmmoDefinition.ProjectileTrailColor * color, 1) * alphaCone,
                                                               previousPosition, direction, (float)projectileTrailLength, thickness);
                    }
                }
            }
        }
Example #16
0
        //  Draw the projectile but only if desired polyline trail distance can fit in the trajectory (otherwise we will see polyline growing from the origin and it's ugly).
        //  Or draw if this is last draw of this projectile (useful for short-distance shots).
        public void Draw()
        {
            const float PROJECTILE_POLYLINE_DESIRED_LENGTH = 120;

            float trajectoryLength = Vector3.Distance(m_position, m_origin);

            if ((trajectoryLength > 0) || (m_state == MyProjectileStateEnum.KILLED))
            {
                if (m_state == MyProjectileStateEnum.KILLED)
                {
                    m_state = MyProjectileStateEnum.KILLED_AND_DRAWN;
                }

                if (!m_positionChecked)
                {
                    return;
                }



                //  If we calculate previous position using normalized direction (insted of velocity), projectile trails will
                //  look like coming from cannon, and that is desired. Even during fast movement, acceleration, rotation or changes in movement directions.
                //Vector3 previousPosition = m_position - m_directionNormalized * projectileTrailLength * 1.05f;
                Vector3 previousPosition = m_position - m_directionNormalized * PROJECTILE_POLYLINE_DESIRED_LENGTH * MyConstants.PHYSICS_STEP_SIZE_IN_SECONDS;
                //Vector3 previousPosition = m_previousPosition;
                //Vector3 previousPosition = m_initialSunWindPosition - MyMwcUtils.Normalize(m_desiredVelocity) * projectileTrailLength;

                Vector3 direction = Vector3.Normalize(m_position - previousPosition);

                float projectileTrailLength = 40 * LengthMultiplier;// PROJECTILE_POLYLINE_DESIRED_LENGTH;

                projectileTrailLength *= MyMwcUtils.GetRandomFloat(0.6f, 0.8f);

                if (trajectoryLength < projectileTrailLength)
                {
                    projectileTrailLength = trajectoryLength;
                }

                previousPosition = m_position - projectileTrailLength * direction;
                if (m_externalAddition >= 1.0f)
                {
                    m_externalAddition = 0.5f;
                }


                //float color = MyMwcUtils.GetRandomFloat(1, 2);
                float color     = MyMwcUtils.GetRandomFloat(1, 2);
                float thickness = m_thicknessMultiplier * MyMwcUtils.GetRandomFloat(0.2f, 0.3f);

                //  Line particles (polyline) don't look good in distance. Start and end aren't rounded anymore and they just
                //  look like a pieces of paper. Especially when zoom-in.
                thickness *= MathHelper.Lerp(0.2f, 0.8f, MyCamera.Zoom.GetZoomLevel());

                float alphaCone  = 1;
                float alphaGlare = 1;

                if (BlendByCameraDirection)
                {
                    float angle = 1 - Math.Abs(Vector3.Dot(MyMwcUtils.Normalize(MyCamera.ForwardVector), direction));
                    alphaGlare = (float)Math.Pow(1 - angle, 2);
                    alphaCone  = (1 - (float)Math.Pow(1 - angle, 30));
                }

                MyTransparentGeometry.AddLineBillboard(MyTransparentMaterialEnum.ProjectileTrailLine, new Vector4(m_ammoProperties.TrailColor * color, 1) * alphaCone,
                                                       previousPosition, direction, projectileTrailLength, thickness);

                if (FrontBillboardMaterial.HasValue)
                {
                    MyTransparentGeometry.AddPointBillboard(FrontBillboardMaterial.Value, new Vector4(m_ammoProperties.TrailColor * color, 1) * alphaGlare, m_position, 0.8f * FrontBillboardSize, 0);
                }
            }
        }
        //  Update position, check collisions, etc.
        //  Return false if projectile dies/timeouts in this tick.
        public bool Update()
        {
            //  Projectile was killed , but still not last time drawn, so we don't need to do update (we are waiting for last draw)
            if (m_state == MyProjectileStateEnum.KILLED)
            {
                return(true);
            }
            //  Projectile was killed and last time drawn, so we can finally remove it from buffer
            if (m_state == MyProjectileStateEnum.KILLED_AND_DRAWN)
            {
                StopEffect();
                return(false);
            }

            Vector3D position = m_position;

            m_position += m_velocity * VRage.Game.MyEngineConstants.UPDATE_STEP_SIZE_IN_SECONDS;

            //  Distance timeout
            Vector3 positionDelta = m_position - m_origin;

            if (Vector3.Dot(positionDelta, positionDelta) >= m_maxTrajectory * m_maxTrajectory)
            {
                StopEffect();
                m_state = MyProjectileStateEnum.KILLED;
                return(true);
            }

            m_checkIntersectionIndex = ++m_checkIntersectionIndex % CHECK_INTERSECTION_INTERVAL;
            if (m_checkIntersectionIndex != 0 && m_positionChecked) //check only each n-th intersection
            {
                return(true);
            }

            //  Calculate hit point, create decal and throw debris particles
            Vector3D lineEndPosition = position + CHECK_INTERSECTION_INTERVAL * (m_velocity * VRage.Game.MyEngineConstants.UPDATE_STEP_SIZE_IN_SECONDS);

            LineD line = new LineD(m_positionChecked ? position : m_origin, lineEndPosition);

            m_positionChecked = true;

            IMyEntity entity;
            Vector3D  hitPosition;
            Vector3   hitNormal;
            bool      headShot;

            GetHitEntityAndPosition(line, out entity, out hitPosition, out hitNormal, out headShot);
            if (entity == null || entity == m_ignoreEntity || entity.Physics == null)
            {
                return(true);
            }
            if ((m_ignoreEntity is IMyGunBaseUser) && (m_ignoreEntity as IMyGunBaseUser).Owner is MyCharacter &&
                (m_ignoreEntity as IMyGunBaseUser).Owner == entity)
            {
                return(true); // prevent player shooting himself
            }

            ProfilerShort.Begin("Projectile.Update");

            {
                MyCharacter hitCharacter = entity as MyCharacter;
                if (hitCharacter != null)
                {
                    IStoppableAttackingTool stoppableTool = hitCharacter.CurrentWeapon as IStoppableAttackingTool;
                    if (stoppableTool != null)
                    {
                        stoppableTool.StopShooting(OwnerEntity);
                    }
                }
            }

            m_position = hitPosition;

            bool isProjectileGroupKilled = false;

            if (!isProjectileGroupKilled)
            {
                MySurfaceImpactEnum surfaceImpact;
                MyStringHash        materialType;
                GetSurfaceAndMaterial(entity, ref hitPosition, out surfaceImpact, out materialType);

                PlayHitSound(materialType, entity, hitPosition);
                DoDamage(headShot ? m_projectileAmmoDefinition.ProjectileHeadShotDamage : m_projectileAmmoDefinition.ProjectileMassDamage, hitPosition, entity);
                //  Create smoke and debris particle at the place of voxel/model hit
                if (surfaceImpact != MySurfaceImpactEnum.CHARACTER)
                {
                    m_projectileAmmoDefinition.ProjectileOnHitParticles(ref hitPosition, ref hitNormal, ref line.Direction, entity, m_weapon, 1, OwnerEntity);
                }

                if (surfaceImpact == MySurfaceImpactEnum.CHARACTER && entity is MyCharacter)
                {
                    MyStringHash bullet = MyStringHash.GetOrCompute("RifleBullet");//temporary
                    MyMaterialPropertiesHelper.Static.TryCreateCollisionEffect(
                        MyMaterialPropertiesHelper.CollisionType.Start,
                        hitPosition,
                        hitNormal,
                        bullet, materialType);
                }

                Vector3D particleHitPosition = hitPosition + line.Direction * -0.2;
                m_projectileAmmoDefinition.ProjectileOnHitMaterialParticles(ref particleHitPosition, ref hitNormal, ref line.Direction, entity, surfaceImpact, m_weapon, 1);

                CreateDecal(materialType);

                if (m_weapon == null || (entity.GetTopMostParent() != m_weapon.GetTopMostParent()))
                {
                    ApplyProjectileForce(entity, hitPosition, m_directionNormalized, false, m_projectileAmmoDefinition.ProjectileHitImpulse * m_impulseMultiplier);
                }

                StopEffect();
                m_state = MyProjectileStateEnum.KILLED;
            }
            ProfilerShort.End();
            return(true);
        }