/* Test for collision between player and blocks, and adjust player's position and velocity accordingly. * There's some tricky order of operations here that is explained inline*/ public static void CollisionCheck(Room r, Player p, int gameTime) { // Variables used for entire collision test method. Physics.BlockUnfold(r, p.center.normal, p.center.direction); List<Vector3> playerVertexList = new List<Vector3>(); List<Vector3> playerGroundBox = new List<Vector3>(); List<Vector3> playerLeftBox = new List<Vector3>(); List<Vector3> playerRightBox = new List<Vector3>(); Vector3 up = p.center.direction; Vector3 right = Vector3.Cross(up, p.center.normal); /* Stores the result of friction calculations, to be applied at the END of the function. Must be done at the end * because during the algorithm, it isn't fully determined what the player's state is, and friction is only applied * in certain cases. Specifically, it isn't known if the player has landed on the ground until the algorithm completes. */ Vector3 frictionAdjustment = Vector3.Zero; /* Perform the basic algorithm several times, each time taking only the most powerful contribution. I'm assuming for now * that it is EXTREMELY unlikely for the player to have meaningful collisions simultaneously with > 3 blocks. * The purpose of this is that sometimes two collisions will be detected, but only one needs to be resolved. For example, * if two blocks are adjacent to one another such that their edges form a single, straight edge, we only need (and want) * to use the projection from one of them*/ #region player-block collision if(p.state != State.Phase && p.state != State.PhaseFail && p.state != State.Tunnel && p.state != State.Jump && p.state != State.Death) { for (int attempt = 0; attempt < 2; attempt++) { playerVertexList = p.GetCollisionRect(); List<Vector3> projectionList = new List<Vector3>(); List<Vector3> relVelList = new List<Vector3>(); List<EdgeProperties> edgePropertiesList = new List<EdgeProperties>(); foreach (Block baseBlock in r.blocks) { foreach (Block b in baseBlock.unfoldedBlocks) { if (p.CollisionFirstPass(b)) continue; List<Vector3> blockVertexList = b.GetCollisionRect(); Vector3 projection = Collide(playerVertexList, blockVertexList, p.center.normal); // If a collision is found, save the necessary data and continue. if (projection.Length() > 0f) { projectionList.Add(projection); relVelList.Add(b.GetVelocity()); EdgeProperties eTemp = b.GetProperties(projection); edgePropertiesList.Add(b.GetProperties(projection)); } } } foreach (Doodad d in r.doodads) { if (p.CollisionFirstPass(d)) continue; if (d.type == VL.DoodadType.BridgeGate) { List<Vector3> doodadVertexList = d.GetCollisionRect(); Vector3 projection = Collide(playerVertexList, doodadVertexList, p.center.normal); d.ActivateDoodad(r, projection.Length() > 0f); } if (d.hasCollisionRect && d.type != VL.DoodadType.PowerPlug) { List<Vector3> doodadVertexList = d.GetCollisionRect(); Vector3 projection = Collide(playerVertexList, doodadVertexList, p.center.normal); if (projection.Length() > 0f) { projectionList.Add(projection); relVelList.Add(d.position.velocity); edgePropertiesList.Add(new EdgeProperties()); } } } foreach (Monster m in r.monsters) { if (p.CollisionFirstPass(m)) continue; if (m.dead == true) continue; if (m.moveType == VL.MovementType.SnakeBoss) { List<Vector3> monsterVertexList = m.GetCollisionRect(); Vector3 projection = Collide(playerVertexList, monsterVertexList, Engine.player.center.normal); if (projection.Length() > 0f) { projectionList.Add(projection); relVelList.Add(m.position.velocity); edgePropertiesList.Add(new EdgeProperties()); } } } // Compute the most powerful collision and resolve it. CollisionResult result = ResolveCollision(projectionList, relVelList, edgePropertiesList, p.center.velocity, p.HasTraction()); if (result.properties != null && result.properties.type == VL.EdgeType.Ice && Vector3.Dot(result.projection, p.center.direction) > 0) p.sliding = true; else p.sliding = false; p.center.velocity += result.velocityAdjustment; frictionAdjustment += result.frictionAdjustment; if(result.projection != Vector3.Zero) { p.center.position += result.projection; if (result.properties.type == VL.EdgeType.Spikes || (result.properties.type == VL.EdgeType.Electric && result.properties.primaryValue > 0) || (result.properties.type == VL.EdgeType.Fire && result.properties.primaryValue > 0)) { p.Damage(result.projection, false); } if (result.projection.Length() > p.playerHalfWidth) { if (p.boosting == true) p.boosting = false; else p.Damage(result.projection,true); } } else break; } List<Vector3> pVertexList = p.GetCollisionRect(); foreach (Projectile s in r.projectiles) { if (s.active == true && s.exploding == false && (s.type != ProjectileType.EyeLaser || Math.Abs(s.depth - p.depth) < .5f)) { if (s.srcMonster == null && (s.type == ProjectileType.Missile || s.type == ProjectileType.Bomb)) continue; s.UpdateBoundingBox(p.center.direction, Vector3.Cross(p.center.direction, p.center.normal)); if (p.CollisionFirstPass(s) == false) { List<Vector3> projectileVertexList = s.GetCollisionRect(); Vector3 projection = Collide(pVertexList, projectileVertexList, Engine.player.center.normal); if (projection != Vector3.Zero) { p.Damage(projection,false); s.Detonate(); s.position.velocity = Vector3.Zero; } } } } foreach (Monster m in r.monsters) { if (m.dead == true) continue; m.UpdateBoundingBox(p.center.direction, Vector3.Cross(p.center.direction, p.center.normal)); if (p.CollisionFirstPass(m) == false) { if (m.moveType == VL.MovementType.ChaseBoss) { List<Vector3> monsterVertexList = m.GetCollisionRect(); Vector3 projection = Collide(playerVertexList, monsterVertexList, Engine.player.center.normal); if (projection.Length() > 0f) { if (m.position.velocity != Vector3.Zero) p.Damage(m.position.velocity,false); else p.Damage(projection,false); } } else if (m.moveType != VL.MovementType.SnakeBoss) { Vector3 distance = (p.center.position - m.unfoldedPosition.position); if (distance.Length() < (p.playerHalfHeight + m.halfHeight)) { Vector3 projection = Vector3.Normalize(distance) * ((p.playerHalfHeight + m.halfHeight) - distance.Length()); p.Damage(projection, false); } } } } foreach (Doodad d in r.doodads) { if (d.type == VL.DoodadType.Beam) { d.UpdateBoundingBox(p.center.direction, Vector3.Cross(p.center.direction, p.center.normal)); if (p.CollisionFirstPass(d) == false) { List<Vector3> doodadVertexList = d.GetCollisionRect(); Vector3 projection = Collide(pVertexList, doodadVertexList, Engine.player.center.normal); if (projection != Vector3.Zero) { p.Damage(projection,false); } } } } // Now that player position is stabilized, use the special rects to detect if it is grounded // or prepped for a wall jump. #region special-blocks for ground and walljump detection playerGroundBox.Add(p.center.position + p.playerHalfWidth * right); playerGroundBox.Add(p.center.position - p.playerHalfWidth * right); playerGroundBox.Add(p.center.position - (p.playerHalfHeight + .1f) * up - p.playerHalfWidth * right); playerGroundBox.Add(p.center.position - (p.playerHalfHeight + .1f) * up + p.playerHalfWidth * right); playerLeftBox.Add(p.center.position); playerLeftBox.Add(p.center.position - (p.playerHalfWidth + .1f) * right); playerLeftBox.Add(p.center.position - (p.playerHalfHeight - .1f) * up - (p.playerHalfWidth + .1f) * right); playerLeftBox.Add(p.center.position - (p.playerHalfHeight - .1f) * up); playerRightBox.Add(p.center.position + (p.playerHalfWidth + .1f) * right); playerRightBox.Add(p.center.position); playerRightBox.Add(p.center.position - (p.playerHalfHeight - .1f) * up); playerRightBox.Add(p.center.position - (p.playerHalfHeight - .1f) * up + (p.playerHalfWidth + .1f) * right); p.leftWall = false; p.rightWall = false; //p.platformVelocity = Vector3.Zero; foreach (Block baseBlock in r.blocks) { foreach (Block b in baseBlock.unfoldedBlocks) { if (p.boundingBoxBottom > b.boundingBoxTop + 1 || p.boundingBoxTop < b.boundingBoxBottom - 1 || p.boundingBoxLeft > b.boundingBoxRight + 1 || p.boundingBoxRight < b.boundingBoxLeft - 1) continue; // if block intesects with rectVertexList List<Vector3> blockVertexList = b.GetCollisionRect(); Vector3 groundProjection = Collide(playerGroundBox, blockVertexList, p.center.normal); Vector3 leftProjection = Collide(playerLeftBox, blockVertexList, p.center.normal); Vector3 rightProjection = Collide(playerRightBox, blockVertexList, p.center.normal); //if (Vector3.Dot(groundProjection, up) > 0) //if (groundProjection != Vector3.Zero) if (groundProjection.Length() > .001f) { p.grounded = true; EdgeProperties properties = b.GetProperties(groundProjection); p.platformVelocity = b.GetVelocity(); if (properties.type == VL.EdgeType.ConveyorBelt) { Vector3 lateralDirection = Vector3.Cross(groundProjection, p.center.normal); lateralDirection.Normalize(); p.platformVelocity += .001f * properties.primaryValue * lateralDirection; } } if (Vector3.Dot(leftProjection, right) > 0) { p.leftWall = true; EdgeProperties properties = b.GetProperties(leftProjection); p.platformVelocity = b.GetVelocity(); if (properties.type == VL.EdgeType.Magnet) { p.Spin(leftProjection / leftProjection.Length()); } } if (Vector3.Dot(rightProjection, -right) != 0) { p.rightWall = true; EdgeProperties properties = b.GetProperties(rightProjection); p.platformVelocity = b.GetVelocity(); if (properties.type == VL.EdgeType.Magnet) { p.Spin(rightProjection / rightProjection.Length()); } } } } foreach (Doodad b in r.doodads) { if (b.hasCollisionRect && b.type != VL.DoodadType.PowerPlug) { b.UpdateBoundingBox(p.center.direction, p.right); if (p.boundingBoxBottom > b.boundingBoxTop +2f || p.boundingBoxTop < b.boundingBoxBottom-2f || p.boundingBoxLeft > b.boundingBoxRight+2f || p.boundingBoxRight < b.boundingBoxLeft-2f) continue; // if block intesects with rectVertexList List<Vector3> brickVertexList = b.GetCollisionRect(); Vector3 groundProjection = Collide(playerGroundBox, brickVertexList, p.center.normal); Vector3 leftProjection = Collide(playerLeftBox, brickVertexList, p.center.normal); Vector3 rightProjection = Collide(playerRightBox, brickVertexList, p.center.normal); if (Vector3.Dot(groundProjection, up) > 0) { p.grounded = true; p.platformVelocity = b.position.velocity; } if (Vector3.Dot(leftProjection, right) > 0) { p.leftWall = true; p.platformVelocity = b.position.velocity; } if (Vector3.Dot(rightProjection, -right) != 0) { p.rightWall = true; p.platformVelocity = b.position.velocity; } } } foreach (Monster m in r.monsters) { if (p.CollisionFirstPass(m)) continue; if (m.dead == true) continue; if (m.moveType == VL.MovementType.SnakeBoss || m.moveType == VL.MovementType.ChaseBoss) { m.UpdateBoundingBox(p.center.direction, p.right); if (p.boundingBoxBottom > m.boundingBoxTop + 2f || p.boundingBoxTop < m.boundingBoxBottom - 2f || p.boundingBoxLeft > m.boundingBoxRight + 2f || p.boundingBoxRight < m.boundingBoxLeft - 2f) continue; // if block intesects with rectVertexList List<Vector3> brickVertexList = m.GetCollisionRect(); Vector3 groundProjection = Collide(playerGroundBox, brickVertexList, p.center.normal); Vector3 leftProjection = Collide(playerLeftBox, brickVertexList, p.center.normal); Vector3 rightProjection = Collide(playerRightBox, brickVertexList, p.center.normal); if (Vector3.Dot(groundProjection, up) > 0) { p.grounded = true; p.platformVelocity = m.position.velocity; } if (Vector3.Dot(leftProjection, right) > 0) { p.leftWall = true; p.platformVelocity = m.position.velocity; } if (Vector3.Dot(rightProjection, -right) != 0) { p.rightWall = true; p.platformVelocity = m.position.velocity; } } } #endregion // Now that we know if the player is grounded or not, we can use the walking property to determine whether or // not we should apply friction. if (!p.walking && !p.boosting) p.center.velocity += frictionAdjustment; } #endregion #region doodad-activation // Test and activate doodads. foreach (Doodad d in r.doodads) { if (d.type == VL.DoodadType.DialogPoint) { if (d.active == false && d.ActivationRange(p)) { DialogBox.SetDialog(d.targetObject); d.active = true; } } if (d.type == VL.DoodadType.TriggerPoint) { if (d.active == false && d.id.Contains("Dialog") && d.ActivationRange(p)) { DialogBox.SetDialog(d.targetObject); d.active = true; } if (d.id.Contains("Rock2Trigger") && d.ActivationRange(p)) RockBoss.triggered = true; } if (d.type == VL.DoodadType.Checkpoint) { d.ActivateDoodad(r, d == p.respawnPoint); } if (d.type == VL.DoodadType.Vortex || d.type == VL.DoodadType.JumpPad || d.type == VL.DoodadType.ItemBlock || d.isStation) { if(Engine.player.state != State.Upgrade && Engine.player.state != State.Save) d.ActivateDoodad(r, d.ActivationRange(p)); } if (d.type == VL.DoodadType.Holoprojector) { d.ActivateDoodad(r, d.ActivationRange(p)); } if( (d.position.position - p.center.position).Length() < 3f*d.triggerDistance) { if (d.isOrb && d.active) { d.tracking = true; } } if ((d.position.position - p.center.position).Length() < d.triggerDistance) { if (d.type == VL.DoodadType.PowerOrb) { if(d.active == true) { d.ActivateDoodad(r, false); d.position.direction = Vector3.Zero; d.position.normal = Vector3.Zero; d.position.position = Vector3.Zero; d.refreshVertexData = true; foreach (Doodad powerup in d.currentRoom.doodads) powerup.RefreshPowerLevels(); r.currentOrbs++; r.parentSector.currentOrbs++; r.refreshVertices = true; p.orbsCollected++; SoundFX.CollectOrb(); } } if (d.type == VL.DoodadType.RedCube) { if (d.active == true) { d.ActivateDoodad(r, false); r.currentRedOrbs++; r.parentSector.currentRedOrbs++; r.refreshVertices = true; p.redOrbsCollected++; if (p.redOrbsCollected % 5 == 0) { p.naturalShield.maxAmmo = p.naturalShield.maxAmmo + 1; p.naturalShield.ammo = p.naturalShield.maxAmmo; } SoundFX.CollectOrb(); } } if (d.type == VL.DoodadType.BlueCube) { if (d.active == true) { d.ActivateDoodad(r, false); r.currentBlueOrbs++; r.parentSector.currentBlueOrbs++; r.refreshVertices = true; SoundFX.CollectOrb(); } } if (d.type == VL.DoodadType.Checkpoint) { if (p.respawnPoint != d) { LevelLoader.QuickSave(); p.respawnPoint = d; p.respawnPlayer = new Player(); p.respawnPlayer.currentRoom = p.currentRoom; p.respawnPlayer.center = new Vertex(d.position.position, p.center.normal, Vector3.Zero, p.center.direction); } } if(d.type == VL.DoodadType.WallSwitch) { d.Activate(); } } } #endregion #region doodadCollisionDetection foreach(Doodad d in r.doodads) { if (d.freeMotion == true) { frictionAdjustment = Vector3.Zero; for (int attempt = 0; attempt < 2; attempt++) { List<Vector3> projectionList = new List<Vector3>(); List<Vector3> relVelList = new List<Vector3>(); List<EdgeProperties> edgePropertiesList = new List<EdgeProperties>(); List<Vector3> doodadVertexList = d.GetCollisionRect(); d.UpdateBoundingBox(p.center.direction, Vector3.Cross(p.center.direction, p.center.normal)); foreach (Block baseBlock in r.blocks) { foreach (Block b in baseBlock.unfoldedBlocks) { if (d.CollisionFirstPass(b)) continue; List<Vector3> blockVertexList = b.GetCollisionRect(); Vector3 projection = Collide(doodadVertexList, blockVertexList, p.center.normal); if (projection.Length() > 0f) { projectionList.Add(projection); relVelList.Add(b.GetVelocity()); edgePropertiesList.Add(b.GetProperties(projection)); } } } foreach (Doodad b in r.doodads) { b.UpdateBoundingBox(p.center.direction, Vector3.Cross(p.center.direction, p.center.normal)); if (b.hasCollisionRect && b != d) { if (d.CollisionFirstPass(b)) continue; List<Vector3> brickVertexList = b.GetCollisionRect(); Vector3 projection = Collide(doodadVertexList, brickVertexList, p.center.normal); if (projection.Length() > 0f) { projectionList.Add(projection); relVelList.Add(b.position.velocity); edgePropertiesList.Add(new EdgeProperties()); } } } CollisionResult result = ResolveCollision(projectionList, relVelList, edgePropertiesList, d.unfoldedPosition.velocity, false); if(result.projection != Vector3.Zero) { d.AdjustVertex(result.projection, result.velocityAdjustment + result.frictionAdjustment, p.center.normal, p.center.direction); d.position.Update(p.currentRoom, 0); d.unfoldedPosition.position += result.projection; d.unfoldedPosition.velocity += result.velocityAdjustment; d.unfoldedPosition.velocity += result.frictionAdjustment; } else break; } } } foreach (Doodad d in r.doodads) { if (d.freeMotion) { foreach (Doodad b in r.doodads) { if ((d.position.position - b.position.position).Length() < b.triggerDistance) { if (b.type == VL.DoodadType.WallSwitch) { b.Activate(); } if (b.type == VL.DoodadType.PlugSlot && d.type == VL.DoodadType.PowerPlug) { d.position.position = b.position.position; if (d.active == false) { d.SetTargetBehavior(); } d.active = true; } } } } } #endregion #region monsters bool smashRockBarrier = false; foreach (Monster m in r.monsters) { if (m.moveType == VL.MovementType.FaceBoss) continue; if (m.dead == true) continue; frictionAdjustment = Vector3.Zero; for (int attempt = 0; attempt < 2; attempt++) { List<Vector3> projectionList = new List<Vector3>(); List<Vector3> relVelList = new List<Vector3>(); List<EdgeProperties> edgePropertiesList = new List<EdgeProperties>(); List<Vector3> monsterVertexList = m.GetCollisionRect(); m.UpdateBoundingBox(p.center.direction, Vector3.Cross(p.center.direction, p.center.normal)); foreach (Block baseBlock in r.blocks) { foreach (Block b in baseBlock.unfoldedBlocks) { if (m.CollisionFirstPass(b)) continue; List<Vector3> blockVertexList = b.GetCollisionRect(); Vector3 projection = Collide(monsterVertexList, blockVertexList, p.center.normal); if (projection.Length() > 0f) { projectionList.Add(projection); relVelList.Add(b.GetVelocity()); edgePropertiesList.Add(b.GetProperties(projection)); } } } foreach (Doodad b in r.doodads) { b.UpdateBoundingBox(p.center.direction, Vector3.Cross(p.center.direction, p.center.normal)); if (b.hasCollisionRect && b.type != VL.DoodadType.PowerPlug) { if (m.CollisionFirstPass(b)) continue; if (m.moveType == VL.MovementType.RockBoss && b.id.Contains("RockBarrier")) { smashRockBarrier = true; } if (m.moveType == VL.MovementType.ChaseBoss && b.id.Contains("ChaseBarrier")) { smashRockBarrier = true; } if (m.moveType == VL.MovementType.ChaseBoss && b.id.Contains("ChaseStop")) { ChaseBoss.studder = true; } if (m.moveType == VL.MovementType.ChaseBoss && b.type == VL.DoodadType.Brick) { b.ActivateDoodad(r, true); continue; } List<Vector3> brickVertexList = b.GetCollisionRect(); Vector3 projection = Collide(monsterVertexList, brickVertexList, p.center.normal); if (projection.Length() > 0f) { if (m.moveType == VL.MovementType.RockBoss && b.type == VL.DoodadType.Crate) { if (m.rockBoss.rockHits > 0) { m.impactVector = projection; m.lastHitType = ProjectileType.Impact; } continue; } projectionList.Add(projection); relVelList.Add(b.position.velocity); edgePropertiesList.Add(new EdgeProperties()); } } } CollisionResult result = ResolveCollision(projectionList, relVelList, edgePropertiesList, m.unfoldedPosition.velocity, false); if (result.projection != Vector3.Zero) { m.AdjustVertex(result.projection, result.velocityAdjustment, p.center.normal, p.center.direction); m.position.Update(p.currentRoom, 0); m.unfoldedPosition.position += result.projection; m.unfoldedPosition.velocity += result.velocityAdjustment; if (result.properties.type == VL.EdgeType.Spikes) { m.impactVector = Monster.AdjustVector(result.projection, m.position.normal, Engine.player.center.normal, Engine.player.center.direction, true); m.lastHitType = ProjectileType.Spikes; } if (result.projection.Length() > m.halfWidth) { m.impactVector = Monster.AdjustVector(result.projection, m.position.normal, Engine.player.center.normal, Engine.player.center.direction, true); m.lastHitType = ProjectileType.Spikes; } if (Vector3.Dot(m.position.direction, Monster.AdjustVector(result.projection, m.position.normal, p.center.normal, p.center.direction, true)) <= 0) { m.AdjustVertex(Vector3.Zero, result.frictionAdjustment, p.center.normal, p.center.direction); m.unfoldedPosition.velocity += result.frictionAdjustment; } } else break; } List<Vector3> mVertexList = m.GetCollisionRect(); m.UpdateBoundingBox(p.center.direction, Vector3.Cross(p.center.direction, p.center.normal)); foreach (Monster m2 in r.monsters) { if (m == m2 || m2.dead == true) continue; if (m.moveType == VL.MovementType.SnakeBoss && m2.moveType == VL.MovementType.SnakeBoss) continue; m2.UpdateBoundingBox(p.center.direction, Vector3.Cross(p.center.direction, p.center.normal)); if (m.CollisionFirstPass(m2)) continue; List<Vector3> brickVertexList = m2.GetCollisionRect(); Vector3 projection = Collide(mVertexList, brickVertexList, p.center.normal); if (projection.Length() > 0f) { m.AdjustVertex(.3f*projection, Vector3.Zero, p.center.normal, p.center.direction); m.position.Update(p.currentRoom, 0); m.unfoldedPosition.position += .3f * projection; m2.AdjustVertex(-.3f * projection, Vector3.Zero, p.center.normal, p.center.direction); m2.position.Update(p.currentRoom, 0); m2.unfoldedPosition.position -= .3f * projection; } } foreach (Projectile s in r.projectiles) { if (s.type == ProjectileType.EyeLaser) continue; if (s.type == ProjectileType.Missile && s.srcMonster == null && (s.position.position - m.position.position).Length() < 7f) { s.SetTarget(m.position.position); } //if (s.active == true || m != s.srcMonster) if (s.srcMonster != null && s.srcMonster.moveType == VL.MovementType.SnakeBoss && m.moveType == VL.MovementType.SnakeBoss) continue; if (m != s.srcMonster) { s.UpdateBoundingBox(p.center.direction, Vector3.Cross(p.center.direction, p.center.normal)); if (m.CollisionFirstPass(s) == false) { List<Vector3> projectileVertexList = s.GetCollisionRect(); Vector3 projection = Collide(mVertexList, projectileVertexList, Engine.player.center.normal); if (projection != Vector3.Zero) { s.Detonate(); m.impactVector = Monster.AdjustVector(s.position.velocity, m.position.normal, Engine.player.center.normal, Engine.player.center.direction, true); m.lastHitType = s.type; s.position.velocity = Vector3.Zero; } } } } List<Vector3> monsterGroundBox = m.GetGroundCollisionRect(); List<Vector3> monsterForwardGroundBox = m.GetForwardGroundCollisionRect(); List<Vector3> monsterForwardBox = m.GetForwardCollisionRect(); m.groundProjection = Vector3.Zero; m.forwardGroundProjection = Vector3.Zero; m.forwardProjection = Vector3.Zero; foreach (Block baseBlock in r.blocks) { foreach (Block b in baseBlock.unfoldedBlocks) { if (m.boundingBoxBottom > b.boundingBoxTop + 2 || m.boundingBoxTop < b.boundingBoxBottom - 2 || m.boundingBoxLeft > b.boundingBoxRight + 2 || m.boundingBoxRight < b.boundingBoxLeft - 2) continue; // if block intesects with rectVertexList List<Vector3> blockVertexList = b.GetCollisionRect(); Vector3 groundProjection = Collide(monsterGroundBox, blockVertexList, p.center.normal); Vector3 forwardGroundProjection = Collide(monsterForwardGroundBox, blockVertexList, p.center.normal); Vector3 forwardProjection = Collide(monsterForwardBox, blockVertexList, p.center.normal); if (forwardGroundProjection != Vector3.Zero) m.forwardGroundProjection = Monster.AdjustVector(forwardGroundProjection, m.position.normal, p.center.normal, p.center.direction, true); if (forwardProjection != Vector3.Zero) m.forwardProjection = Monster.AdjustVector(forwardProjection, m.position.normal, p.center.normal, p.center.direction, true); if (groundProjection != Vector3.Zero) m.groundProjection = Monster.AdjustVector(groundProjection, m.position.normal, p.center.normal, p.center.direction, true); } } foreach (Monster m2 in r.monsters) { if (m == m2 || m2.dead == true) continue; if (m.boundingBoxBottom > m2.boundingBoxTop + 2 || m.boundingBoxTop < m2.boundingBoxBottom - 2 || m.boundingBoxLeft > m2.boundingBoxRight + 2 || m.boundingBoxRight < m2.boundingBoxLeft - 2) continue; // if block intesects with rectVertexList List<Vector3> blockVertexList = m2.GetCollisionRect(); Vector3 forwardProjection = Collide(monsterForwardBox, blockVertexList, p.center.normal); if (forwardProjection != Vector3.Zero) { m.forwardProjection = Monster.AdjustVector(forwardProjection, m.position.normal, p.center.normal, p.center.direction, true); } } } if (smashRockBarrier == true) { foreach (Doodad d in r.doodads) { if (d.id.Contains("RockBarrier") || d.id.Contains("ChaseBarrier")) { d.ActivateDoodad(r, true); } } } #endregion #region projectile-collisions foreach (Projectile s in r.projectiles) { if (s.type == ProjectileType.EyeLaser) continue; frictionAdjustment = Vector3.Zero; List<Vector3> projectionList = new List<Vector3>(); List<Vector3> relVelList = new List<Vector3>(); List<EdgeProperties> edgePropertiesList = new List<EdgeProperties>(); List<Vector3> doodadVertexList = s.GetCollisionRect(); s.UpdateBoundingBox(p.center.direction, Vector3.Cross(p.center.direction, p.center.normal)); foreach (Block baseBlock in r.blocks) { foreach (Block b in baseBlock.unfoldedBlocks) { if (s.CollisionFirstPass(b)) continue; List<Vector3> blockVertexList = b.GetCollisionRect(); Vector3 projection = Collide(doodadVertexList, blockVertexList, p.center.normal); if (projection != Vector3.Zero) { s.Detonate(); s.position.velocity = Vector3.Zero; } } } foreach (Doodad b in r.doodads) { b.UpdateBoundingBox(p.center.direction, Vector3.Cross(p.center.direction, p.center.normal)); if ((b.hasCollisionRect && b.type != VL.DoodadType.PowerPlug) || (b.type == VL.DoodadType.LaserSwitch && s.type == ProjectileType.Laser && b.active == false)) { if (s.CollisionFirstPass(b)) continue; List<Vector3> brickVertexList = b.GetCollisionRect(); Vector3 projection = Collide(doodadVertexList, brickVertexList, p.center.normal); if (projection != Vector3.Zero) { s.Detonate(); s.position.velocity = Vector3.Zero; if (b.type == VL.DoodadType.LaserSwitch && s.type == ProjectileType.Laser) { b.ActivateDoodad(r, true); b.SetTargetBehavior(); } if ((s.type == ProjectileType.Bomb || s.type == ProjectileType.Missile) && s.exploding == true && b.type == VL.DoodadType.Brick) { b.ActivateDoodad(r, true); } } } } } #endregion p.center.Update(r, 0); }