Example #1
0
		public DungeonFloor(DungeonGenerator dungeonGenerator, DungeonFloorData floorData, bool isLastFloor, DungeonFloor prevFloor)
		{
			_pos = new Position(0, 0);
			_startPos = new Position(0, 0);
			_startDirection = Direction.Down;
			this.Width = 1;
			this.Height = 1;
			this.MazeGenerator = new MazeGenerator();
			this.Sections = new List<DungeonFloorSection>();

			_dungeonGenerator = dungeonGenerator;
			_branchProbability = floorData.Branch;
			_coverageFactor = floorData.Coverage;
			this.IsLastFloor = isLastFloor;
			_prevFloor = prevFloor;

			this.HasBossRoom = floorData.HasBoss;
			this.Statue = floorData.Statue;

			this.CalculateSize(floorData);
			this.InitRoomtraits();
			this.GenerateMaze(floorData);
			this.GenerateRooms(floorData);
			this.InitSections(floorData);
		}
Example #2
0
		public MazeGenerator()
		{
			_currentPos = new Position(0, 0);
			this.StartPos = new Position(0, 0);
			this.EndPos = new Position(0, 0);
			this.Rooms = new List<List<MazeRoomInternal>>();
			this.CriticalPath = new List<MazeMove>();
		}
Example #3
0
		private bool CreateSubPathRecursive(Position pos)
		{
			var room = GetRoom(pos);
			var maze_room = this.MazeGenerator.GetRoom(pos);

			for (int direction = 0; direction < 4; direction++)
			{
				if (maze_room.GetPassageType(direction) == 2)
				{
					var biased_pos = pos.GetBiasedPosition(direction);
					if (room != null)
						room.Link(direction, LinkType.To);

					this.CreateSubPathRecursive(biased_pos);
				}
			}
			return true;
		}
Example #4
0
		private List<MazeMove> CreateCriticalPath(int crit_path_min, int crit_path_max)
		{
			while (true)
			{
				this.MazeGenerator.SetSize(this.Width, this.Height);
				this.SetRandomPathPosition();

				if (this.MazeGenerator.GenerateCriticalPath(_dungeonGenerator.RngMaze, crit_path_min, crit_path_max))
				{
					_startPos = this.MazeGenerator.StartPos;
					if (this.SetTraits(_startPos, this.MazeGenerator.StartDirection, (int)DungeonBlockType.StairsUp))
						break;
				}

				this.MazeGenerator = new MazeGenerator();
				this.InitRoomtraits();
			}

			return this.MazeGenerator.CriticalPath;
		}
Example #5
0
		private void SetRoomTypes()
		{
			for (int y = 0; y < this.MazeGenerator.Height; ++y)
			{
				for (int x = 0; x < this.MazeGenerator.Width; ++x)
				{
					var pos = new Position(x, y);
					var room = this.MazeGenerator.GetRoom(pos);
					var roomTrait = this.GetRoom(pos);

					if (this.MazeGenerator.StartPos.X == x && this.MazeGenerator.StartPos.Y == y)
						roomTrait.RoomType = RoomType.Start;
					else if (this.MazeGenerator.EndPos.X == x && this.MazeGenerator.EndPos.Y == y)
						roomTrait.RoomType = RoomType.End;
					else if (room.Visited)
						roomTrait.RoomType = RoomType.Alley;
				}
			}
		}
Example #6
0
		private bool SetTraits(Position pos, int direction, int doorType)
		{
			var biased_pos = pos.GetBiasedPosition(direction);
			if ((biased_pos.X >= 0) && (biased_pos.Y >= 0))
			{
				if ((biased_pos.X < this.Width) && (biased_pos.Y < this.Height))
				{
					if (!this.MazeGenerator.IsFree(biased_pos))
						return false;

					this.MazeGenerator.MarkReservedPosition(biased_pos);
				}
			}

			var room = this.GetRoom(pos);
			if (room.IsLinked(direction))
				throw new Exception("Room in direction isn't linked");

			if (room.GetDoorType(direction) != 0)
				throw new Exception();

			LinkType linkType;
			if (doorType == (int)DungeonBlockType.StairsDown)
				linkType = LinkType.To;
			else if (doorType == (int)DungeonBlockType.StairsUp)
				linkType = LinkType.From;
			else
				throw new Exception("Invalid door_type");

			room.Link(direction, linkType);
			room.SetDoorType(direction, doorType);

			return true;
		}
Example #7
0
		public RoomTrait GetRoom(Position pos)
		{
			return this.GetRoom(pos.X, pos.Y);
		}
Example #8
0
		/// <summary>
		/// Adds boss to list of bosses to spawn.
		/// </summary>
		/// <param name="raceId"></param>
		/// <param name="amount"></param>
		public void AddBoss(int raceId, int amount = 1)
		{
			var rnd = RandomProvider.Get();
			var end = this.Generator.Floors.Last().MazeGenerator.EndPos;
			var endX = end.X * TileSize + TileSize / 2;
			var endY = end.Y * TileSize + TileSize / 2;
			var regionId = this.Regions.Last().Id;

			for (int i = 0; i < amount; ++i)
			{
				var pos = new Position(endX, endY + TileSize / 2);
				pos = pos.GetRandomInRange(TileSize / 2, rnd);

				var npc = new NPC(raceId);
				npc.Death += this.OnBossDeath;
				npc.Spawn(regionId, pos.X, pos.Y);
				Send.SpawnEffect(SpawnEffect.Monster, regionId, pos.X, pos.Y, npc, npc);
				if (npc.AI != null)
					npc.AI.Activate(0);
			}

			Interlocked.Add(ref _bossesRemaining, amount);
		}
Example #9
0
		public void MarkReservedPosition(Position pos)
		{
			MazeRoomInternal room = Rooms[pos.X][pos.Y];
			if (!room.Visited)
				room.IsReserved = true;
		}
Example #10
0
		public bool IsRoomInDirectionFree(Position pos, int direction)
		{
			var dirPos = pos.GetBiasedPosition(direction);

			if ((0 <= dirPos.X) && (dirPos.X < Width) && (0 <= dirPos.Y) && (dirPos.Y < Height))
				return !Rooms[dirPos.X][dirPos.Y].Occupied;

			return false;
		}
Example #11
0
		public bool IsFree(Position pos)
		{
			return !this.Rooms[pos.X][pos.Y].Occupied;
		}
Example #12
0
		public MazeRoomInternal GetRoom(Position pos)
		{
			return this.GetRoom(pos.X, pos.Y);
		}
Example #13
0
		private void UndoMove(int count = 1)
		{
			for (int i = 0; i < count; i++)
			{
				var move = this.CriticalPath[this.CriticalPath.Count - 1];

				this.CriticalPath.Remove(this.CriticalPath[this.CriticalPath.Count - 1]);

				var current_room = this.GetRoom(move.PosFrom);
				var next_room = this.GetRoom(move.PosTo);

				var oppositeDirection = Direction.GetOppositeDirection(move.Direction);

				if (next_room.Visited)
				{
					current_room.Directions[move.Direction] = 0;
					next_room.Directions[oppositeDirection] = 0;

					next_room.VisitedCount = 0;
					next_room.IsOnCriticalPath = false;

					_counter -= 1;
				}

				_currentPos = _currentPos.GetBiasedPosition(oppositeDirection);
			}
		}
Example #14
0
		private bool MakeMove(int direction)
		{
			if (this.IsRoomInDirectionFree(_currentPos, direction))
			{
				var nextPos = _currentPos.GetBiasedPosition(direction);
				var currentRoom = this.GetRoom(_currentPos);
				var nextRoom = this.GetRoom(nextPos);
				var move = new MazeMove(_currentPos, nextPos, direction);

				this.CriticalPath.Add(move);

				_counter++;

				nextRoom.VisitedCount = _counter;
				nextRoom.IsOnCriticalPath = true;

				currentRoom.Directions[direction] = 1;
				nextRoom.Directions[Direction.GetOppositeDirection(direction)] = 2;

				_currentPos = nextPos;

				return true;
			}

			return false;
		}
Example #15
0
		private int Sub(Position pos)
		{
			if (this.GetRoom(pos) != null)
			{
				var count = 1;

				for (int iDir = 0; iDir < 4; iDir++)
				{
					var room = this.GetRoom(pos.GetBiasedPosition(iDir));
					if (room != null && !room.Occupied)
						count += 1;
				}

				return count;
			}

			return 0;
		}
Example #16
0
		public void SetPathPosition(Position pos)
		{
			if ((this.Width > pos.X) && (this.Height > pos.Y))
			{
				MazeRoomInternal room;

				for (int y = 0; y < Height; y++)
				{
					for (int x = 0; x < Width; x++)
					{
						room = Rooms[x][y];
						room.Directions = new int[4];
						room.VisitedCount = 0;
					}
				}

				this.CriticalPath = new List<MazeMove>();
				this.StartPos = new Position(0, 0);
				_currentPos = new Position(0, 0);

				this.EndPos.X = pos.X;
				this.EndPos.Y = pos.Y;

				_currentPos.X = pos.X;
				_currentPos.Y = pos.Y;

				_counter = 1;

				room = Rooms[pos.X][pos.Y];
				room.VisitedCount = _counter;
				room.IsOnCriticalPath = true;
			}
		}
Example #17
0
		/// <summary>
		/// Initiates floor, creating puzzles and props.
		/// </summary>
		/// <param name="iRegion"></param>
		public void InitFloorRegion(int iRegion)
		{
			this.CreatePuzzles(iRegion);

			var region = this.Regions[iRegion];
			var floor = this.Generator.Floors[iRegion - 1];
			var gen = floor.MazeGenerator;
			var floorData = this.Data.Floors[iRegion - 1];

			var iPrevRegion = iRegion - 1;
			var iNextRegion = iRegion + 1;

			var startTile = gen.StartPos;
			var startPos = new Generation.Position(startTile.X * Dungeon.TileSize + Dungeon.TileSize / 2, startTile.Y * Dungeon.TileSize + Dungeon.TileSize / 2);
			var startRoomTrait = floor.GetRoom(startTile);
			var startRoomIncomingDirection = startRoomTrait.GetIncomingDirection();

			var endTile = gen.EndPos;
			var endPos = new Generation.Position(endTile.X * Dungeon.TileSize + Dungeon.TileSize / 2, endTile.Y * Dungeon.TileSize + Dungeon.TileSize / 2);
			var endRoomTrait = floor.GetRoom(endTile);
			var endRoomDirection = 0;
			for (int dir = 0; dir < 4; ++dir)
			{
				if (endRoomTrait.Links[dir] == LinkType.To)
				{
					endRoomDirection = dir;
					break;
				}
			}

			// Create upstairs prop
			var stairsBlock = this.Data.Style.Get(DungeonBlockType.StairsUp, startRoomIncomingDirection);
			var stairs = new Prop(stairsBlock.PropId, region.Id, startPos.X, startPos.Y, MabiMath.DegreeToRadian(stairsBlock.Rotation), 1, 0, "single");
			stairs.Info.Color1 = floorData.Color1;
			stairs.Info.Color2 = floorData.Color1;
			stairs.Info.Color3 = floorData.Color3;
			region.AddProp(stairs);

			// Create portal prop leading to prev floor
			var portalBlock = this.Data.Style.Get(DungeonBlockType.PortalUp, startRoomIncomingDirection);
			var portal = new Prop(portalBlock.PropId, region.Id, startPos.X, startPos.Y, MabiMath.DegreeToRadian(portalBlock.Rotation), 1, 0, "single", "_upstairs", Localization.Get("<mini>TO</mini> Upstairs"));
			portal.Info.Color1 = floorData.Color1;
			portal.Info.Color2 = floorData.Color1;
			portal.Info.Color3 = floorData.Color3;
			portal.Behavior = (cr, pr) =>
			{
				// Indoor_RDungeon_EB marks the end position on the prev floor.
				var clientEvent = this.Regions[iPrevRegion].GetClientEvent("Indoor_RDungeon_EB");
				if (clientEvent == null)
				{
					Log.Error("Event 'Indoor_RDungeon_EB' not found while trying to warp to '{0}'.", this.Regions[iPrevRegion].Name);
					return;
				}

				// Warp to prev region
				var regionId = this.Regions[iPrevRegion].Id;
				var x = (int)clientEvent.Data.X;
				var y = (int)clientEvent.Data.Y;

				cr.Warp(regionId, x, y);
			};
			region.AddProp(portal);

			// Create save statue
			var saveStatue = new Prop(this.Data.SaveStatuePropId, region.Id, startPos.X, startPos.Y, MabiMath.DegreeToRadian(stairsBlock.Rotation + 180), 1, 0, "single");
			saveStatue.Info.Color1 = floorData.Color1;
			saveStatue.Info.Color2 = floorData.Color1;
			saveStatue.Info.Color3 = floorData.Color3;
			saveStatue.Behavior = (cr, pr) =>
			{
				cr.DungeonSaveLocation = cr.GetLocation();
				Send.Notice(cr, Localization.Get("You have memorized this location."));

				// Scroll message
				var msg = string.Format("You're currently on Floor {0} of {1}. ", iRegion, this.Data.EngName);
				Send.Notice(cr, NoticeType.Top, ScrollMessageDuration, msg + this.GetPlayerListScrollMessage());
			};
			region.AddProp(saveStatue);

			// Spawn boss or downstair props
			// TODO: There is one dungeon that has two boss rooms.
			if (floor.IsLastFloor)
			{
				// Create door to treasure room
				_bossExitDoor = new Prop(this.Data.BossExitDoorId, region.Id, endPos.X, endPos.Y + Dungeon.TileSize / 2, Rotation(Direction.Up), 1, 0, "closed");
				_bossExitDoor.Info.Color1 = floorData.Color1;
				_bossExitDoor.Info.Color2 = floorData.Color1;
				_bossExitDoor.Info.Color3 = floorData.Color3;
				region.AddProp(_bossExitDoor);

				// Get or create boss door
				if (endRoomTrait.PuzzleDoors[Direction.Down] == null)
				{
					Log.Warning("Dungeon.InitFloorRegion: No locked place in last section of '{0}'.", this.Name);

					_bossDoor = new Door(this.Data.BossDoorId, region.Id, endPos.X, endPos.Y - Dungeon.TileSize, Direction.Up, DungeonBlockType.BossDoor, "", "closed");
					_bossDoor.Info.Color1 = floorData.Color1;
					_bossDoor.Info.Color2 = floorData.Color1;
					_bossDoor.Info.Color3 = floorData.Color3;
					_bossDoor.Behavior = (cr, pr) => { _bossDoor.Open(); };
					_bossDoor.Behavior += this.BossDoorBehavior;
					_bossDoor.UpdateShapes();
					endRoomTrait.PuzzleDoors[Direction.Down] = _bossDoor; // making sure another open dummy door won't be added here
					region.AddProp(_bossDoor);
				}
				else
				{
					_bossDoor = endRoomTrait.PuzzleDoors[Direction.Down];
				}

				// Create exit statue
				var exitStatue = new Prop(this.Data.LastStatuePropId, region.Id, endPos.X, endPos.Y + Dungeon.TileSize * 2, Rotation(Direction.Up), 1, 0, "single");
				exitStatue.Info.Color1 = floorData.Color1;
				exitStatue.Info.Color2 = floorData.Color1;
				exitStatue.Info.Color3 = floorData.Color3;
				exitStatue.Extensions.AddSilent(new ConfirmationPropExtension("GotoLobby", "_LT[code.standard.msg.dungeon_exit_notice_msg]", "_LT[code.standard.msg.dungeon_exit_notice_title]", "haskey(chest)"));
				exitStatue.Behavior = (cr, pr) => { cr.Warp(this.Data.Exit); };
				region.AddProp(exitStatue);
			}
			else
			{
				// Create downstairs prop
				var stairsDownBlock = this.Data.Style.Get(DungeonBlockType.StairsDown, endRoomDirection);
				var stairsDown = new Prop(stairsDownBlock.PropId, region.Id, endPos.X, endPos.Y, MabiMath.DegreeToRadian(stairsDownBlock.Rotation), 1, 0, "single");
				stairsDown.Info.Color1 = floorData.Color1;
				stairsDown.Info.Color2 = floorData.Color1;
				stairsDown.Info.Color3 = floorData.Color3;
				region.AddProp(stairsDown);

				// Create portal leading to the next floor
				var portalDownBlock = this.Data.Style.Get(DungeonBlockType.PortalDown, endRoomDirection);
				var portalDown = new Prop(portalDownBlock.PropId, region.Id, endPos.X, endPos.Y, MabiMath.DegreeToRadian(portalDownBlock.Rotation), 1, 0, "single", "_downstairs", Localization.Get("<mini>TO</mini> Downstairs"));
				portalDown.Info.Color1 = floorData.Color1;
				portalDown.Info.Color2 = floorData.Color1;
				portalDown.Info.Color3 = floorData.Color3;
				portalDown.Behavior = (cr, pr) =>
				{
					// Indoor_RDungeon_SB marks the start position on the next floor.
					var clientEvent = this.Regions[iNextRegion].GetClientEvent("Indoor_RDungeon_SB");
					if (clientEvent == null)
					{
						Log.Error("Event 'Indoor_RDungeon_SB' not found while trying to warp to '{0}'.", this.Regions[iNextRegion].Name);
						return;
					}

					// Warp to next floor
					var regionId = this.Regions[iNextRegion].Id;
					var x = (int)clientEvent.Data.X;
					var y = (int)clientEvent.Data.Y;

					cr.Warp(regionId, x, y);
				};
				region.AddProp(portalDown);
			}

			// Place dummy doors
			for (int x = 0; x < floor.MazeGenerator.Width; ++x)
			{
				for (int y = 0; y < floor.MazeGenerator.Height; ++y)
				{
					var room = floor.MazeGenerator.GetRoom(x, y);
					var roomTrait = floor.GetRoom(x, y);
					var isRoom = (roomTrait.RoomType >= RoomType.Start);

					if (!room.Visited || !isRoom)
						continue;

					for (var dir = 0; dir < 4; ++dir)
					{
						// Skip stairs
						if (roomTrait.RoomType == RoomType.Start && dir == startRoomIncomingDirection)
							continue;
						if (roomTrait.RoomType == RoomType.End && dir == endRoomDirection)
							continue;

						if (roomTrait.Links[dir] == LinkType.None)
							continue;

						if (roomTrait.PuzzleDoors[dir] == null)
						{
							var doorX = x * Dungeon.TileSize + Dungeon.TileSize / 2;
							var doorY = y * Dungeon.TileSize + Dungeon.TileSize / 2;

							var doorBlock = this.Data.Style.Get(DungeonBlockType.Door, dir);
							var doorProp = new Prop(doorBlock.PropId, region.Id, doorX, doorY, MabiMath.DegreeToRadian(doorBlock.Rotation), state: "open");
							doorProp.Info.Color1 = floorData.Color1;
							doorProp.Info.Color2 = floorData.Color2;
							doorProp.Info.Color3 = floorData.Color3;
							region.AddProp(doorProp);
						}
						else if (roomTrait.PuzzleDoors[dir].EntityId == 0)
						{
							// Add doors from failed puzzles
							roomTrait.PuzzleDoors[dir].Info.Region = region.Id;
							region.AddProp(roomTrait.PuzzleDoors[dir]);
						}
					}
				}
			}
		}
Example #18
0
		private void InitRoomtraits()
		{
			_rooms = new List<List<RoomTrait>>();
			for (int x = 0; x < this.Width; x++)
			{
				var row = new List<RoomTrait>();
				for (int y = 0; y < this.Height; y++)
					row.Add(new RoomTrait(x, y));

				_rooms.Add(row);
			}

			for (int y = 0; y < this.Height; y++)
			{
				for (int x = 0; x < this.Width; x++)
				{
					for (int direction = 0; direction < 4; direction++)
					{
						var biased_pos = new Position(x, y).GetBiasedPosition(direction);
						if ((biased_pos.X >= 0) && (biased_pos.Y >= 0))
							if ((biased_pos.X < this.Width) && (biased_pos.Y < this.Height))
								_rooms[x][y].SetNeighbor(direction, _rooms[biased_pos.X][biased_pos.Y]);
					}
				}
			}
		}
Example #19
0
		/// <summary>
		/// Spawns treasure chests in treasure room.
		/// </summary>
		private void SpawnTreasureChests()
		{
			var region = this.Regions.Last();
			var rnd = RandomProvider.Get();

			for (int i = 0; i < _treasureChests.Count; ++i)
			{
				var pos = new Generation.Position(this.Generator.Floors.Last().MazeGenerator.EndPos);

				pos.X *= Dungeon.TileSize;
				pos.Y *= Dungeon.TileSize;

				pos.X += Dungeon.TileSize / 2;
				pos.Y += (int)(Dungeon.TileSize * 2.5f);

				var placement = _treasurePlacementProvider.GetPosition();

				pos.X += placement[0];
				pos.Y += placement[1];
				var rotation = MabiMath.DegreeToRadian(placement[2]);

				var prop = _treasureChests[i];
				prop.RegionId = region.Id;
				prop.Info.X = pos.X;
				prop.Info.Y = pos.Y;
				prop.Info.Direction = rotation;
				region.AddProp(prop);
			}
		}
Example #20
0
		private bool GenerateSubPathSub2(Position pos)
		{
			var room = this.GetRoom(pos);

			if (room != null)
			{
				for (int i = 0; i < 4; i++)
				{
					if (room.GetPassageType(i) == 0)
					{
						if (this.IsRoomInDirectionFree(pos, i))
							return true;
					}
				}
			}

			return false;
		}