private bool determineShouldBuildFloor(Voxel v, Voxel.State floorState) { bool result = false; if (floorState == Voxel.States.Blue || floorState == Voxel.States.Powered) { // If we're standing on blue or powered, we need to check if we're close to a non-blue block before we can build a floor // This prevents the player from building a floor infinitely if (VoxelAStar.BroadphaseSearch(this.floorMap, this.floorCoordinate, 6, x => x.Type != Voxel.States.Blue && x.Type != Voxel.States.Powered) != null) { result = true; } } else if (v == null || v.Entity.Type != "Bouncer") { result = true; } return(result); }
public override void Bind(Entity entity, Main main, bool creating = false) { if (ParticleSystem.Get(main, "SnakeSparks") == null) { ParticleSystem.Add(main, "SnakeSparks", new ParticleSystem.ParticleSettings { TextureName = "Particles\\splash", MaxParticles = 1000, Duration = TimeSpan.FromSeconds(1.0f), MinHorizontalVelocity = -7.0f, MaxHorizontalVelocity = 7.0f, MinVerticalVelocity = 0.0f, MaxVerticalVelocity = 7.0f, Gravity = new Vector3(0.0f, -10.0f, 0.0f), MinRotateSpeed = -2.0f, MaxRotateSpeed = 2.0f, MinStartSize = 0.3f, MaxStartSize = 0.7f, MinEndSize = 0.0f, MaxEndSize = 0.0f, BlendState = Microsoft.Xna.Framework.Graphics.BlendState.AlphaBlend, MinColor = new Vector4(2.0f, 2.0f, 2.0f, 1.0f), MaxColor = new Vector4(2.0f, 2.0f, 2.0f, 1.0f), }); ParticleSystem.Add(main, "SnakeSparksRed", new ParticleSystem.ParticleSettings { TextureName = "Particles\\splash", MaxParticles = 1000, Duration = TimeSpan.FromSeconds(1.0f), MinHorizontalVelocity = -7.0f, MaxHorizontalVelocity = 7.0f, MinVerticalVelocity = 0.0f, MaxVerticalVelocity = 7.0f, Gravity = new Vector3(0.0f, -10.0f, 0.0f), MinRotateSpeed = -2.0f, MaxRotateSpeed = 2.0f, MinStartSize = 0.3f, MaxStartSize = 0.7f, MinEndSize = 0.0f, MaxEndSize = 0.0f, BlendState = Microsoft.Xna.Framework.Graphics.BlendState.AlphaBlend, MinColor = new Vector4(1.4f, 0.8f, 0.7f, 1.0f), MaxColor = new Vector4(1.4f, 0.8f, 0.7f, 1.0f), }); ParticleSystem.Add(main, "SnakeSparksYellow", new ParticleSystem.ParticleSettings { TextureName = "Particles\\splash", MaxParticles = 1000, Duration = TimeSpan.FromSeconds(1.0f), MinHorizontalVelocity = -7.0f, MaxHorizontalVelocity = 7.0f, MinVerticalVelocity = 0.0f, MaxVerticalVelocity = 7.0f, Gravity = new Vector3(0.0f, -10.0f, 0.0f), MinRotateSpeed = -2.0f, MaxRotateSpeed = 2.0f, MinStartSize = 0.3f, MaxStartSize = 0.7f, MinEndSize = 0.0f, MaxEndSize = 0.0f, BlendState = Microsoft.Xna.Framework.Graphics.BlendState.AlphaBlend, MinColor = new Vector4(1.4f, 1.4f, 0.7f, 1.0f), MaxColor = new Vector4(1.4f, 1.4f, 0.7f, 1.0f), }); } Snake snake = entity.GetOrCreate <Snake>("Snake"); entity.CannotSuspendByDistance = true; Transform transform = entity.GetOrCreate <Transform>("Transform"); AI ai = entity.GetOrCreate <AI>("AI"); Agent agent = entity.GetOrCreate <Agent>("Agent"); const float defaultSpeed = 5.0f; const float chaseSpeed = 18.0f; const float closeChaseSpeed = 12.0f; const float crushSpeed = 125.0f; VoxelChaseAI chase = entity.GetOrCreate <VoxelChaseAI>("VoxelChaseAI"); chase.Add(new TwoWayBinding <Vector3>(transform.Position, chase.Position)); chase.Speed.Value = defaultSpeed; chase.EnablePathfinding.Value = ai.CurrentState.Value == "Chase"; chase.Filter = delegate(Voxel.State state) { if (state == Voxel.States.Infected || state == Voxel.States.Neutral || state == Voxel.States.Hard || state == Voxel.States.HardInfected) { return(true); } return(false); }; entity.Add(new CommandBinding(chase.Delete, entity.Delete)); PointLight positionLight = null; if (!main.EditorEnabled) { positionLight = new PointLight(); positionLight.Serialize = false; positionLight.Color.Value = new Vector3(1.5f, 0.5f, 0.5f); positionLight.Attenuation.Value = 20.0f; positionLight.Add(new Binding <bool, string>(positionLight.Enabled, x => x != "Suspended", ai.CurrentState)); positionLight.Add(new Binding <Vector3, string>(positionLight.Color, delegate(string state) { switch (state) { case "Chase": case "Crush": return(new Vector3(1.5f, 0.5f, 0.5f)); case "Alert": return(new Vector3(1.5f, 1.5f, 0.5f)); default: return(new Vector3(1.0f, 1.0f, 1.0f)); } }, ai.CurrentState)); entity.Add("PositionLight", positionLight); ParticleEmitter emitter = entity.GetOrCreate <ParticleEmitter>("Particles"); emitter.Serialize = false; emitter.ParticlesPerSecond.Value = 100; emitter.Add(new Binding <string>(emitter.ParticleType, delegate(string state) { switch (state) { case "Chase": case "Crush": return("SnakeSparksRed"); case "Alert": return("SnakeSparksYellow"); default: return("SnakeSparks"); } }, ai.CurrentState)); emitter.Add(new Binding <Vector3>(emitter.Position, transform.Position)); emitter.Add(new Binding <bool, string>(emitter.Enabled, x => x != "Suspended", ai.CurrentState)); positionLight.Add(new Binding <Vector3>(positionLight.Position, transform.Position)); emitter.Add(new Binding <Vector3>(emitter.Position, transform.Position)); agent.Add(new Binding <Vector3>(agent.Position, transform.Position)); Sound.AttachTracker(entity); } AI.Task checkMap = new AI.Task { Action = delegate() { if (chase.Voxel.Value.Target == null || !chase.Voxel.Value.Target.Active) { entity.Delete.Execute(); } }, }; AI.Task checkOperationalRadius = new AI.Task { Interval = 2.0f, Action = delegate() { bool shouldBeActive = (transform.Position.Value - main.Camera.Position).Length() < snake.OperationalRadius; if (shouldBeActive && ai.CurrentState == "Suspended") { ai.CurrentState.Value = "Idle"; } else if (!shouldBeActive && ai.CurrentState != "Suspended") { ai.CurrentState.Value = "Suspended"; } }, }; AI.Task checkTargetAgent = new AI.Task { Action = delegate() { Entity target = ai.TargetAgent.Value.Target; if (target == null || !target.Active) { ai.TargetAgent.Value = null; ai.CurrentState.Value = "Idle"; } }, }; Func <Voxel, Direction> randomValidDirection = delegate(Voxel m) { Voxel.Coord c = chase.Coord; Direction[] dirs = new Direction[6]; Array.Copy(DirectionExtensions.Directions, dirs, 6); // Shuffle directions int i = 5; while (i > 0) { int k = this.random.Next(i); Direction temp = dirs[i]; dirs[i] = dirs[k]; dirs[k] = temp; i--; } foreach (Direction dir in dirs) { if (chase.Filter(m[c.Move(dir)])) { return(dir); } } return(Direction.None); }; Direction currentDir = Direction.None; chase.Add(new CommandBinding <Voxel, Voxel.Coord>(chase.Moved, delegate(Voxel m, Voxel.Coord c) { if (chase.Active) { string currentState = ai.CurrentState.Value; Voxel.t id = m[c].ID; if (id == Voxel.t.Hard) { m.Empty(c); m.Fill(c, Voxel.States.HardInfected); m.Regenerate(); } else if (id == Voxel.t.Neutral) { m.Empty(c); m.Fill(c, Voxel.States.Infected); m.Regenerate(); } else if (id == Voxel.t.Empty) { m.Fill(c, Voxel.States.Infected); m.Regenerate(); } if (currentState == "Idle") { if (currentDir == Direction.None || !chase.Filter(m[chase.Coord.Value.Move(currentDir)]) || this.random.Next(8) == 0) { currentDir = randomValidDirection(m); } chase.Coord.Value = chase.Coord.Value.Move(currentDir); } else if (snake.Path.Length > 0) { chase.Coord.Value = snake.Path[0]; snake.Path.RemoveAt(0); } } })); Sound.AttachTracker(entity); SoundKiller.Add(entity, AK.EVENTS.STOP_SNAKE); ai.Add(new ChangeBinding <string>(ai.CurrentState, delegate(string old, string value) { if (value == "Suspended" || value == "Alert") { AkSoundEngine.PostEvent(AK.EVENTS.STOP_SNAKE, entity); } else if (old != "Idle" && old != "Chase" && old != "Crush") { AkSoundEngine.PostEvent(AK.EVENTS.PLAY_SNAKE, entity); } })); const float sightDistance = 50.0f; const float hearingDistance = 0.0f; ai.Setup ( new AI.AIState { Name = "Suspended", Tasks = new[] { checkOperationalRadius }, }, new AI.AIState { Name = "Idle", Enter = delegate(AI.AIState previous) { Entity voxelEntity = chase.Voxel.Value.Target; if (voxelEntity != null) { Voxel m = voxelEntity.Get <Voxel>(); if (currentDir == Direction.None || !chase.Filter(m[chase.Coord.Value.Move(currentDir)])) { currentDir = randomValidDirection(m); } chase.Coord.Value = chase.Coord.Value.Move(currentDir); } }, Tasks = new[] { checkMap, checkOperationalRadius, new AI.Task { Interval = 1.0f, Action = delegate() { Agent a = Agent.Query(transform.Position, sightDistance, hearingDistance, x => x.Entity.Type == "Player"); if (a != null) { ai.CurrentState.Value = "Alert"; } }, }, }, }, new AI.AIState { Name = "Alert", Enter = delegate(AI.AIState previous) { chase.EnableMovement.Value = false; }, Exit = delegate(AI.AIState next) { chase.EnableMovement.Value = true; }, Tasks = new[] { checkMap, checkOperationalRadius, new AI.Task { Interval = 0.4f, Action = delegate() { if (ai.TimeInCurrentState > 3.0f) { ai.CurrentState.Value = "Idle"; } else { Agent a = Agent.Query(transform.Position, sightDistance, hearingDistance, x => x.Entity.Type == "Player"); if (a != null) { ai.TargetAgent.Value = a.Entity; ai.CurrentState.Value = "Chase"; } } }, }, }, }, new AI.AIState { Name = "Chase", Enter = delegate(AI.AIState previousState) { chase.EnablePathfinding.Value = true; chase.Speed.Value = chaseSpeed; }, Exit = delegate(AI.AIState nextState) { chase.EnablePathfinding.Value = false; chase.Speed.Value = defaultSpeed; }, Tasks = new[] { checkMap, checkOperationalRadius, checkTargetAgent, new AI.Task { Interval = 0.07f, Action = delegate() { Vector3 targetPosition = ai.TargetAgent.Value.Target.Get <Agent>().Position; float targetDistance = (targetPosition - transform.Position).Length(); chase.Speed.Value = targetDistance < 15.0f ? closeChaseSpeed : chaseSpeed; if (targetDistance > 50.0f || ai.TimeInCurrentState > 30.0f) // He got away { ai.CurrentState.Value = "Alert"; } else if (targetDistance < 4.0f) // We got 'im { // First, make sure we're not near a reset block Voxel v = chase.Voxel.Value.Target.Get <Voxel>(); if (VoxelAStar.BroadphaseSearch(v, chase.Coord, 6, x => x.Type == Lemma.Components.Voxel.States.Reset) == null) { ai.CurrentState.Value = "Crush"; } } else { chase.Target.Value = targetPosition; } }, }, }, }, new AI.AIState { Name = "Crush", Enter = delegate(AI.AIState lastState) { // Set up cage Voxel.Coord center = chase.Voxel.Value.Target.Get <Voxel>().GetCoordinate(ai.TargetAgent.Value.Target.Get <Agent>().Position); int radius = 1; // Bottom for (int x = center.X - radius; x <= center.X + radius; x++) { for (int z = center.Z - radius; z <= center.Z + radius; z++) { snake.Path.Add(new Voxel.Coord { X = x, Y = center.Y - 4, Z = z }); } } // Outer shell radius = 2; for (int y = center.Y - 3; y <= center.Y + 3; y++) { // Left for (int z = center.Z - radius; z <= center.Z + radius; z++) { snake.Path.Add(new Voxel.Coord { X = center.X - radius, Y = y, Z = z }); } // Right for (int z = center.Z - radius; z <= center.Z + radius; z++) { snake.Path.Add(new Voxel.Coord { X = center.X + radius, Y = y, Z = z }); } // Backward for (int x = center.X - radius; x <= center.X + radius; x++) { snake.Path.Add(new Voxel.Coord { X = x, Y = y, Z = center.Z - radius }); } // Forward for (int x = center.X - radius; x <= center.X + radius; x++) { snake.Path.Add(new Voxel.Coord { X = x, Y = y, Z = center.Z + radius }); } } // Top for (int x = center.X - radius; x <= center.X + radius; x++) { for (int z = center.Z - radius; z <= center.Z + radius; z++) { snake.Path.Add(new Voxel.Coord { X = x, Y = center.Y + 3, Z = z }); } } chase.EnablePathfinding.Value = false; chase.Speed.Value = crushSpeed; snake.CrushCoordinate.Value = chase.Coord; }, Exit = delegate(AI.AIState nextState) { chase.Speed.Value = defaultSpeed; chase.Coord.Value = chase.LastCoord.Value = snake.CrushCoordinate; snake.Path.Clear(); }, Tasks = new[] { checkMap, checkOperationalRadius, checkTargetAgent, new AI.Task { Interval = 0.01f, Action = delegate() { Agent a = ai.TargetAgent.Value.Target.Get <Agent>(); a.Damage.Execute(0.01f / 1.5f); // seconds to kill if (!a.Active) { ai.CurrentState.Value = "Alert"; } else { if ((a.Position - transform.Position.Value).Length() > 5.0f) // They're getting away { ai.CurrentState.Value = "Chase"; } } } } }, } ); this.SetMain(entity, main); entity.Add("OperationalRadius", snake.OperationalRadius); }
public override void Awake() { base.Awake(); Switch.all.Add(this); this.Add(new NotifyBinding(delegate() { if (this.On) { this.OnPowerOn.Execute(); } else { this.OnPowerOff.Execute(); } }, this.On)); this.Add(new CommandBinding(this.OnPowerOff, delegate() { if (this.main.TotalTime > 0.1f) { AkSoundEngine.PostEvent(AK.EVENTS.PLAY_SWITCH_OFF, this.Entity); } })); this.Add(new CommandBinding(this.OnPowerOn, delegate() { if (this.main.TotalTime > 0.1f && this.PowerOnCue != 0) { AkSoundEngine.PostEvent(this.PowerOnCue, this.Entity); } Voxel map = this.AttachedVoxel.Value.Target.Get <Voxel>(); Switch closestConnectedSwitch = null; Stack <Voxel.Box> closestConnectedPath = null; foreach (Switch s in Switch.all) { Stack <Voxel.Box> path = new Stack <Voxel.Box>(); if (s.On && s != this && s.AttachedVoxel.Value.Target == this.AttachedVoxel.Value.Target && VoxelAStar.Broadphase(map, map.GetBox(this.Coord), s.Coord, canConnect, path, 2000)) { if (closestConnectedPath == null || path.Count < closestConnectedPath.Count) { closestConnectedSwitch = s; closestConnectedPath = path; } } } if (closestConnectedSwitch != null) { List <Voxel.Coord> changes = new List <Voxel.Coord>(); Queue <Voxel.Coord> queue = new Queue <Voxel.Coord>(); Voxel.Coord start = closestConnectedSwitch.Coord; start.Data = null; queue.Enqueue(start); Voxel.CoordSetCache.Add(start); start.Data = Voxel.States.Switch; changes.Add(start); while (queue.Count > 0) { Voxel.Coord c = queue.Dequeue(); foreach (Direction adjacentDirection in DirectionExtensions.Directions) { Voxel.Coord adjacentCoord = c.Move(adjacentDirection); if (!Voxel.CoordSetCache.Contains(adjacentCoord)) { Voxel.CoordSetCache.Add(adjacentCoord); Voxel.State adjacentState = map[adjacentCoord]; if (adjacentState == Voxel.States.PoweredSwitch) { queue.Enqueue(adjacentCoord); adjacentCoord.Data = Voxel.States.Switch; changes.Add(adjacentCoord); } else if (adjacentState == Voxel.States.Hard) { queue.Enqueue(adjacentCoord); } else if (adjacentState == Voxel.States.HardPowered) { queue.Enqueue(adjacentCoord); adjacentCoord.Data = Voxel.States.Hard; changes.Add(adjacentCoord); } else if ((adjacentState == Voxel.States.Blue || adjacentState == Voxel.States.Powered) && closestConnectedPath.Contains(map.GetBox(adjacentCoord))) { adjacentCoord.Data = Voxel.States.Neutral; changes.Add(adjacentCoord); adjacentCoord.Data = null; } } } } Voxel.CoordSetCache.Clear(); if (changes.Count > 0) { lock (map.MutationLock) { map.Empty(changes, true, true, map); map.Fill(changes); } map.Regenerate(); } } })); }
public override void Awake() { base.Awake(); Switch.all.Add(this); this.Add(new NotifyBinding(delegate() { if (this.On) { this.OnPowerOn.Execute(); } else { this.OnPowerOff.Execute(); } }, this.On)); this.Add(new CommandBinding(this.OnPowerOff, delegate() { AkSoundEngine.PostEvent(AK.EVENTS.PLAY_SWITCH_OFF, this.Entity); })); this.Add(new CommandBinding(this.OnPowerOn, delegate() { AkSoundEngine.PostEvent(AK.EVENTS.PLAY_SWITCH_ON, this.Entity); Voxel map = this.AttachedVoxel.Value.Target.Get <Voxel>(); List <Voxel.Coord> changes = new List <Voxel.Coord>(); Stack <Voxel.Box> path = new Stack <Voxel.Box>(); Queue <Voxel.Coord> queue = new Queue <Voxel.Coord>(); foreach (Switch s in Switch.all) { if (s.On && s != this && s.AttachedVoxel.Value.Target == this.AttachedVoxel.Value.Target && VoxelAStar.Broadphase(map, map.GetBox(this.Coord), s.Coord, canConnect, path, 2000)) { Voxel.Coord start = s.Coord; start.Data = map[start]; queue.Enqueue(start); while (queue.Count > 0) { Voxel.Coord c = queue.Dequeue(); c.Data = null; // Ensure the visited dictionary works correctly Voxel.CoordDictionaryCache[c] = true; Voxel.Coord change = c.Clone(); change.Data = Voxel.States.Switch; changes.Add(change); foreach (Direction adjacentDirection in DirectionExtensions.Directions) { Voxel.Coord adjacentCoord = c.Move(adjacentDirection); if (!Voxel.CoordDictionaryCache.ContainsKey(adjacentCoord)) { Voxel.State adjacentState = map[adjacentCoord]; if (adjacentState == Voxel.States.PoweredSwitch) { queue.Enqueue(adjacentCoord); } else if ((adjacentState == Voxel.States.Blue || adjacentState == Voxel.States.Powered) && path.Contains(map.GetBox(adjacentCoord))) { adjacentCoord.Data = Voxel.States.Neutral; changes.Add(adjacentCoord); } } } } } path.Clear(); queue.Clear(); } Voxel.CoordDictionaryCache.Clear(); if (changes.Count > 0) { lock (map.MutationLock) { map.Empty(changes, true, true, map); map.Fill(changes); } map.Regenerate(); } })); }
public void Update(float dt) { Entity mapEntity = this.Voxel.Value.Target; if (mapEntity == null || !mapEntity.Active) { // Find closest map int closest = 5; Voxel.Coord newCoord = default(Voxel.Coord); foreach (Voxel m in Lemma.Components.Voxel.Voxels) { Voxel.Coord mCoord = m.GetCoordinate(this.Position); Voxel.Coord?c = m.FindClosestFilledCell(mCoord, closest); if (c.HasValue) { mapEntity = m.Entity; newCoord = c.Value; closest = Math.Min(Math.Abs(mCoord.X - newCoord.X), Math.Min(Math.Abs(mCoord.Y - newCoord.Y), Math.Abs(mCoord.Z - newCoord.Z))); } } if (mapEntity == null) { this.Delete.Execute(); } else { this.Voxel.Value = mapEntity; this.Coord.Value = this.LastCoord.Value = newCoord; this.Blend.Value = 1.0f; } } else { Voxel m = mapEntity.Get <Voxel>(); if (this.EnableMovement) { this.Blend.Value += dt * this.Speed; } if (this.Blend > 1.0f) { this.Blend.Value = 0.0f; Voxel.Coord c = this.Coord.Value; this.Moved.Execute(m, c); this.LastCoord.Value = c; if (this.EnablePathfinding) { if (this.broadphasePath.Count == 0 || this.main.TotalTime - this.lastPathCalculation > 1.0f) { this.lastPathCalculation = this.main.TotalTime; Voxel.Coord?targetCoord = m.FindClosestFilledCell(m.GetCoordinate(this.Target)); if (targetCoord.HasValue) { this.narrowphasePath.Clear(); this.broadphasePath.Clear(); Voxel.Box box = m.GetBox(c); VoxelAStar.Broadphase(m, box, targetCoord.Value, this.Filter, this.broadphasePath); if (this.broadphasePath.Count > 0) { this.broadphasePath.Pop(); // First box is the current one } //this.debugBroadphase(m, this.broadphasePath); } } if (this.narrowphasePath.Count == 0 && this.broadphasePath.Count > 0) { VoxelAStar.Narrowphase(m, this.Coord, this.broadphasePath.Pop(), this.narrowphasePath); if (this.narrowphasePath.Count <= 1) { this.broadphasePath.Clear(); this.narrowphasePath.Clear(); this.Blend.Value = 1.0f; } else { this.narrowphasePath.Pop(); // First coordinate is the current one } //this.debugNarrowphase(m, this.narrowphasePath); } if (this.narrowphasePath.Count > 0) { Voxel.Coord newCoord = this.narrowphasePath.Pop(); if (this.Filter(m[newCoord])) { this.Coord.Value = newCoord; } else { this.broadphasePath.Clear(); this.narrowphasePath.Clear(); this.Blend.Value = 1.0f; } } } } Vector3 last = m.GetAbsolutePosition(this.LastCoord), current = m.GetAbsolutePosition(this.Coord); this.Position.Value = Vector3.Lerp(last, current, this.Blend); } }