/// <summary> /// Calculates the Manhatten distance on the XY plane between two coordinates /// </summary> /// <param name="c1"></param> /// <param name="c2"></param> /// <returns></returns> public static int GetManhattenDistanceOnXYPlane(MapCoordinate c1, MapCoordinate c2) { int deltaX = Math.Abs(c1.X - c2.X); int deltaY = Math.Abs(c1.Y - c2.Y); return deltaX + deltaY; }
/// <summary> /// Gets a path from the startPoint to the endPoint. Or null if there are no possible points /// </summary> /// <param name="startPoint"></param> /// <param name="endPoint"></param> /// <returns></returns> public static Stack<MapCoordinate> GetPath(MapCoordinate startPoint, MapCoordinate endPoint) { if (pathFinder == null) { if (nodes == null) { GameState.LocalMap.GeneratePathfindingMap(); nodes = GameState.LocalMap.PathfindingMap; } //Create the new pathfinder with the map - add the settings pathFinder = new PathFinderFast(nodes); pathFinder.Formula = HeuristicFormula.Manhattan; pathFinder.Diagonals = false; pathFinder.HeavyDiagonals = false; pathFinder.HeuristicEstimate = 2; pathFinder.PunishChangeDirection = true; pathFinder.TieBreaker = false; pathFinder.SearchLimit = 5000; } List<PathFinderNode> path = pathFinder.FindPath(new Point(startPoint.X, startPoint.Y), new Point(endPoint.X, endPoint.Y)); Stack<MapCoordinate> coordStack = new Stack<MapCoordinate>(); if (path == null) { Console.WriteLine("No path found :("); return null; } //Check that all points are valid foreach (var point in path) { if (nodes[point.X, point.Y] > 100) { Console.WriteLine("No path found :( "); return null; } } foreach (PathFinderNode node in path) { coordStack.Push(new MapCoordinate(node.X, node.Y, 0, startPoint.MapType)); } if (coordStack.Count == 0) { return null; } else { coordStack.Pop(); //remove the start node return coordStack; } }
public ThrowItemComponent(int x, int y, MapCoordinate targetCoordinate, Actor actor) { this.locationX = x; this.locationY = y; this.targetCoordinate = targetCoordinate; this.Actor = actor; PerformDrag(0, 0); }
/// <summary> /// Creates a new Context Menu starting at x,y /// </summary> /// <param name="x"></param> /// <param name="y"></param> public ContextMenuComponent(int x, int y,MapCoordinate coordinate) { this.contextMenuItems = new List<ContextMenuItem>(); this.drawRectangle = new Rectangle(); this.drawRectangle.X = x; this.drawRectangle.Y = y; this.coordinate = coordinate; Visible = true; }
/// <summary> /// Calculates the Cartisian Displacement between two coordinates on the XY plane /// </summary> /// <param name="c1"></param> /// <param name="c2"></param> /// <returns></returns> public static double GetCartisianDisplacementOnXYPlane(MapCoordinate c1, MapCoordinate c2) { //using pythagoras //H = sqrt(Delta X ^2 + Delta Y ^ 2) int deltaX = Math.Abs(c1.X - c2.X); int deltaY = Math.Abs(c1.Y - c2.Y); return Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2)); }
/// <summary> /// Constructor. Sets the default values for this tile at a particular coordinate /// </summary> public Air(MapCoordinate coord) : base(coord) { this.Description = ""; this.Graphic = null; this.MayContainItems = false; this.Name = ""; this.InternalName = "Air"; this.IsActive = true; }
public object ConvertValueWhenRead(object inputValue) { var coordStr = inputValue as string; var mapCoordinate = new MapCoordinate {Latitude = 0, Longitude = 0, Zoom = 12}; if (coordStr != null) { var vals = coordStr.Split(new[] {','}); if (vals.Length == 3) { mapCoordinate = new MapCoordinate { Latitude = float.Parse(vals[0]), Longitude = float.Parse(vals[1]), Zoom = int.Parse(vals[2]) }; } } return mapCoordinate; }
/// <summary> /// Gets a graphical block whch exists on a particular point, with a global overlay /// </summary> /// <param name="point"></param> /// <param name="globalOverlay"></param> /// <returns></returns> public static GraphicalBlock GetBlockAtPoint(MapCoordinate point, GlobalOverlay globalOverlay) { switch (point.MapType) { case (DRObjects.Enums.MapType.GLOBAL) : try { return GameState.GlobalMap.GetBlockAtCoordinate(point).ConvertToGraphicalBlock(globalOverlay); } catch {//send an empty one GraphicalBlock block = new GraphicalBlock(); block.MapCoordinate = point; return block; } case (DRObjects.Enums.MapType.LOCAL) : return GameState.LocalMap.GetBlockAtCoordinate(point).ConvertToGraphicalBlock(); default: throw new NotImplementedException("There is no map manager for that type"); } }
public List<MapCoordinate> GetOutletMap(Guid distrubutrId, Guid? routeId) { IQueryable<tblCostCentre> data = _ctx.tblCostCentre.Where(s => s.CostCentreType == 5 && s.ParentCostCentreId == distrubutrId); if(routeId.HasValue) { data = data.Where(s => s.RouteId == routeId.Value); } var mapdata= new List<MapCoordinate>(); foreach (var outlet in data) { float longi = 0, lati = 0; float.TryParse(outlet.StandardWH_Latitude,out lati); float.TryParse(outlet.StandardWH_Longtitude, out longi); var item = new MapCoordinate(); item.Name = outlet.Name; item.Description = outlet.Name; item.Latitude = lati; item.Longitude = longi; mapdata.Add(item); } return mapdata; }
public bool HandleKeyboard(Microsoft.Xna.Framework.Input.KeyboardState keyboard, out ActionType? actionType, out object[] args, out MapCoordinate coord, out bool destroy) { //This component doesn't handle any keyboard actionType = null; args = null; coord = null; //But because we're moving, destroy it destroy = false; return false; }
public bool HandleClick(int x, int y, MouseActionEnum mouseAction, out ActionType? actionType, out InternalActionEnum? internalActionType, out object[] args, out MapItem itm, out MapCoordinate coord, out bool destroy) { itm = null; internalActionType = null; //We only handle left clicks properly if (mouseAction.Equals(MouseActionEnum.LEFT_CLICK)) { //check whether x and y was within a context menu item foreach (ContextMenuItem item in this.contextMenuItems) { if (item.Rect.Contains(new Point(x, y))) { actionType = item.Action; args = item.Args; coord = this.coordinate; //destroy the component destroy = true; return true; } } args = null; actionType = null; coord = null; destroy = false; return false; } else { actionType = null; args = null; coord = null; //destroy it destroy = true; return false; } }
/// <summary> /// Performs the action and handles feedback /// </summary> /// <param name="?"></param> public void PerformAction(MapCoordinate coord, MapItem item, DRObjects.Enums.ActionType actionType, object[] args) { //remove any viewtiletext components or contextmenu components for (int i = 0; i < interfaceComponents.Count; i++) { var type = interfaceComponents[i].GetType(); if (type.Equals(typeof(ViewTileTextComponent)) || type.Equals(typeof(ContextMenuComponent))) { //delete interfaceComponents.RemoveAt(i); i--; } } ActionFeedback[] fb = UserInterfaceManager.PerformAction(coord, item, actionType, args); //go through all the feedback for (int i = 0; i < fb.Length; i++) { ActionFeedback feedback = fb[i]; if (feedback == null) { continue; } if (feedback.GetType().Equals(typeof(AttackFeedback))) { AttackFeedback af = feedback as AttackFeedback; var combatAf = CombatManager.Attack(af.Attacker, af.Defender, AttackLocation.CHEST); //always attack the chest var tempFBList = fb.ToList(); tempFBList.AddRange(combatAf); fb = tempFBList.ToArray(); } else if (feedback.GetType().Equals(typeof(OpenInterfaceFeedback))) { OpenInterfaceFeedback oif= feedback as OpenInterfaceFeedback; //Generate one if (oif.Interface.GetType() == typeof(CombatManualInterface)) { var cmi = oif.Interface as CombatManualInterface; CombatManualComponent cmc = new CombatManualComponent(GraphicsDevice.Viewport.Width / 2 - 200, GraphicsDevice.Viewport.Height / 2 - 150, cmi.Manual); interfaceComponents.Add(cmc); } else if (oif.Interface.GetType() == typeof(ThrowItemInterface)) { var tii = oif.Interface as ThrowItemInterface; //Do we have LoS to that point? if (GameState.LocalMap.HasDirectPath(GameState.PlayerCharacter.MapCharacter.Coordinate,tii.Coordinate)) { ThrowItemComponent tic = new ThrowItemComponent(Mouse.GetState().X, Mouse.GetState().Y, tii.Coordinate, GameState.PlayerCharacter); interfaceComponents.Add(tic); } else { var tempFBList = fb.ToList(); tempFBList.Add(new TextFeedback("You can't see there")); fb = tempFBList.ToArray(); } } } else if (feedback.GetType().Equals(typeof(TextFeedback))) { MouseState mouse = Mouse.GetState(); //Display it interfaceComponents.Add(new ViewTileTextComponent(mouse.X + 15, mouse.Y, (feedback as TextFeedback).Text)); } else if (feedback.GetType().Equals(typeof(LogFeedback))) { GameState.NewLog.Add(feedback as LogFeedback); } else if (feedback.GetType().Equals(typeof(InterfaceToggleFeedback))) { InterfaceToggleFeedback iop = feedback as InterfaceToggleFeedback; if (iop.InterfaceComponent == InternalActionEnum.OPEN_ATTACK && iop.Open) { //Open the attack interface for a particular actor. If one is not open already //Identify the actor in question var actorMapItem = iop.Argument as LocalCharacter; //Locate the actual actor Actor actor = GameState.LocalMap.Actors.Where(lm => lm.MapCharacter == actorMapItem).FirstOrDefault(); //Yep, it's a pointer equals bool openAlready = false; //Do we have one open already? foreach (AttackActorComponent aac in interfaceComponents.Where(ic => ic.GetType().Equals(typeof(AttackActorComponent)))) { if (aac.TargetActor.Equals(actor)) { openAlready = true; break; } } if (!openAlready) { //Open it. Otherwise don't do anything interfaceComponents.Add(new AttackActorComponent(150, 150, GameState.PlayerCharacter, actor) { Visible = true }); } } else if (iop.InterfaceComponent == InternalActionEnum.OPEN_ATTACK && !iop.Open) { //Close it var actor = iop.Argument as Actor; AttackActorComponent component = null; foreach (AttackActorComponent aac in interfaceComponents.Where(ic => ic.GetType().Equals(typeof(AttackActorComponent)))) { if (aac.TargetActor.Equals(actor)) { component = aac; } } //Did we have a match? if (component != null) { //remove it interfaceComponents.Remove(component); } } else if (iop.InterfaceComponent == InternalActionEnum.OPEN_TRADE && iop.Open) { //Open trade var arguments = iop.Argument as object[]; TradeDisplayComponent tdc = new TradeDisplayComponent(100, 100, arguments[1] as Actor, arguments[0] as Actor); interfaceComponents.Add(tdc); } else if (iop.InterfaceComponent == InternalActionEnum.OPEN_LOOT) { //Open Loot TreasureChest lootContainer = (iop.Argument as object[])[0] as TreasureChest; LootComponent lc = new LootComponent(100, 100, lootContainer); interfaceComponents.Add(lc); } } else if (feedback.GetType().Equals(typeof(CreateEventFeedback))) { CreateEventFeedback eventFeedback = feedback as CreateEventFeedback; var gameEvent = EventHandlingManager.CreateEvent(eventFeedback.EventName); //Create the actual control interfaceComponents.Add(new DecisionPopupComponent(PlayableWidth / 2 - 150, PlayableHeight / 2 - 150, gameEvent)); } else if (feedback.GetType().Equals(typeof(ReceiveEffectFeedback))) { ReceiveEffectFeedback recFeed = feedback as ReceiveEffectFeedback; EffectsManager.PerformEffect(recFeed.Effect.Actor, recFeed.Effect); } else if (feedback.GetType().Equals(typeof(ReceiveBlessingFeedback))) { ReceiveBlessingFeedback blessFeedback = feedback as ReceiveBlessingFeedback; LogFeedback lg = null; //Bless him! //Later we're going to want to do this properly so other characters can get blessed too BlessingManager.GetAndApplyBlessing(GameState.PlayerCharacter, out lg); if (lg != null) { //Log it GameState.NewLog.Add(lg); } } else if (feedback.GetType().Equals(typeof(ReceiveItemFeedback))) { ReceiveItemFeedback receiveFeedback = feedback as ReceiveItemFeedback; //Determine which item we're going to generate InventoryItemManager iim = new InventoryItemManager(); InventoryItem itm = iim.GetBestCanAfford(receiveFeedback.Category.ToString(), receiveFeedback.MaxValue); if (itm != null) { itm.InInventory = true; GameState.PlayerCharacter.Inventory.Inventory.Add(itm.Category, itm); GameState.NewLog.Add(new LogFeedback(InterfaceSpriteName.SUN, Color.DarkGreen, "You throw in your offering. You then see something glimmer and take it out")); } else { GameState.NewLog.Add(new LogFeedback(InterfaceSpriteName.MOON, Color.DarkBlue, "You throw in your offering. Nothing appears to be there. Hmm...")); } } else if (feedback.GetType().Equals(typeof(LocationChangeFeedback))) { //Remove settlement button and interface var locDetails = this.interfaceComponents.Where(ic => ic.GetType().Equals(typeof(LocationDetailsComponent))).FirstOrDefault(); if (locDetails != null) { this.interfaceComponents.Remove(locDetails); } var button = this.menuButtons.Where(mb => (mb as AutoSizeGameButton).Action == InternalActionEnum.TOGGLE_SETTLEMENT).FirstOrDefault(); if (button != null) { this.menuButtons.Remove(button); } LocationChangeFeedback lce = feedback as LocationChangeFeedback; if (lce.Location != null) { LoadLocation(lce.Location); if (lce.Location is Settlement) { //Makde the components visible LocationDetailsComponent ldc = new LocationDetailsComponent(GameState.LocalMap.Location as Settlement, PlayableWidth - 170, 0); ldc.Visible = true; interfaceComponents.Add(ldc); menuButtons.Add(new AutoSizeGameButton(" Settlement ", this.game.Content, InternalActionEnum.TOGGLE_SETTLEMENT, new object[] { }, 270, GraphicsDevice.Viewport.Height - 35)); Window_ClientSizeChanged(null, null); //button is in the wrong position for some reason } GameState.LocalMap.IsGlobalMap = false; } else if (lce.VisitMainMap) { //If it's a bandit camp or a site, update the values of the members if (GameState.LocalMap.Location as MapSite != null || GameState.LocalMap.Location as BanditCamp != null) { if (GameState.LocalMap.Location as BanditCamp != null) { var banditCamp = GameState.LocalMap.Location as BanditCamp; banditCamp.BanditTotal = GameState.LocalMap.Actors.Count(a => a.IsActive && a.IsAlive && !a.IsPlayerCharacter && a.EnemyData != null && a.EnemyData.Profession == ActorProfession.WARRIOR); //Has it been cleared? if (banditCamp.BanditTotal == 0) { //Find the item var campItem = GameState.GlobalMap.CampItems.FirstOrDefault(ci => ci.Camp == (GameState.LocalMap.Location as BanditCamp)); if (campItem != null) { campItem.IsActive = false; GameState.GlobalMap.CampItems.Remove(campItem); //Also find the coordinate of the camp, grab a circle around it and remove the owner var mapblocks = GameState.GlobalMap.GetBlocksAroundPoint(campItem.Coordinate, WorldGenerationManager.BANDIT_CLAIMING_RADIUS); foreach (var block in mapblocks) { var tile = (block.Tile as GlobalTile); //Owned by bandit if (tile.Owner == 50) { tile.RemoveOwner(); } } //Yes. Let's clear the camp GameState.NewLog.Add(new LogFeedback(InterfaceSpriteName.SWORD, Color.Black, "You drive the bandits away from the camp")); } } } else if (GameState.LocalMap.Location as MapSite != null) { var site = GameState.LocalMap.Location as MapSite; site.SiteData.ActorCounts.Clear(); foreach (var actorProfession in (ActorProfession[])Enum.GetValues(typeof(ActorProfession))) { int count = GameState.LocalMap.Actors.Count(a => a.IsActive && a.IsAlive && !a.IsPlayerCharacter && a.EnemyData != null && a.EnemyData.Profession == actorProfession); site.SiteData.ActorCounts.Add(actorProfession, count); } if (site.SiteData.ActorCounts[ActorProfession.WARRIOR] == 0) { //Out of warriors, abandon it. We'll decide who really owns it later site.SiteData.OwnerChanged = true; site.SiteData.MapRegenerationRequired = true; site.SiteData.Owners = OwningFactions.ABANDONED; site.SiteData.ActorCounts = new Dictionary<ActorProfession, int>(); } } } //Serialise the old map GameState.LocalMap.SerialiseLocalMap(); //Clear the stored location items GameState.LocalMap.Location = null; LoadGlobalMap(GameState.PlayerCharacter.GlobalCoordinates); GameState.LocalMap.IsGlobalMap = true; } else if (lce.RandomEncounter != null) { //Get the biome LoadRandomEncounter(lce.RandomEncounter.Value); } } else if (feedback.GetType().Equals(typeof(DropItemFeedback))) { DropItemFeedback dif = feedback as DropItemFeedback; //Drop the item underneath the player GameState.LocalMap.GetBlockAtCoordinate(dif.ItemToDrop.Coordinate).PutItemUnderneathOnBlock(dif.ItemToDrop); //Remove from inventory dif.ItemToDrop.InInventory = false; GameState.PlayerCharacter.Inventory.Inventory.Remove(dif.ItemToDrop.Category, dif.ItemToDrop); } else if (feedback.GetType().Equals(typeof(TimePassFeedback))) { TimePassFeedback tpf = feedback as TimePassFeedback; //Move time forth GameState.IncrementGameTime(DRTimeComponent.MINUTE, tpf.TimePassInMinutes); //Is the character dead? if (!GameState.PlayerCharacter.IsAlive) { var gameEvent = EventHandlingManager.CreateEvent("Hunger Death"); //Create the actual control interfaceComponents.Add(new DecisionPopupComponent(PlayableWidth / 2 - 150, PlayableHeight / 2 - 150, gameEvent)); } } else if (feedback.GetType().Equals(typeof(VisitedBlockFeedback))) { VisitedBlockFeedback vbf = feedback as VisitedBlockFeedback; //Visit a region equal to the line of sight of the player character - var blocks = GameState.LocalMap.GetBlocksAroundPoint(vbf.Coordinate, GameState.PlayerCharacter.LineOfSight); //Only do the ones which can be ray traced foreach (var block in RayTracingHelper.RayTraceForExploration(blocks, GameState.PlayerCharacter.MapCharacter.Coordinate)) { block.WasVisited = true; } } else if (feedback.GetType().Equals(typeof(DescendDungeonFeedback))) { DescendDungeonFeedback ddf = feedback as DescendDungeonFeedback; (GameState.LocalMap.Location as Dungeon).DifficultyLevel++; this.LoadLocation(GameState.LocalMap.Location, true); } } //Update the log control log.UpdateLog(); }
public override void Update(GameTime gameTime) { //This fixes an issue in monogame with resizing graphics.ApplyChanges(); //Is the user asking to quit? if (saveAndQuit) { //Go to loading. It'll open the main menu when it's done BaseGame.requestedInternalAction = InternalActionEnum.CONTINUE; BaseGame.requestedArgs = new object[1] { "Save" }; saveAndQuit = false; } // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) game.Exit(); //Only check if the game is actually active and not busy if (Game.IsActive && !GameState.IsRunningHeavyProcessing) { //Lets see if there are any keyboard keys being pressed KeyboardState keyboardState = Keyboard.GetState(); //Has the user pressed esc? if (keyboardState.IsKeyDown(Keys.Escape)) { GameState.NewLog.Add(new LogFeedback(InterfaceSpriteName.BANNER_GREEN, Color.White, "Saving Game Please Wait...")); saveAndQuit = true; } bool shiftHeld = keyboardState.IsKeyDown(Keys.LeftShift) || keyboardState.IsKeyDown(Keys.RightShift); //has the user pressed one of the directional keys? //If yes, try to move //If shift was held, then we wait for less, so game moves faster if (gameTime.TotalGameTime.TotalMilliseconds - previousGameTime < GAMEINPUTDELAY / (shiftHeld ? 2 : 1)) { //do nothing } else { //is there a component which wants to handle the keyboard movement? ActionType? kAction = null; object[] kArgs = null; MapCoordinate kTargetCoord = null; bool keyHandled = false; bool destroy = false; //Do it upside down, so the stuff which appears on top happens first for (int i = interfaceComponents.Count - 1; i >= 0; i--) { var interfaceComponent = interfaceComponents[i]; keyHandled = interfaceComponent.HandleKeyboard(keyboardState, out kAction, out kArgs, out kTargetCoord, out destroy); //do we destroy? if (destroy) { interfaceComponents.RemoveAt(i); } if (keyHandled) { //Break out of the loop - someone handled it break; } } if (kAction != null) { this.PerformAction(kTargetCoord, null, kAction.Value, kArgs); } //did we do anything? if (keyHandled) { } else { //Lets see if tab is held down if (keyboardState.IsKeyDown(Keys.Tab)) { //Zoom out a bit TILEWIDTH = MINTILEWIDTH; TILEHEIGHT = MINTILEHEIGHT; } else { //Let's animate it :) //Leave it zoomed in TILEWIDTH = MAXTILEWIDTH; TILEHEIGHT = MAXTILEHEIGHT; } //not handled, lets walk //where is the user? MapCoordinate coord = UserInterfaceManager.GetPlayerActor().MapCharacter.Coordinate; MapCoordinate difference = new MapCoordinate(0, 0, 0, coord.MapType); //we will either increase or decrease it by an amount depending on the directional key pressed if (keyboardState.IsKeyDown(Keys.OemPeriod)) { //Just waste time this.PerformAction(null, null, ActionType.IDLE, null); } else if (keyboardState.IsKeyDown(Keys.Up)) { difference = new MapCoordinate(0, 1, 0, coord.MapType); } else if (keyboardState.IsKeyDown(Keys.Down)) { difference = new MapCoordinate(0, -1, 0, coord.MapType); } else if (keyboardState.IsKeyDown(Keys.Left)) { difference = new MapCoordinate(-1, 0, 0, coord.MapType); } else if (keyboardState.IsKeyDown(Keys.Right)) { difference = new MapCoordinate(1, 0, 0, coord.MapType); } //The fact that they'er not the same means the user pressed a key, lets move if (!difference.Equals(new MapCoordinate(0, 0, 0, coord.MapType))) { //add the difference to the coordinate coord += difference; //send a move message to that coordinate this.PerformAction(coord, null, DRObjects.Enums.ActionType.MOVE, null); } } //mark the current time previousGameTime = (int)gameTime.TotalGameTime.TotalMilliseconds; } #region Mouse Handling //See what the mouse is doing MouseState mouse = Mouse.GetState(); MouseActionEnum? mouseAction = this.DetermineMouseAction(mouse); //this is a potential mouse action ActionType? action = null; object[] args = null; MapCoordinate targetCoord = null; MapItem item = null; //see if there is a component which will handle it instead //Is the mouse over a particular component ? foreach (var component in interfaceComponents.Where(ic => ic.Visible)) { if (component.ReturnLocation().Contains(mouse.X, mouse.Y)) { //Mouse over trigger component.HandleMouseOver(mouse.X, mouse.Y); } } if (mouseAction != null) { bool mouseHandled = false; InternalActionEnum? internalAction = null; object[] arg = null; foreach (var menuButton in menuButtons) { if (menuButton.ReturnLocation().Contains(new Point(mouse.X, mouse.Y)) && mouseAction == MouseActionEnum.LEFT_CLICK && menuButton.HandleClick(mouse.X, mouse.Y, out internalAction, out arg)) { mouseHandled = true; //don't get into the other loop break; //break out } } //Are we dragging something? if (isDragging) { //Check the location we're at int deltaX = mouse.X - dragPointX; int deltaY = mouse.Y - dragPointY; //Drag it dragItem.PerformDrag(deltaX, deltaY); //Update the dragpoints dragPointX = deltaX; dragPointY = deltaY; //are we still dragging? if (mouseAction.Value != MouseActionEnum.DRAG) { //Nope isDragging = false; } } //Do we have a MODAL interface component? var modalComponent = interfaceComponents.Where(ic => ic.IsModal()).FirstOrDefault(); if (modalComponent != null) { //Force it to handle it bool destroy = false; modalComponent.HandleClick(mouse.X, mouse.Y, mouseAction.Value, out action, out internalAction, out args, out item, out targetCoord, out destroy); if (destroy) { //Destroy it interfaceComponents.Remove(modalComponent); } //It's handled mouseHandled = true; } for (int i = interfaceComponents.Count - 1; i >= 0; i--) { if (mouseHandled) { break; } var interfaceComponent = interfaceComponents[i]; //is the click within this interface's scope? or is it modal? Point mousePoint = new Point(mouse.X, mouse.Y); if (interfaceComponent.IsModal() || interfaceComponent.ReturnLocation().Contains(mousePoint) && interfaceComponent.Visible) { bool destroy; //Are we dragging? if (mouseAction.Value == MouseActionEnum.DRAG) { //Yes this.dragPointX = mouse.X; this.dragPointY = mouse.Y; isDragging = true; dragItem = interfaceComponent; mouseHandled = true; //insert the item again interfaceComponents.RemoveAt(i); interfaceComponents.Add(interfaceComponent); break; //break out } //see if the component can handle it mouseHandled = interfaceComponent.HandleClick(mouse.X, mouse.Y, mouseAction.Value, out action, out internalAction, out args, out item, out targetCoord, out destroy); if (destroy) { //destroy the component interfaceComponents.RemoveAt(i); i++; } if (mouseHandled) { //Get out of the loop - someone has handled it break; } } } //dispatch the action, if any if (action.HasValue) { this.PerformAction(targetCoord, item, action.Value, args); } //Dispatch the internal action, if any if (internalAction.HasValue) { //Let's do it here switch (internalAction.Value) { case InternalActionEnum.OPEN_HEALTH: //Toggle the health var health = this.interfaceComponents.Where(ic => ic.GetType().Equals(typeof(HealthDisplayComponent))).FirstOrDefault(); health.Visible = !health.Visible; break; case InternalActionEnum.OPEN_ATTRIBUTES: //Toggle the attributes var att = this.interfaceComponents.Where(ic => ic.GetType().Equals(typeof(CharacterSheetComponent))).FirstOrDefault(); att.Visible = !att.Visible; break; case InternalActionEnum.TOGGLE_SETTLEMENT: //Toggle the settlements var loc = this.interfaceComponents.Where(ic => ic.GetType().Equals(typeof(LocationDetailsComponent))).FirstOrDefault(); loc.Visible = !loc.Visible; break; case InternalActionEnum.OPEN_LOG: //Toggle the log var log = this.interfaceComponents.Where(ic => ic.GetType().Equals(typeof(TextLogComponent))).FirstOrDefault(); log.Visible = !log.Visible; break; case InternalActionEnum.OPEN_INVENTORY: //Toggle inventory var inv = this.interfaceComponents.Where(ic => ic.GetType().Equals(typeof(InventoryDisplayComponent))).FirstOrDefault(); inv.Visible = !inv.Visible; break; case InternalActionEnum.LOSE: //For now, just go back to the main menu BaseGame.requestedInternalAction = InternalActionEnum.EXIT; BaseGame.requestedArgs = new object[0]; break; case InternalActionEnum.MULTIDECISION: //The only thing that has multidecisions right now is char creation //So let's do it dirty for now CharacterCreation.ProcessParameters(args[1] as List<string>); break; //TODO: THE REST } } //do we continue? if (mouseHandled) { } else { //the grid will handle it //determine where we clicked int xCord = (int)(mouse.X / TILEWIDTH); int yCord = (int)(mouse.Y / TILEHEIGHT); //get the game coordinate from the interface coordinate InterfaceBlock iBlock = blocks.FirstOrDefault(b => b.InterfaceX.Equals(xCord) && b.InterfaceY.Equals(yCord)); //if it was a left click, we look if (mouseAction.Value == MouseActionEnum.LEFT_CLICK) { if (iBlock != null) { this.PerformAction(iBlock.MapCoordinate, null, DRObjects.Enums.ActionType.LOOK, null); } } else if (mouseAction.Value == MouseActionEnum.RIGHT_CLICK) { if (iBlock != null) { ActionType[] actions = UserInterfaceManager.GetPossibleActions(iBlock.MapCoordinate); //we are going to get a context menu //Check for other context menus and remove them - we can only have one for (int i = 0; i < interfaceComponents.Count; i++) { if (interfaceComponents[i].GetType().Equals(typeof(ContextMenuComponent))) { //remove it interfaceComponents.RemoveAt(i); i--; } } ContextMenuComponent comp = new ContextMenuComponent(mouse.X + 10, mouse.Y, iBlock.MapCoordinate); foreach (ActionType act in actions) { comp.AddContextMenuItem(act, null, this.game.Content); } //And add it interfaceComponents.Add(comp); } } } } //end mouse handling this.lastLeftButtonClicked = (mouse.LeftButton == ButtonState.Pressed); this.lastRightButtonClicked = (mouse.RightButton == ButtonState.Pressed); #endregion } }
/// <summary> /// Generates a camp /// </summary> /// <returns></returns> public static MapBlock[,] GenerateCamp(int enemies, out MapCoordinate startPoint, out DRObjects.Actor[] enemyArray) { MapBlock[,] map = new MapBlock[MAP_EDGE, MAP_EDGE]; Random random = new Random(); ItemFactory.ItemFactory factory = new ItemFactory.ItemFactory(); int grassTileID = 0; factory.CreateItem(Archetype.TILES, "grass", out grassTileID); //Create a new map which is edge X edge in dimensions and made of grass for (int x = 0; x < MAP_EDGE; x++) { for (int y = 0; y < MAP_EDGE; y++) { MapBlock block = new MapBlock(); map[x, y] = block; block.Tile = factory.CreateItem("tile", grassTileID); block.Tile.Coordinate = new MapCoordinate(x, y, 0, MapType.LOCAL); } } //Now created a wall int pallisadeID = 0; factory.CreateItem(Archetype.MUNDANEITEMS, "pallisade wall lr", out pallisadeID); //Create a square of pallisade wall int startCoord = (MAP_EDGE - FORTIFICATION_EDGE) / 2; int endCoord = MAP_EDGE - ((MAP_EDGE - FORTIFICATION_EDGE) / 2); for (int x = startCoord + 1; x < endCoord; x++) { MapBlock block = map[x, startCoord]; MapItem item = factory.CreateItem("mundaneitems", pallisadeID); block.ForcePutItemOnBlock(item); block = map[x, endCoord]; item = factory.CreateItem("mundaneitems", pallisadeID); block.ForcePutItemOnBlock(item); } pallisadeID = 0; for (int y = startCoord + 1; y < endCoord; y++) { factory.CreateItem(Archetype.MUNDANEITEMS, "pallisade wall tb", out pallisadeID); MapBlock block = map[startCoord, y]; MapItem item = factory.CreateItem("mundaneitems", pallisadeID); block.ForcePutItemOnBlock(item); factory.CreateItem(Archetype.MUNDANEITEMS, "pallisade wall bt", out pallisadeID); block = map[endCoord, y]; item = factory.CreateItem("mundaneitems", pallisadeID); block.ForcePutItemOnBlock(item); } //We need to poke a hole in wall as an entrance //Let's poke one at the top and one at the bottom int center = MAP_EDGE / 2; int rValue = GameState.Random.Next(2); for (int x = -1; x < 2; x++) { if (rValue == 1) { MapBlock block = map[center + x, startCoord]; block.RemoveTopItem(); } else { MapBlock block = map[center + x, endCoord]; block.RemoveTopItem(); } } rValue = GameState.Random.Next(2); for (int y = -1; y < 2; y++) { if (rValue == 1) { MapBlock block = map[startCoord, y + center]; block.RemoveTopItem(); } else { MapBlock block = map[endCoord, y + center]; block.RemoveTopItem(); } } //Now, let's create some maplets in there //There's a single maplet containing the other maplets - let's get it LocalMapXMLParser lm = new LocalMapXMLParser(); Maplet maplet = lm.ParseMapletFromTag("camp"); LocalMapGenerator gen = new LocalMapGenerator(); //TODO: LATER THE OWNER MIGHT NOT BE A BANDIT MapletActorWanderArea[] wanderAreas = null; MapletPatrolPoint[] patrolPoints = null; MapletFootpathNode[] footPath = null; var gennedMap = gen.GenerateMap(grassTileID, null, maplet, false, "", OwningFactions.BANDITS, out enemyArray, out wanderAreas, out patrolPoints,out footPath); gen.JoinMaps(map, gennedMap, startCoord + 1, startCoord + 1); //Let's add some trees and stuff int decorCount = (int)(map.GetLength(1) * 0.50); //Just find as many random points and if it happens to be grass, drop them int itemID = 0; for (int i = 0; i < decorCount; i++) { //Just trees MapItem decorItem = factory.CreateItem(Archetype.MUNDANEITEMS, "tree", out itemID); //Pick a random point MapBlock randomBlock = map[random.Next(map.GetLength(0)), random.Next(map.GetLength(1))]; //Make sure its not inside the camp if (randomBlock.Tile.Coordinate.X >= startCoord && randomBlock.Tile.Coordinate.X <= endCoord && randomBlock.Tile.Coordinate.Y >= startCoord && randomBlock.Tile.Coordinate.Y <= endCoord) { //Not within the camp i--; continue; //try again } if (randomBlock.MayContainItems && randomBlock.Tile.Name == "Grass") { //Yes, can support it randomBlock.ForcePutItemOnBlock(decorItem); } //Otherwise forget all about it } //Now select all the border tiles and put in a "Exit here" border for (int x = 0; x < map.GetLength(0); x++) { MapCoordinate coo = new MapCoordinate(x, 0, 0, MapType.LOCAL); LeaveTownItem lti = new LeaveTownItem(); lti.Coordinate = coo; lti.Description = "path outside the town"; lti.Name = "Leave Town"; lti.Coordinate = coo; map[x, 0].ForcePutItemOnBlock(lti); coo = new MapCoordinate(x, map.GetLength(1) - 1, 0, MapType.LOCAL); lti = new LeaveTownItem(); lti.Coordinate = coo; lti.Description = "path outside the town"; lti.Name = "Leave Town"; lti.Coordinate = coo; map[x, map.GetLength(1) - 1].ForcePutItemOnBlock(lti); } for (int y = 0; y < map.GetLength(1); y++) { MapCoordinate coo = new MapCoordinate(0, y, 0, MapType.LOCAL); LeaveTownItem lti = new LeaveTownItem(); lti.Coordinate = coo; lti.Description = "path outside the town"; lti.Name = "Leave Town"; lti.Coordinate = coo; map[0, y].ForcePutItemOnBlock(lti); coo = new MapCoordinate(map.GetLength(0) - 1, y, 0, MapType.LOCAL); lti = new LeaveTownItem(); lti.Coordinate = coo; lti.Description = "path outside the town"; lti.Name = "Town Borders"; lti.Coordinate = coo; map[map.GetLength(0) - 1, y].ForcePutItemOnBlock(lti); } #region Treasure Room //This is a bit naughty. We need to locate where the tiles become soil MapCoordinate soilStart = null; bool breakOut = false; for (int x = 0; x < map.GetLength(0); x++) { for (int y = 0; y < map.GetLength(1); y++) { if (map[x, y].Tile.Name.ToLower().Equals("soil")) { soilStart = new MapCoordinate(x, y, 0, MapType.LOCAL); breakOut = true; break; } } if (breakOut) { break; } } //Also naughty, we know it's 5x5 #region Inner Wall for (int x = 0; x < 5; x++) { factory.CreateItem(Archetype.MUNDANEITEMS, "pallisade wall lr", out pallisadeID); MapItem item = factory.CreateItem("mundaneitems", pallisadeID); if (x != 2) //hole in the top { map[soilStart.X + x, soilStart.Y].ForcePutItemOnBlock(item); } factory.CreateItem(Archetype.MUNDANEITEMS, "pallisade wall lr", out pallisadeID); item = factory.CreateItem("mundaneitems", pallisadeID); map[soilStart.X + x, soilStart.Y + 5].ForcePutItemOnBlock(item); } for (int y = 0; y < 5; y++) { factory.CreateItem(Archetype.MUNDANEITEMS, "pallisade wall tb", out pallisadeID); MapItem item = factory.CreateItem("mundaneitems", pallisadeID); map[soilStart.X - 1, soilStart.Y + y].ForcePutItemOnBlock(item); factory.CreateItem(Archetype.MUNDANEITEMS, "pallisade wall bt", out pallisadeID); item = factory.CreateItem("mundaneitems", pallisadeID); map[soilStart.X + 5, soilStart.Y + y].ForcePutItemOnBlock(item); } #endregion #endregion #region Patrol Points //Now let's collect the patrol points. We're going to have two possible patrols - one around each of the entrances - and another on the outside corners of the map List<MapCoordinate> outsidePatrol = new List<MapCoordinate>(); outsidePatrol.Add(new MapCoordinate(startCoord - 2, startCoord, 0, MapType.LOCAL)); outsidePatrol.Add(new MapCoordinate(endCoord + 2, startCoord, 0, MapType.LOCAL)); outsidePatrol.Add(new MapCoordinate(endCoord + 2, endCoord, 0, MapType.LOCAL)); outsidePatrol.Add(new MapCoordinate(startCoord - 2, endCoord, 0, MapType.LOCAL)); List<MapCoordinate> insidePatrol = new List<MapCoordinate>(); insidePatrol.Add(new MapCoordinate(center, startCoord + 1, 0, MapType.LOCAL)); insidePatrol.Add(new MapCoordinate(center, endCoord - 1, 0, MapType.LOCAL)); insidePatrol.Add(new MapCoordinate(startCoord + 1, center, 0, MapType.LOCAL)); insidePatrol.Add(new MapCoordinate(endCoord - 1, center, 0, MapType.LOCAL)); //Go through all of those and make sure they're clear of anything that wouldn't let them walk upon them foreach (var coordinate in outsidePatrol) { map[coordinate.X, coordinate.Y].RemoveTopItem(); } foreach (var coordinate in insidePatrol) { map[coordinate.X, coordinate.Y].RemoveTopItem(); } #endregion #region Actors enemyArray = CreateBandits(enemies, outsidePatrol.Select(op => new PatrolPoint() { AcceptableRadius = 2, Coordinate = op }).ToList(), insidePatrol.Select(op => new PatrolPoint() { AcceptableRadius = 2, Coordinate = op }).ToList()); int tries = 0; //Put them on the mappity map for (int i = 0; i < enemyArray.Length; i++) { Actor actor = enemyArray[i]; int randomX = random.Next(map.GetLength(0)); int randomY = random.Next(map.GetLength(1)); if (map[randomX, randomY].MayContainItems) { //Plop it on there actor.MapCharacter.Coordinate = new MapCoordinate(randomX, randomY, 0, MapType.LOCAL); map[randomX, randomY].ForcePutItemOnBlock(actor.MapCharacter); tries = 0; ////If they are wandering, make them wander in the right place //var mission = actor.MissionStack.Peek(); //if (mission.MissionType == ActorMissionType.WANDER) //{ // var wander = mission as WanderMission; // wander.WanderPoint = new MapCoordinate(actor.MapCharacter.Coordinate); // wander.WanderRectangle = new Rectangle(startCoord, startCoord, FORTIFICATION_EDGE, FORTIFICATION_EDGE); //} } else { tries++; i--; } if (tries >= 50) { //give up continue; } } #endregion startPoint = new MapCoordinate(map.GetLength(0) / 2, 0, 0, MapType.LOCAL); return map; }
/// <summary> /// Performs an action on a particular or a particular item. /// If item is not null, will use the item. Otherwise will use the coordinate /// </summary> /// <param name="coordinate"></param> /// <param name="actionType"></param> /// <param name="args"></param> public static ActionFeedback[] PerformAction(MapCoordinate coordinate,MapItem item, ActionType actionType, object[] args) { List<ActionFeedback> feedback = new List<ActionFeedback>(); bool validAttack = false; if (actionType == ActionType.ATTACK) { //Handle this seperatly //Argument 0 - attacker //Argument 1 - target //Argument 2 - Body part to attack //Argument 3 - Special attack if there //Are we in the right place? Actor attacker = args[0] as Actor; Actor defender = args[1] as Actor; AttackLocation location = (AttackLocation) args[2]; int distance = attacker.MapCharacter.Coordinate - defender.MapCharacter.Coordinate; if (defender.MapCharacter == null) { //Something went wrong validAttack = false; } if ( distance < 2) { //Hand to hand if (args.Length > 3) { //Special attack! feedback.AddRange(CombatManager.PerformSpecialAttack(attacker, defender, args[3] as SpecialAttack)); } else { feedback.AddRange(CombatManager.Attack(attacker, defender, location)); } validAttack = true; //perform the tick } else { //Is the attacker armed properly? if (attacker.Inventory.EquippedItems.ContainsKey(EquipmentLocation.BOW)) { //Do they have line of sight? if (GameState.LocalMap.HasDirectPath(attacker.MapCharacter.Coordinate, defender.MapCharacter.Coordinate)) { //Are they within a reasonable distance? if (distance <= attacker.LineOfSight) { //Yes! if (args.Length > 3) { //Special attack! feedback.AddRange(CombatManager.PerformSpecialAttack(attacker, defender, args[3] as SpecialAttack)); } else { feedback.AddRange(CombatManager.Attack(attacker, defender, location)); } validAttack = true; } else { validAttack = false; return new ActionFeedback[] { new LogFeedback(InterfaceSpriteName.PERC,Color.Black, "This target is outside of your range") }; } } else { validAttack = false; return new ActionFeedback[] { new LogFeedback(null, Color.Black, "You don't have a clear line of sight to the target") }; } } else { validAttack = false; //Invalid - no tick return new ActionFeedback[] { new LogFeedback(null, Color.Black, "You are too far away to hit your target") }; } } } else if (actionType == ActionType.THROW_ITEM) { //Throw it Potion potion = args[2] as Potion; Actor attacker = args[0] as Actor; List<Actor> victims = args[1] as List<Actor>; //Also create a temporary icon to show what was done foreach(var victim in victims) { TemporaryGraphic tg = new TemporaryGraphic() { Coord = victim.MapCharacter.Coordinate, Graphic = SpriteManager.GetSprite(InterfaceSpriteName.POTION_BREAK), LifeTime = 2 }; GameState.LocalMap.TemporaryGraphics.Add(tg); } feedback.AddRange(potion.ThrowUpon(attacker, victims)); } else if (actionType == ActionType.IDLE) { //Do nothing } else { if (item == null) { switch (coordinate.MapType) { case MapType.LOCAL: feedback.AddRange(GameState.LocalMap.GetBlockAtCoordinate(coordinate).PerformAction(actionType, GameState.PlayerCharacter, args)); break; case MapType.GLOBAL: feedback.AddRange(GameState.GlobalMap.GetBlockAtCoordinate(coordinate).PerformAction(actionType, GameState.PlayerCharacter, args)); break; default: throw new NotImplementedException("There is no support for that particular maptype"); } } else { //Perform it on the item feedback.AddRange(item.PerformAction(actionType, GameState.PlayerCharacter, args)); } } if (actionType == ActionType.EXAMINE || actionType == ActionType.THROW_ITEM || actionType == ActionType.MOVE || (actionType == ActionType.ATTACK && validAttack) || actionType == ActionType.IDLE) { //Perform a tick //Clear the Log GameState.NewLog.Clear(); feedback.AddRange(UserInterfaceManager.PerformLocalTick()); } //Is the player stunned? while (GameState.PlayerCharacter.IsStunned && GameState.PlayerCharacter.IsAlive) { feedback.AddRange(UserInterfaceManager.PerformLocalTick()); } return feedback.ToArray(); }
/// <summary> /// Gets a number of graphical blocks around a particular point. /// The order will be as follows: /// first max z, then max x, then max y. /// </summary> /// <param name="centrePoint">The center point around which the tiles will be calculated</param> /// <param name="xRange">How many tiles away from the centre point on the x axis will be obtained</param> /// <param name="yCount">How many tiles away from the centre point on the y axis will be obtained</param> /// <param name="zCount">How many tiles away from the centre point on the y axis will be obtained</param> /// <param name="overlay">If its a global map, the overlay to apply on it</param> /// <returns></returns> public static GraphicalBlock[] GetBlocksAroundPoint(MapCoordinate centrePoint, int xRange, int yRange, int zRange,GlobalOverlay overlay = GlobalOverlay.NONE) { int minZ = centrePoint.Z - Math.Abs(zRange); int maxZ = centrePoint.Z + Math.Abs(zRange); int minY = centrePoint.Y - Math.Abs(yRange); int maxY = centrePoint.Y + Math.Abs(yRange); int minX = centrePoint.X - Math.Abs(xRange); int maxX = centrePoint.X + Math.Abs(xRange); List<GraphicalBlock> returnList = new List<GraphicalBlock>(); //go through all of them for (int zLoop = maxZ; zLoop >= minZ; zLoop--) { for (int yLoop = maxY; yLoop >= minY; yLoop--) { for (int xLoop = minX; xLoop <= maxX; xLoop++) { MapCoordinate coord = new MapCoordinate(xLoop, yLoop, zLoop, centrePoint.MapType); if (overlay != GlobalOverlay.NONE) { returnList.Add(GetBlockAtPoint(coord, overlay)); } else { returnList.Add(GetBlockAtPoint(coord)); } } } } return returnList.ToArray(); }
/// <summary> /// Puts settlement items on a particular location pertianing to a particular settlement. /// </summary> /// <param name="capital">Whether it's the capital or not</param> /// <param name="settlement">The settlement which it represents</param> /// <param name="block">The block making up the center</param> private static SettlementItem CreateSettlement(bool capital, Settlement settlement, MapBlock block, int owner) { //Put an entire group of SettlementItems on it MapCoordinate[] settlementCoordinates = new MapCoordinate[10]; SettlementItem retItem = null; settlementCoordinates[7] = new MapCoordinate(block.Tile.Coordinate.X - 1, block.Tile.Coordinate.Y - 1, 0, MapType.GLOBAL); settlementCoordinates[8] = new MapCoordinate(block.Tile.Coordinate.X, block.Tile.Coordinate.Y - 1, 0, MapType.GLOBAL); settlementCoordinates[9] = new MapCoordinate(block.Tile.Coordinate.X + 1, block.Tile.Coordinate.Y - 1, 0, MapType.GLOBAL); settlementCoordinates[4] = new MapCoordinate(block.Tile.Coordinate.X - 1, block.Tile.Coordinate.Y, 0, MapType.GLOBAL); settlementCoordinates[5] = new MapCoordinate(block.Tile.Coordinate.X, block.Tile.Coordinate.Y, 0, MapType.GLOBAL); settlementCoordinates[6] = new MapCoordinate(block.Tile.Coordinate.X + 1, block.Tile.Coordinate.Y, 0, MapType.GLOBAL); settlementCoordinates[1] = new MapCoordinate(block.Tile.Coordinate.X - 1, block.Tile.Coordinate.Y + 1, 0, MapType.GLOBAL); settlementCoordinates[2] = new MapCoordinate(block.Tile.Coordinate.X, block.Tile.Coordinate.Y + 1, 0, MapType.GLOBAL); settlementCoordinates[3] = new MapCoordinate(block.Tile.Coordinate.X + 1, block.Tile.Coordinate.Y + 1, 0, MapType.GLOBAL); //Block the radius around it from colonising MapBlock[] regionalBlocks = GetBlocksAroundPoint(block.Tile.Coordinate, HUMAN_COLONY_BLOCKING_RADIUS); foreach (MapBlock rblock in regionalBlocks) { (rblock.Tile as GlobalTile).IsBlockedForColonisation = true; } //Claim the land around it MapBlock[] claimedBlocks = GetBlocksAroundPoint(block.Tile.Coordinate, HUMAN_COLONY_CLAIMING_RADIUS); foreach (MapBlock rblock in claimedBlocks) { //This is a disputed region. For now let's not allow creep. //Later this might be cause for war if (!(rblock.Tile as GlobalTile).Owner.HasValue) { (rblock.Tile as GlobalTile).Owner = owner; } } for (int corner = 1; corner < 10; corner++) { var cornerBlock = GameState.GlobalMap.GetBlockAtCoordinate(settlementCoordinates[corner]); //Cut any forests down switch ((cornerBlock.Tile as GlobalTile).Biome) { case GlobalBiome.DENSE_FOREST: (cornerBlock.Tile as GlobalTile).Biome = GlobalBiome.GRASSLAND; break; case GlobalBiome.WOODLAND: (cornerBlock.Tile as GlobalTile).Biome = GlobalBiome.GRASSLAND; break; case GlobalBiome.POLAR_FOREST: (cornerBlock.Tile as GlobalTile).Biome = GlobalBiome.POLAR_DESERT; break; } var settlementItem = new SettlementItem() { Coordinate = settlementCoordinates[corner], IsCapital = capital, MayContainItems = true, SettlementCorner = corner, SettlementSize = settlement.SettlementSize, Description = (capital ? "the capital of " + settlement.Name : "the settlement of " + settlement.Name) + " owned by " + settlement.Civilisation.Name, Name = settlement.Name, Settlement = settlement, OwnerID = owner }; GameState.GlobalMap.GetBlockAtCoordinate(settlementCoordinates[corner]) .ForcePutItemOnBlock(settlementItem); if (settlementItem.SettlementCorner == 5) //center { retItem = settlementItem; } } return retItem; }
/// <summary> /// Creates a default map item at a current location /// </summary> /// <param name="coordinate"></param> public MapItem(MapCoordinate coordinate) { this.Coordinate = coordinate; this.Graphics = new List<SpriteData>(); }
/// <summary> /// Generates a map with a particular biome /// </summary> /// <param name="herdAmount">The total amount of herds to generate</param> /// <param name="BanditAmount">The total amount of bandits to generate.</param> /// <param name="actors"></param> /// <returns></returns> public static MapBlock[,] GenerateMap(GlobalBiome biome, int herdAmount, int banditAmount, out Actor[] actors, out MapCoordinate startPoint) { MapBlock[,] map = new MapBlock[MAP_EDGE, MAP_EDGE]; Random random = new Random(); ItemFactory.ItemFactory factory = new ItemFactory.ItemFactory(); int tileID = 0; factory.CreateItem(Archetype.TILES, details[biome].BaseTileTag, out tileID); //Create a new map which is edge X edge in dimensions and made of the base tile for (int x = 0; x < MAP_EDGE; x++) { for (int y = 0; y < MAP_EDGE; y++) { MapBlock block = new MapBlock(); map[x, y] = block; block.Tile = factory.CreateItem("tile", tileID); block.Tile.Coordinate = new MapCoordinate(x, y, 0, MapType.LOCAL); } } #region Leave Town Item //Now select all the border tiles and put in a "Exit here" border for (int x = 0; x < map.GetLength(0); x++) { MapCoordinate coo = new MapCoordinate(x, 0, 0, MapType.LOCAL); LeaveTownItem lti = new LeaveTownItem(); lti.Coordinate = coo; lti.Description = "continue on your journey"; lti.Name = "Leave Area"; lti.Coordinate = coo; map[x, 0].ForcePutItemOnBlock(lti); coo = new MapCoordinate(x, map.GetLength(1) - 1, 0, MapType.LOCAL); lti = new LeaveTownItem(); lti.Coordinate = coo; lti.Description = "continue on your journey"; lti.Name = "Leave Area"; lti.Coordinate = coo; map[x, map.GetLength(1) - 1].ForcePutItemOnBlock(lti); } for (int y = 0; y < map.GetLength(1); y++) { MapCoordinate coo = new MapCoordinate(0, y, 0, MapType.LOCAL); LeaveTownItem lti = new LeaveTownItem(); lti.Coordinate = coo; lti.Description = "continue on your journey"; lti.Name = "Leave Area"; lti.Coordinate = coo; map[0, y].ForcePutItemOnBlock(lti); coo = new MapCoordinate(map.GetLength(0) - 1, y, 0, MapType.LOCAL); lti = new LeaveTownItem(); lti.Coordinate = coo; lti.Description = "continue on your journey"; lti.Name = "Leave Area"; lti.Coordinate = coo; map[map.GetLength(0) - 1, y].ForcePutItemOnBlock(lti); } #endregion #region Desert Oasis if (biome == GlobalBiome.ARID_DESERT) { //Let's create a pool of water towards one of the corners. int randomNumber = random.Next(2); int rXCoord = 0; if (randomNumber == 1) { //Lower rXCoord = random.Next(0, MAP_EDGE / 3); } else { rXCoord = random.Next(2 * MAP_EDGE / 3, MAP_EDGE); } randomNumber = random.Next(2); int rYCoord = 0; if (randomNumber == 1) { //Lower rYCoord = random.Next(0, MAP_EDGE / 3); } else { rYCoord = random.Next(2 * MAP_EDGE / 3, MAP_EDGE); } //The pool will have a radius of 3 MapCoordinate coo = new MapCoordinate(rXCoord, rYCoord, 0, MapType.LOCAL); //Go through the blocks with a radius of 3 var oasisBlocks = map.Cast<MapBlock>().ToArray().Where(b => Math.Abs(b.Tile.Coordinate - coo) <= 3).ToArray(); int waterTile = -1; factory.CreateItem(Archetype.TILES, "water", out waterTile); foreach (var block in oasisBlocks) { var coord = block.Tile.Coordinate; block.Tile = factory.CreateItem("tile", waterTile); block.Tile.Coordinate = coord; } var aroundOasis = map.Cast<MapBlock>().ToArray().Where(b => Math.Abs(b.Tile.Coordinate - coo) <= 4 && Math.Abs(b.Tile.Coordinate - coo) > 3).ToArray(); int dummy; int grassTile = 0; factory.CreateItem(Archetype.TILES, "grass", out grassTile); foreach (var block in aroundOasis) { var coord = block.Tile.Coordinate; block.Tile = factory.CreateItem("tile", grassTile); block.Tile.Coordinate = coord; } //Put in some trees around the pool for (int i = 0; i < 3; i++) { MapItem tree = factory.CreateItem(Archetype.MUNDANEITEMS, "jungle tree", out dummy); var block = aroundOasis[random.Next(aroundOasis.Length)]; if (block.MayContainItems) { //Drop it block.ForcePutItemOnBlock(tree); } } } #endregion #region Wetland Splotches if (biome == GlobalBiome.WETLAND) { int waterTile = -1; factory.CreateItem(Archetype.TILES, "water", out waterTile); for (int i=0; i < 7; i++) { MapBlock rBlock = map[random.Next(map.GetLength(0)), random.Next(map.GetLength(1))]; Rectangle safeRect = new Rectangle( MAP_EDGE/2 - 5,MAP_EDGE/2 -5,10,10); if (safeRect.Contains(rBlock.Tile.Coordinate.X,rBlock.Tile.Coordinate.Y)) { continue; //Not here! } int size = random.Next(1, 3); //Get all the tiles around the block for a particular size var pool = map.Cast<MapBlock>().ToArray().Where(b => Math.Abs(b.Tile.Coordinate - rBlock.Tile.Coordinate) <= size).ToArray(); foreach(var block in pool) { MapCoordinate coo = block.Tile.Coordinate; block.Tile = factory.CreateItem("tiles", waterTile); block.Tile.Coordinate = coo; } } } #endregion for (int i = 0; i < details[biome].TreeCount; i++) { int treeID = 0; MapItem item = null; item = factory.CreateItem(Archetype.MUNDANEITEMS, details[biome].TreeTag, out treeID); //try 50 times to put it somewhere int tries = 0; while (tries < 50) { MapBlock randomBlock = map[random.Next(map.GetLength(0)), random.Next(map.GetLength(1))]; if (randomBlock.MayContainItems) { randomBlock.ForcePutItemOnBlock(item); break; } tries++; } } List<Actor> actorList = new List<Actor>(); //There, now that's done, lets generate some animals if (herdAmount + banditAmount > 0) { var herds = ActorGeneration.CreateAnimalHerds(biome, false, null, herdAmount); var bandits = CampGenerator.CreateBandits(banditAmount); var actorGroups = herds.Union(bandits); //Each actor group will be placed in a random 3 radius circle foreach (var actorGroup in actorGroups) { MapBlock randomBlock = map[random.Next(map.GetLength(0)), random.Next(map.GetLength(1))]; Rectangle wanderRect = new Rectangle(randomBlock.Tile.Coordinate.X - 2, randomBlock.Tile.Coordinate.Y - 2, 4, 4); //Put the actor groups somewhere around that block var blocks = map.Cast<MapBlock>().ToArray().Where(b => Math.Abs(b.Tile.Coordinate - randomBlock.Tile.Coordinate) < 4).ToArray(); //Pick a number of random blocks foreach (var newActor in actorGroup) { int tries = 0; while (tries < 50) { var actorBlock = blocks[random.Next(blocks.Length)]; if (actorBlock.MayContainItems) { //Put it there newActor.MapCharacter.Coordinate = actorBlock.Tile.Coordinate; actorBlock.ForcePutItemOnBlock(newActor.MapCharacter); //Make them wander newActor.MissionStack.Push(new WanderMission() { LoiterPercentage = 50, WanderPoint = actorBlock.Tile.Coordinate, WanderRectangle = wanderRect }); actorList.Add(newActor); break; } tries++; } } } } //Drop the player in the thick of it MapBlock center = map[map.GetLength(0) / 2, map.GetLength(1) / 2]; center.RemoveTopItem(); //Remove it if someone else wanted it startPoint = center.Tile.Coordinate; actors = actorList.ToArray(); return map; }
public static ActionFeedback[] WanderMission(WanderMission mission, Actor actor) { MapCoordinate playerLocation = GameState.PlayerCharacter.MapCharacter.Coordinate; if (actor.UsesRanged) { //Is he a ranged user and seeing the player character if (actor.IsAggressive && actor.MapCharacter.Coordinate - playerLocation <= actor.LineOfSight) { //Attack! actor.MissionStack.Push(actor.CurrentMission); actor.CurrentMission = new AttackMission(GameState.PlayerCharacter); } } else { //Is he seeing the player character? if (actor.IsAggressive && Math.Abs(actor.MapCharacter.Coordinate - playerLocation) <= 1) { //He's there. Push the current mission into the stack and go on the attack actor.MissionStack.Push(actor.CurrentMission); actor.CurrentMission = new AttackMission(GameState.PlayerCharacter); return new ActionFeedback[] { }; } else if (actor.IsAggressive && Math.Abs(actor.MapCharacter.Coordinate - playerLocation) < actor.LineOfSight) { //He's there. Push the current mission into the stack and follow him actor.MissionStack.Push(actor.CurrentMission); actor.CurrentMission = new HuntDownMission() { Target = GameState.PlayerCharacter, TargetCoordinate = GameState.PlayerCharacter.MapCharacter.Coordinate }; return new ActionFeedback[] { }; } } //Perform an action accordingly //Is he outside of the patrol area? if (!mission.WanderRectangle.Contains(actor.MapCharacter.Coordinate.X, actor.MapCharacter.Coordinate.Y)) { //Send him back. WalkToMission walkMission = new WalkToMission(); walkMission.TargetCoordinate = mission.WanderPoint; //Push it actor.MissionStack.Push(mission); actor.CurrentMission = walkMission; } else { int randomNumber = GameState.Random.Next(100); //Do we wander? if (mission.LoiterPercentage != 0 && randomNumber < mission.LoiterPercentage) { //Nope, we loiter } else { //Walk somewhere randomly int direction = GameState.Random.Next(4); MapCoordinate coord = actor.MapCharacter.Coordinate; //Copy it MapCoordinate newCoord = new MapCoordinate(coord.X, coord.Y, coord.Z, coord.MapType); switch (direction) { case 0: //Top newCoord.Y++; break; case 1: //Bottom newCoord.Y--; break; case 2: //Right newCoord.X++; break; case 3: //Left newCoord.X--; break; } //Can we go there? if (GameState.LocalMap.GetBlockAtCoordinate(newCoord).MayContainItems && mission.WanderRectangle.Contains(newCoord.X, newCoord.Y)) { //Do it GameState.LocalMap.GetBlockAtCoordinate(newCoord).PutItemOnBlock(actor.MapCharacter); actor.MapCharacter.Coordinate = newCoord; //And that's done } } //Otherwise do nothing. Stay there } return new ActionFeedback[] { }; }
public static IEnumerable<ActionFeedback> PerformActions(Actor actor, IEnumerable<Actor> actors, MapCoordinate playerLocation) { if (GameState.LocalMap.PathfindingMap == null) { GameState.LocalMap.GeneratePathfindingMap(); } List<ActionFeedback> feedback = new List<ActionFeedback>(); //First check whether they have a mission while (actor.CurrentMission == null) { //Try to pop a new one if (actor.MissionStack.Count() == 0) { //no mission. ah well return new ActionFeedback[] { }; } actor.CurrentMission = actor.MissionStack.Pop(); if (actor.CurrentMission == null) { //Just idle then actor.CurrentMission = new IdleMission(); } if (!actor.CurrentMission.IsRetainable) { //Discard! actor.CurrentMission = null; //grab another } } //Update the graphic if (actor.IsProne) { (actor.MapCharacter as LocalCharacter).EnemyThought = EnemyThought.PRONE; } else { (actor.MapCharacter as LocalCharacter).EnemyThought = actor.CurrentMission.EnemyThought; } if (actor.IsStunned) //Do nothing { return new ActionFeedback[] { }; } if (actor.IsProne) { //Can we get up? if (GameState.LocalMap.GetBlockAtCoordinate(actor.MapCharacter.Coordinate).MayContainItems) { //Yes we can actor.IsProne = false; } else { return new ActionFeedback[] { }; //Do nothing } } if (actor.CurrentMission.MissionType == DRObjects.ActorHandling.ActorMissionType.IDLE) { if (actor.IsAggressive && Math.Abs(actor.MapCharacter.Coordinate - playerLocation) < actor.LineOfSight) { //He's there. Push the current mission into the stack and follow him actor.MissionStack.Push(actor.CurrentMission); actor.CurrentMission = new HuntDownMission() { Target = actors.Where(a => a.IsPlayerCharacter).FirstOrDefault(), TargetCoordinate = actors.Where(a => a.IsPlayerCharacter).FirstOrDefault().MapCharacter.Coordinate }; return new ActionFeedback[] { }; } } else if (actor.CurrentMission.MissionType == DRObjects.ActorHandling.ActorMissionType.WAIT) { return WaitMission(actor.CurrentMission as WaitMission, actor); } else if (actor.CurrentMission.MissionType == DRObjects.ActorHandling.ActorMissionType.WANDER) { return WanderMission(actor.CurrentMission as WanderMission, actor); } else if (actor.IsAggressive && actor.CurrentMission.MissionType == DRObjects.ActorHandling.ActorMissionType.ATTACK) { return AttackMission(actor.CurrentMission as AttackMission, actor); } else if (actor.IsAggressive && actor.CurrentMission.MissionType == DRObjects.ActorHandling.ActorMissionType.HUNTDOWN) { return HuntDownMission(actor.CurrentMission as HuntDownMission, actor); } else if (actor.CurrentMission.MissionType == DRObjects.ActorHandling.ActorMissionType.WALKTO) { return WalkToMission(actor.CurrentMission as WalkToMission, actor); } else if (actor.CurrentMission.MissionType == DRObjects.ActorHandling.ActorMissionType.PATROL) { return PatrolMission(actor.CurrentMission as PatrolMission, actor); } else if (actor.CurrentMission.MissionType == ActorMissionType.PATROL_ROUTE) { return PatrolRouteMission(actor.CurrentMission as PatrolRouteMission, actor); } return feedback; }
public static MapBlock[,] GenerateDungeonLevel(int level, int percentCovered, out MapCoordinate startPoint, out Actor[] enemies, out Dungeon dungeon) { startPoint = new MapCoordinate(); List<Actor> ens = new List<Actor>(); List<Rectangle> rectangles = null; Stack<Rectangle> unusedRectangles = null; ItemFactory.ItemFactory fact = new ItemFactory.ItemFactory(); List<SummoningCircle> summoningCircles = new List<SummoningCircle>(); int tileID = -1; MapBlock[,] map = GenerateBaseMap(level, percentCovered, out rectangles, out tileID); //Copy the rectangles unusedRectangles = new Stack<Rectangle>(); foreach (var rectangle in rectangles) { unusedRectangles.Push(rectangle); } //Put the tiles SummoningCircle dummyCircle = null; //Each rectangle is going to contain a room //First pick two to be the start and end rooms PutRoom(map, tileID, level, DungeonRoomType.ENTRANCE, unusedRectangles.Pop(), out dummyCircle); startPoint = new MapCoordinate(rectangles[rectangles.Count - 1].Center.X, rectangles[rectangles.Count - 1].Center.Y, 0, MapType.LOCAL); PutRoom(map, tileID, level, DungeonRoomType.EXIT, unusedRectangles.Pop(), out dummyCircle); //Then pick d6 + level as summoning rooms. Ensuring they are less than half the rooms int summoningRooms = GameState.Random.Next(1, 6) + level; summoningRooms = summoningRooms > rectangles.Count / 2 ? rectangles.Count / 2 : summoningRooms; for (int i = 0; i < summoningRooms; i++) { SummoningCircle circle = null; PutRoom(map, tileID, level, DungeonRoomType.SUMMONING, unusedRectangles.Pop(), out circle); //Create them //Grab references to the summoning circle as we'll need them later summoningCircles.Add(circle); } int treasureRooms = GameState.Random.Next((int)Math.Ceiling((double)(level / 2 > 5 ? 5 : level / 2))); if (treasureRooms > unusedRectangles.Count) { treasureRooms = unusedRectangles.Count - 1; } else if (treasureRooms < 1) { treasureRooms = 1; //At least one } //Create some treasure rooms for (int i = 0; i < treasureRooms; i++) { PutRoom(map, tileID, level, DungeonRoomType.TREASURE, unusedRectangles.Pop(), out dummyCircle); } //Then we can pick some of the rooms as being the other room types List<DungeonRoomType> roomTypes = ((DungeonRoomType[])Enum.GetValues(typeof(DungeonRoomType))).ToList(); roomTypes.Remove(DungeonRoomType.ENTRANCE); roomTypes.Remove(DungeonRoomType.EXIT); for(int i=0; i < 7; i++) { roomTypes.Add(DungeonRoomType.EMPTY); } //Make the remaining rectangles into rooms while (unusedRectangles.Count > 0) { //Pick a room type at random PutRoom(map, tileID, level, roomTypes.GetRandom(), unusedRectangles.Pop(), out dummyCircle); } //Package it all into a Dungeon object dungeon = new Dungeon(); dungeon.DifficultyLevel = level; dungeon.Rooms = rectangles; dungeon.SummoningCircles = summoningCircles; enemies = ens.ToArray(); return map; }
/// <summary> /// Part 1 - Generate the base map /// </summary> /// <param name="level"></param> /// <param name="percentCovered"></param> /// <returns></returns> private static MapBlock[,] GenerateBaseMap(int level, int percentCovered, out List<Rectangle> rectangles, out int tileID) { MapBlock[,] map = new MapBlock[SIZE, SIZE]; ItemFactory.ItemFactory fact = new ItemFactory.ItemFactory(); //Put the tiles tileID = -1; var dummy = fact.CreateItem("tiles", "cave", out tileID); for (int x = 0; x < SIZE; x++) { for (int y = 0; y < SIZE; y++) { map[x, y] = new MapBlock(); //Start with air :) map[x, y].Tile = new Air(); map[x, y].Tile.Coordinate = (new MapCoordinate(x, y, 0, MapType.LOCAL)); } } //Now we need to drop particular shapes on the map, with a minimum area of MINIMUM_AREA until we reach a particular percentage covered int totalCovered = 0; //Or until we run out of attempts int attempts = 0; rectangles = new List<Rectangle>(); while (totalCovered < AREA && attempts < 50) { attempts++; //Pick a random spot to drop it in Random random = GameState.Random; Point p = new Point(random.Next(SIZE), random.Next(SIZE)); //From this point, draw a rectangle with a random size from Edge to Edge Rectangle rect = new Rectangle(p.X, p.Y, random.Next(MINIMUM_EDGE, MAXIMUM_EDGE), random.Next(MINIMUM_EDGE, MAXIMUM_EDGE)); //Does it have a valid area? Is it within the current map if ((rect.Width * rect.Height) >= MINIMUM_AREA && rect.Right < SIZE && rect.Bottom < SIZE) { //Does it intersect or is contained within another rectangle ? //(This will not preclude two rectangles next to each other, as long as they don't intersect) if (rectangles.Any(r => r.Intersects(rect) || r.Contains(rect) || rect.Contains(r))) { //invalid continue; } rectangles.Add(rect); //Valid totalCovered += (rect.Width * rect.Height); attempts = 0; } else { //invalid continue; } } //Now we should have a number of rectangles, let's turn those into actual rooms foreach (var rect in rectangles) { for (int x = 0; x < rect.Width; x++) { for (int y = 0; y < rect.Height; y++) { MapCoordinate coord = new MapCoordinate(rect.X + x, rect.Y + y, 0, MapType.LOCAL); MapBlock b = map[coord.X, coord.Y]; b.Tile = fact.CreateItem("tiles", tileID); b.Tile.Coordinate = coord; } } } int pathTile = -1; dummy = fact.CreateItem("tiles", "pavement", out pathTile); //Let's connect each room with each other room for (int i = 0; i < rectangles.Count; i++) { Rectangle curr = rectangles[i]; Rectangle next = i == rectangles.Count - 1 ? rectangles[0] : rectangles[i + 1]; //Next rectangle is either the one in the list, or the first one PathfinderInterface.Nodes = GeneratePathfindingMapConnector(map); //Path from the center of the rectangles var path = PathfinderInterface.GetPath(new MapCoordinate(curr.Center.X, curr.Center.Y, 0, MapType.LOCAL), new MapCoordinate(next.Center.Y, next.Center.Y, 0, MapType.LOCAL)); if (path != null) { //Path it! foreach (var p in path) { if (!map[p.X, p.Y].MayContainItems) { MapCoordinate coord = new MapCoordinate(p.X, p.Y, 0, MapType.LOCAL); MapBlock b = map[coord.X, coord.Y]; b.Tile = fact.CreateItem("tiles", pathTile); b.Tile.Coordinate = coord; } } } } //Now we can go through the blocks and put walls around the border int borderID = -1; dummy = fact.CreateItem("mundaneitems", "dungeon wall", out borderID); for (int x = 0; x < SIZE; x++) { for (int y = 0; y < SIZE; y++) { MapBlock current = map[x, y]; if (!current.MayContainItems) { //Are we near one which can contain items? if (GetBlocksAroundPoint(map, current.Tile.Coordinate, 1).Any(b => b.MayContainItems)) { //Yep - put a wall there current.ForcePutItemOnBlock(fact.CreateItem("mundaneitems", borderID)); } } } } return map; }
/// <summary> /// Load the location /// </summary> /// <param name="location"></param> /// <param name="forceRegenerate">If set to true, will regenerate the item anyway</param> private void LoadLocation(Location location, bool forceRegenerate = false) { if (LocalMap.MapGenerated(location.UniqueGUID) && !forceRegenerate) { //Reload the map var savedMap = LocalMap.DeserialiseLocalMap(location.UniqueGUID); GameState.LocalMap = new LocalMap(savedMap.localGameMap.GetLength(0), savedMap.localGameMap.GetLength(1), 1, 0); GameState.LocalMap.Location = savedMap.Location; //GameState.LocalMap.Location = location; if (GameState.LocalMap.Location is MapSite && (GameState.LocalMap.Location as MapSite).SiteData.MapRegenerationRequired) { Actor[] newActors = null; //Before we do anything, check that we don't need to regenerate it MapBlock[,] savedMap2D = new MapBlock[savedMap.localGameMap.GetLength(0), savedMap.localGameMap.GetLength(1)]; for (int x = 0; x < savedMap.localGameMap.GetLength(0); x++) { for (int y = 0; y < savedMap.localGameMap.GetLength(1); y++) { savedMap2D[x, y] = savedMap.localGameMap[x, y, 0]; //NB: CHANGE IF WE GO 3D } } var blocks = SiteGenerator.RegenerateSite((GameState.LocalMap.Location as MapSite).SiteData, savedMap2D, savedMap.Actors.ToArray(), out newActors); List<MapBlock> cM = new List<MapBlock>(); foreach (MapBlock block in blocks) { cM.Add(block); } GameState.LocalMap.AddToLocalMap(cM.ToArray()); GameState.LocalMap.Actors = newActors.ToList(); GameState.LocalMap.Tick(); //Tick to remove the dead actors } GameState.LocalMap.Actors = new List<Actor>(); List<MapBlock> collapsedMap = new List<MapBlock>(); foreach (MapBlock block in savedMap.localGameMap) { collapsedMap.Add(block); } GameState.LocalMap.AddToLocalMap(collapsedMap.ToArray()); GameState.LocalMap.PathfindingMap = savedMap.PathfindingMap; GameState.LocalMap.PointsOfInterest = savedMap.PointsOfInterest; GameState.LocalMap.IsUnderground = savedMap.IsUnderground; GameState.LocalMap.Actors = savedMap.Actors; LocalMapGenerator lmg = new LocalMapGenerator(); //Go through the actors foreach (var actor in GameState.LocalMap.Actors) { //Do we have any vendors ? if (actor.VendorDetails != null) { if (Math.Abs((actor.VendorDetails.GenerationTime - GameState.UniverseTime).GetTimeComponent(DRTimeComponent.MONTH)) > 1) { //More than a month old //Regenerate it lmg.UpdateVendorStock(actor); } } } //Find the player character item var playerActor = GameState.LocalMap.Actors.Where(a => a.IsPlayerCharacter).FirstOrDefault(); GameState.PlayerCharacter.MapCharacter.Coordinate = playerActor.MapCharacter.Coordinate; GameState.PlayerCharacter.MapCharacter = playerActor.MapCharacter; //GameState.LocalMap.Location = location; } else { Actor[] actors = null; MapCoordinate startPoint = null; List<PointOfInterest> pointsOfInterest = null; MapBlock[,] gennedMap = null; if (location is BanditCamp) { gennedMap = CampGenerator.GenerateCamp((location as BanditCamp).BanditTotal, out startPoint, out actors); } else if (location is Citadel) { var citadel = location as Citadel; CitadelGenerator gen = new CitadelGenerator(); gennedMap = gen.GenerateDungeon(citadel.TierCount, citadel.TrapRooms, citadel.GuardRooms, citadel.TreasureRoom, citadel.OwnerCreatureType, (decimal)citadel.PercentageOwned, citadel.MaxWildPopulation, citadel.MaxOwnedPopulation, out startPoint, out actors, out pointsOfInterest); } else if (location is MapSite) { var mapSite = location as MapSite; gennedMap = SiteGenerator.GenerateSite(mapSite.SiteData, out actors); startPoint = new MapCoordinate(gennedMap.GetLength(0) / 2, 0, 0, MapType.LOCAL); } else if (location is Settlement) { List<Actor> settlementActors = null; PointOfInterest sp = null; gennedMap = SettlementGenerator.GenerateMap((location as Settlement), out settlementActors, out sp); actors = settlementActors.ToArray(); startPoint = sp.Coordinate; } else if (location is Dungeon) { Dungeon dungeon = null; gennedMap = DungeonGenerator.GenerateDungeonLevel((location as Dungeon).DifficultyLevel, 80, out startPoint, out actors, out dungeon); //Copy the changes, that way we retain the object reference and the guid for serialization (location as Dungeon).Rooms = dungeon.Rooms; (location as Dungeon).SummoningCircles = dungeon.SummoningCircles; } GameState.LocalMap = new LocalMap(gennedMap.GetLength(0), gennedMap.GetLength(1), 1, 0); GameState.LocalMap.Actors = new List<Actor>(); List<MapBlock> collapsedMap = new List<MapBlock>(); foreach (MapBlock block in gennedMap) { collapsedMap.Add(block); } GameState.LocalMap.AddToLocalMap(collapsedMap.ToArray()); GameState.PlayerCharacter.MapCharacter.Coordinate = startPoint; MapBlock playerBlock = GameState.LocalMap.GetBlockAtCoordinate(startPoint); playerBlock.PutItemOnBlock(GameState.PlayerCharacter.MapCharacter); GameState.LocalMap.Actors.AddRange(actors); GameState.LocalMap.Actors.Add(GameState.PlayerCharacter); GameState.LocalMap.PointsOfInterest = pointsOfInterest; GameState.LocalMap.Location = location; GameState.LocalMap.IsUnderground = (location is Dungeon); if (location is Dungeon) //Spawn at least 10 enemies { for (int i = 0; i < 10; i++) { GameState.LocalMap.MinuteChanged(null, null); //Summon! } } } }
/// <summary> /// Creates Citadels on the map /// Will colonise areas not claimed by humans. /// </summary> public static void CreateOrcCitadels() { //Create the civilisation GameState.GlobalMap.Civilisations.Add(new Civilisation() { Faction = OwningFactions.ORCS, ID = 100, Name = "Orc Hoardes" }); List<MapBlock> blocks = new List<MapBlock>(); //Collect all the points which aren't claimed for (int x = 0; x < WORLDSIZE; x++) { for (int y = 0; y < WORLDSIZE; y++) { MapBlock block = GameState.GlobalMap.GetBlockAtCoordinate(new MapCoordinate(x, y, 0, MapType.GLOBAL)); var tile = block.Tile as GlobalTile; if (!tile.HasRiver && !tile.IsBlockedForColonisation && !tile.Owner.HasValue && !tile.HasResource && tile.Elevation > 0 && tile.Elevation < 250) { blocks.Add(block); } } } for (int i = 0; i < CITADEL_TOTAL; i++) { //Create the actual item - Completly Randomly for now var enemyRace = ActorGeneration.GetEnemyType(true); MapBlock block = blocks[GameState.Random.Next(blocks.Count)]; //Still valid? if ((block.Tile as GlobalTile).IsBlockedForColonisation || (block.Tile as GlobalTile).Owner.HasValue) { //Nope i--; continue; } Citadel dungeon = new Citadel(); dungeon.Coordinate = new MapCoordinate(block.Tile.Coordinate); dungeon.GuardRooms = GameState.Random.Next(2) + 1; dungeon.MaxOwnedPopulation = GameState.Random.Next(10) + 2; dungeon.MaxWildPopulation = GameState.Random.Next(4); dungeon.OwnerCreatureType = enemyRace; dungeon.PercentageOwned = GameState.Random.Next(50) + 50; dungeon.TierCount = GameState.Random.Next(4) + 3; dungeon.TrapRooms = GameState.Random.Next(5) + 2; dungeon.TreasureRoom = GameState.Random.Next(5); //Put an entire group of Dungeon Items on it MapCoordinate[] dungeonCoordinates = new MapCoordinate[10]; dungeonCoordinates[7] = new MapCoordinate(block.Tile.Coordinate.X - 1, block.Tile.Coordinate.Y - 1, 0, MapType.GLOBAL); dungeonCoordinates[8] = new MapCoordinate(block.Tile.Coordinate.X, block.Tile.Coordinate.Y - 1, 0, MapType.GLOBAL); dungeonCoordinates[9] = new MapCoordinate(block.Tile.Coordinate.X + 1, block.Tile.Coordinate.Y - 1, 0, MapType.GLOBAL); dungeonCoordinates[4] = new MapCoordinate(block.Tile.Coordinate.X - 1, block.Tile.Coordinate.Y, 0, MapType.GLOBAL); dungeonCoordinates[5] = new MapCoordinate(block.Tile.Coordinate.X, block.Tile.Coordinate.Y, 0, MapType.GLOBAL); dungeonCoordinates[6] = new MapCoordinate(block.Tile.Coordinate.X + 1, block.Tile.Coordinate.Y, 0, MapType.GLOBAL); dungeonCoordinates[1] = new MapCoordinate(block.Tile.Coordinate.X - 1, block.Tile.Coordinate.Y + 1, 0, MapType.GLOBAL); dungeonCoordinates[2] = new MapCoordinate(block.Tile.Coordinate.X, block.Tile.Coordinate.Y + 1, 0, MapType.GLOBAL); dungeonCoordinates[3] = new MapCoordinate(block.Tile.Coordinate.X + 1, block.Tile.Coordinate.Y + 1, 0, MapType.GLOBAL); //Block surrounding tiles MapBlock[] regionalBlocks = GetBlocksAroundPoint(block.Tile.Coordinate, 1); foreach (MapBlock rblock in regionalBlocks) { (rblock.Tile as GlobalTile).IsBlockedForColonisation = true; } //Also claim the surrounding areas for orcs. Let's give them an owner of 100 MapBlock[] claimedBlocks = GetBlocksAroundPoint(block.Tile.Coordinate, CITADEL_CLAIMING_RADIUS); foreach (MapBlock rblock in claimedBlocks) { (rblock.Tile as GlobalTile).Owner = 100; } for (int corner = 1; corner < 10; corner++) { var cornerBlock = GameState.GlobalMap.GetBlockAtCoordinate(dungeonCoordinates[corner]); //Cut any forests down switch ((cornerBlock.Tile as GlobalTile).Biome) { case GlobalBiome.DENSE_FOREST: (cornerBlock.Tile as GlobalTile).Biome = GlobalBiome.GRASSLAND; break; case GlobalBiome.WOODLAND: (cornerBlock.Tile as GlobalTile).Biome = GlobalBiome.GRASSLAND; break; case GlobalBiome.POLAR_FOREST: (cornerBlock.Tile as GlobalTile).Biome = GlobalBiome.POLAR_DESERT; break; } //Create a new dungeon CitadelItem item = new CitadelItem(corner); item.Coordinate = new MapCoordinate(cornerBlock.Tile.Coordinate); item.Name = "Orc Citadel"; item.Description = "a dark dungeon owned by Orcs"; item.Citadel = dungeon; cornerBlock.ForcePutItemOnBlock(item); blocks.Remove(block); } } }
/// <summary> /// Returns a filled square around a particular point /// </summary> /// <param name="centre"></param> /// <param name="radius"></param> /// <returns></returns> public static MapBlock[] GetBlocksAroundPoint(MapCoordinate centre, int radius) { int minY = centre.Y - Math.Abs(radius); int maxY = centre.Y + Math.Abs(radius); int minX = centre.X - Math.Abs(radius); int maxX = centre.X + Math.Abs(radius); List<MapBlock> returnList = new List<MapBlock>(); //go through all of them for (int yLoop = maxY; yLoop >= minY; yLoop--) { for (int xLoop = minX; xLoop <= maxX; xLoop++) { MapCoordinate coord = new MapCoordinate(xLoop, yLoop, 0, MapType.GLOBAL); if (xLoop >= 0 && xLoop < WORLDSIZE && yLoop >= 0 && yLoop < WORLDSIZE) { //make sure they're in the map returnList.Add(GameState.GlobalMap.GetBlockAtCoordinate(coord)); } } } return returnList.ToArray(); }
/// <summary> /// Gets possible actions for a particular block /// </summary> /// <param name="coordinate"></param> /// <returns></returns> public static ActionType[] GetPossibleActions(MapCoordinate coordinate) { switch (coordinate.MapType) { case MapType.LOCAL: return GameState.LocalMap.GetBlockAtCoordinate(coordinate).GetActions(GameState.PlayerCharacter); case MapType.GLOBAL: return GameState.GlobalMap.GetBlockAtCoordinate(coordinate).GetActions(GameState.PlayerCharacter); default: throw new NotImplementedException("There is no support for that particular maptype"); } }
/// <summary> /// Generates a map based on the maplet assigned /// </summary> /// <param name="maplet">The maplet to generate</param> /// <param name="parentWallID">The wall that the parent has</param> /// <param name="parentTileID">The ID of the tiles used in the parent maplet item</param> /// <param name="enemyType">The type of actor which is dominant in this map</param> /// <param name="owner">The owner of the map. Any maplet items which don't belong will be hidden</param> /// <param name="actors">The actors which we have generated</param> /// <param name="actorType">The type of actors to generate</param> /// <returns></returns> public MapBlock[,] GenerateMap(int parentTileID, int? parentWallID, Maplet maplet, bool preferSides, string actorType, OwningFactions owner, out Actor[] actors, out MapletActorWanderArea[] wAreas, out MapletPatrolPoint[] patrolRoutes, out MapletFootpathNode[] footpathNodes) { List<Actor> actorList = new List<Actor>(); List<MapletActorWanderArea> wanderAreas = new List<MapletActorWanderArea>(); List<MapletPatrolPoint> patrolRouteList = new List<MapletPatrolPoint>(); List<MapletFootpathNode> footpathNodeList = new List<MapletFootpathNode>(); PlanningMapItemType[,] planningMap = new PlanningMapItemType[maplet.SizeX, maplet.SizeY]; //Step 1: Plan how the map will look //Step 1a: Set all tiles to available, and set the frame to walls if there's a wall planningMap = this.CreateBlueprint(maplet); //Step 1b: Put in the tiles in the actual map, and the walls if they are present MapBlock[,] generatedMap = new MapBlock[maplet.SizeX, maplet.SizeY]; ItemFactory.ItemFactory factory = new ItemFactory.ItemFactory(); int tileID = 0; if (maplet.Tiled) { if (maplet.TileID.HasValue) { tileID = maplet.TileID.Value; } else { //Load the tileID from the factory factory.CreateItem(Archetype.TILES, maplet.TileTag, out tileID); } } else { tileID = parentTileID; } //That's the tiles done for (int x = 0; x < generatedMap.GetLength(0); x++) { for (int y = 0; y < generatedMap.GetLength(1); y++) { MapItem tile = factory.CreateItem("tile", tileID); tile.Coordinate = new MapCoordinate(x, y, 0, DRObjects.Enums.MapType.LOCAL); MapBlock block = new MapBlock(); block.Tile = tile; generatedMap[x, y] = block; } } //Do the walls now if they are required int? wallID = null; int tempWallID = -1; if (parentWallID.HasValue) { MapItem wall = factory.CreateItem("MUNDANEITEMS", parentWallID.Value); wallID = parentWallID; } else { MapItem wall = factory.CreateItem(DRObjects.Enums.Archetype.MUNDANEITEMS, "wall", out tempWallID); wallID = tempWallID; } if (maplet.Walled && wallID.HasValue) { //wall the edge tiles for (int x = 0; x < maplet.SizeX; x++) { generatedMap[x, 0].PutItemOnBlock(factory.CreateItem("mundaneitems", wallID.Value)); generatedMap[x, maplet.SizeY - 1].PutItemOnBlock(factory.CreateItem("mundaneitems", wallID.Value)); if (maplet.WindowProbability.HasValue && maplet.WindowProbability.Value > 0) { if (random.Next(100) < maplet.WindowProbability.Value) { int itemID; //Put a window :) generatedMap[x, 0].ForcePutItemOnBlock(factory.CreateItem(DRObjects.Enums.Archetype.MUNDANEITEMS, "window", out itemID)); } else if (random.Next(100) < maplet.WindowProbability.Value) { int itemID; //Put a window :) generatedMap[x, maplet.SizeY - 1].ForcePutItemOnBlock(factory.CreateItem(DRObjects.Enums.Archetype.MUNDANEITEMS, "window", out itemID)); } } } for (int y = 0; y < maplet.SizeY; y++) { generatedMap[0, y].PutItemOnBlock(factory.CreateItem("mundaneitems", wallID.Value)); generatedMap[maplet.SizeX - 1, y].PutItemOnBlock(factory.CreateItem("mundaneitems", wallID.Value)); if (maplet.WindowProbability.HasValue && maplet.WindowProbability.Value > 0) { if (random.Next(100) < maplet.WindowProbability.Value) { int itemID; //Put a window :) generatedMap[0, y].ForcePutItemOnBlock(factory.CreateItem(DRObjects.Enums.Archetype.MUNDANEITEMS, "window", out itemID)); } else if (random.Next(100) < maplet.WindowProbability.Value) { int itemID; //Put a window :) generatedMap[maplet.SizeX - 1, y].ForcePutItemOnBlock(factory.CreateItem(DRObjects.Enums.Archetype.MUNDANEITEMS, "window", out itemID)); } } } } //Step 1c: Determine where we'll put the maplets foreach (MapletContentsMaplet childMaplet in maplet.MapletContents.Where(mc => (mc is MapletContentsMaplet)).OrderByDescending(mc => mc.ProbabilityPercentage).ThenBy(mc => random.Next())) { //Calculate the probability of putting the item in, and how many items we're putting for (int i = 0; i < childMaplet.MaxAmount; i++) { if (random.NextDouble() * 100 <= childMaplet.ProbabilityPercentage) { //Does it fit? int x = -1; int y = -1; PlanningMapItemType[,] newMap; //Convert the child maplet into a planning map PlanningMapItemType[,] childMapletBlueprint = this.CreateBlueprint(childMaplet.Maplet); //mark the areas covered by the blueprint as being held by that blueprint if (childMaplet.Position == PositionAffinity.FIXED) { if (Fits(planningMap, childMapletBlueprint, childMaplet.x.Value, childMaplet.y.Value, out newMap)) { //it fits, generate it - <3 Recursion Actor[] childActors = null; MapletActorWanderArea[] wanderA = null; MapletPatrolPoint[] patrolPoints = null; MapletFootpathNode[] fpNodes = null; MapBlock[,] childMap = this.GenerateMap(tileID, wallID.Value, childMaplet.Maplet, childMaplet.Position == DRObjects.LocalMapGeneratorObjects.Enums.PositionAffinity.SIDES, actorType, owner, out childActors, out wanderA, out patrolPoints, out fpNodes); //Add the child actors actorList.AddRange(childActors); //Update any actors's locations should they have any foreach (var actor in childActors) { if (actor.MissionStack.Count() > 0) { var wander = actor.MissionStack.Peek() as WanderMission; if (wander != null) { wander.WanderPoint.X += childMaplet.x.Value; wander.WanderPoint.Y += childMaplet.y.Value; wander.WanderRectangle = new Rectangle(wander.WanderRectangle.X + childMaplet.x.Value, wander.WanderRectangle.Y + childMaplet.y.Value, wander.WanderRectangle.Width, wander.WanderRectangle.Height); } } } //Update any wander areas too foreach (var area in wanderA) { area.WanderRect = new Rectangle(area.WanderRect.X + childMaplet.x.Value, area.WanderRect.Y + childMaplet.y.Value, area.WanderRect.Width, area.WanderRect.Height); area.WanderPoint.X += childMaplet.x.Value; area.WanderPoint.Y += childMaplet.y.Value; } //and patrol points foreach (var point in patrolPoints) { point.Point.X += childMaplet.x.Value; point.Point.Y += childMaplet.y.Value; } foreach (var n in fpNodes) { n.Point.X += childMaplet.x.Value; n.Point.Y += childMaplet.y.Value; } //And add them wanderAreas.AddRange(wanderA); patrolRouteList.AddRange(patrolPoints); footpathNodeList.AddRange(fpNodes); //Join the two maps together generatedMap = this.JoinMaps(generatedMap, childMap, childMaplet.x.Value, childMaplet.y.Value); } } else { if (Fits(planningMap, childMapletBlueprint, childMaplet.Position == DRObjects.LocalMapGeneratorObjects.Enums.PositionAffinity.SIDES, childMaplet.FirstFit, childMaplet.Padding, out x, out y, out newMap)) { //it fits, generate it - <3 Recursion Actor[] childActors = null; MapletActorWanderArea[] wanderA = null; MapletPatrolPoint[] patrolPoints = null; MapletFootpathNode[] fpNodes = null; MapBlock[,] childMap = this.GenerateMap(tileID, wallID.Value, childMaplet.Maplet, childMaplet.Position == DRObjects.LocalMapGeneratorObjects.Enums.PositionAffinity.SIDES, actorType, owner, out childActors, out wanderA, out patrolPoints, out fpNodes); //Add the child actors actorList.AddRange(childActors); //Update any actors's locations should they have any foreach (var actor in childActors) { if (actor.MissionStack.Count() > 0) { var wander = actor.MissionStack.Peek() as WanderMission; if (wander != null) { wander.WanderPoint.X += x; wander.WanderPoint.Y += y; wander.WanderRectangle = new Rectangle(wander.WanderRectangle.X + x, wander.WanderRectangle.Y + y, wander.WanderRectangle.Width, wander.WanderRectangle.Height); } } } //Update any wander areas too foreach (var area in wanderA) { area.WanderRect = new Rectangle(area.WanderRect.X + x, area.WanderRect.Y + y, area.WanderRect.Width, area.WanderRect.Height); area.WanderPoint.X += x; area.WanderPoint.Y += y; } //and patrol routes foreach (var point in patrolPoints) { point.Point.X += x; point.Point.Y += y; } foreach (var n in fpNodes) { n.Point.X += x; n.Point.Y += y; } //And add them wanderAreas.AddRange(wanderA); patrolRouteList.AddRange(patrolPoints); footpathNodeList.AddRange(fpNodes); //Join the two maps together generatedMap = this.JoinMaps(generatedMap, childMap, x, y); } } } } } //Step 2: Put the items into the map //Lets list places we can put it in List<MapBlock> candidateBlocks = new List<MapBlock>(); for (int x = 0; x < planningMap.GetLength(0); x++) { for (int y = 0; y < planningMap.GetLength(1); y++) { if (planningMap[x, y] == PlanningMapItemType.FREE) { candidateBlocks.Add(generatedMap[x, y]); } } } List<MapBlock> edgeBlocks = new List<MapBlock>(); //Lets also get the edge mapblocks - for those who prefer being on the edge for (int x = 0; x < planningMap.GetLength(0); x++) { if (!maplet.Walled) { if (planningMap[x, 0] == PlanningMapItemType.FREE) { edgeBlocks.Add(generatedMap[x, 0]); } if (planningMap[x, planningMap.GetLength(1) - 1] == PlanningMapItemType.FREE) { edgeBlocks.Add(generatedMap[x, planningMap.GetLength(1) - 1]); } } else { if (planningMap[x, 1] == PlanningMapItemType.FREE) { edgeBlocks.Add(generatedMap[x, 1]); } if (planningMap[x, planningMap.GetLength(1) - 2] == PlanningMapItemType.FREE) { edgeBlocks.Add(generatedMap[x, planningMap.GetLength(1) - 2]); } } } //Doing the y parts for (int y = 0; y < planningMap.GetLength(1); y++) { if (!maplet.Walled) { if (planningMap[0, y] == PlanningMapItemType.FREE) { edgeBlocks.Add(generatedMap[0, y]); } if (planningMap[planningMap.GetLength(0) - 1, y] == PlanningMapItemType.FREE) { edgeBlocks.Add(generatedMap[planningMap.GetLength(0) - 1, y]); } } else { if (planningMap[1, y] == PlanningMapItemType.FREE) { edgeBlocks.Add(generatedMap[1, y]); } if (planningMap[planningMap.GetLength(0) - 2, y] == PlanningMapItemType.FREE) { edgeBlocks.Add(generatedMap[planningMap.GetLength(0) - 2, y]); } } } //go through the maplet contents //Get the smallest x and y coordinate in the candidate blocks so we can use it for fixed things int smallestX = -1; int smallestY = -1; try { smallestX = candidateBlocks.Select(b => b.Tile.Coordinate.X).Min(); smallestY = candidateBlocks.Select(b => b.Tile.Coordinate.Y).Min(); } catch { //No space :( } foreach (MapletContents contents in maplet.MapletContents.Where(mc => mc is MapletContentsItem || mc is MapletContentsItemTag || mc is MapletContentsItemSpecial).OrderByDescending(mc => mc.ProbabilityPercentage)) { //We'll see if we even put this at all MapItem itemPlaced = null; for (int i = 0; i < contents.MaxAmount; i++) { //lets see what the probability of putting it in is if ((random.NextDouble() * 100) <= contents.ProbabilityPercentage) { //Put it in if (contents is MapletContentsItem) { MapletContentsItem mapletContent = (MapletContentsItem)contents; itemPlaced = factory.CreateItem(mapletContent.ItemCategory, mapletContent.ItemID); } else if (contents is MapletContentsItemTag) { MapletContentsItemTag mapletContent = (MapletContentsItemTag)contents; int tempInt; itemPlaced = factory.CreateItem(mapletContent.Category, mapletContent.Tag, out tempInt); //I CHANGED THIS itemPlaced.OwnedBy = mapletContent.Factions; } else if (contents is MapletContentsItemSpecial) { //what type is it switch ((contents as MapletContentsItemSpecial).Type) { case "StairsUp": itemPlaced = new DungeonStairs(true); break; case "StairsDown": itemPlaced = new DungeonStairs(false); break; case "SummoningCircle": itemPlaced = new SummoningCircle(); break; case "TreasureChest": itemPlaced = new TreasureChest(); break; case "Altar": itemPlaced = new Altar(); break; case "WishingWell": itemPlaced = new WishingWell(); break; case "Potion": var potionType = (PotionType[]) Enum.GetValues(typeof(PotionType)); var potion = potionType.GetRandom(); itemPlaced = new Potion(potion); break; default: throw new NotImplementedException("No code for " + (contents as MapletContentsItemSpecial).Type + " can be found"); } } if (candidateBlocks.Count != 0) { //Lets decide where to put it if (contents.Position == DRObjects.LocalMapGeneratorObjects.Enums.PositionAffinity.SIDES && edgeBlocks.Count != 0) { //pick a place at random and add it to the maplet int position = random.Next(edgeBlocks.Count); edgeBlocks[position].PutItemOnBlock(itemPlaced); if (!contents.AllowItemsOnTop) { //remove it from both candidateBlocks.Remove(edgeBlocks[position]); edgeBlocks.RemoveAt(position); } } if (contents.Position == DRObjects.LocalMapGeneratorObjects.Enums.PositionAffinity.MIDDLE && candidateBlocks.Except(edgeBlocks).Count() != 0) { //pick a place at random and add it to the maplet int position = random.Next(candidateBlocks.Except(edgeBlocks).Count()); MapBlock block = candidateBlocks.Except(edgeBlocks).ToArray()[position]; block.PutItemOnBlock(itemPlaced); if (!contents.AllowItemsOnTop) { //remove it from both candidateBlocks.Remove(block); edgeBlocks.Remove(block); } } if (contents.Position == DRObjects.LocalMapGeneratorObjects.Enums.PositionAffinity.ANYWHERE) { //pick a place at random and add it to the maplet int position = random.Next(candidateBlocks.Count); candidateBlocks[position].PutItemOnBlock(itemPlaced); if (!contents.AllowItemsOnTop) { //remove it from both edgeBlocks.Remove(candidateBlocks[position]); candidateBlocks.RemoveAt(position); } } if (contents.Position == DRObjects.LocalMapGeneratorObjects.Enums.PositionAffinity.FIXED) { //Fix it in a particular position. MapCoordinate coordinate = new MapCoordinate(smallestX + contents.x.Value, smallestY + contents.y.Value, 0, DRObjects.Enums.MapType.LOCAL); var selectedBlock = candidateBlocks.Where(cb => cb.Tile.Coordinate.Equals(coordinate)).FirstOrDefault(); if (selectedBlock != null) { //maybe someone put something there already selectedBlock.PutItemOnBlock(itemPlaced); } if (!contents.AllowItemsOnTop) { //and remoev it from both candidateBlocks.Remove(selectedBlock); edgeBlocks.Remove(selectedBlock); } } } } } } //Step 3: Stripe through the map except for the current maplet's walls - work out where the walls are, and for each wall segment, put a door in #region Wall Segments List<Line> wallSegments = new List<Line>(); for (int x = 1; x < planningMap.GetLength(0) - 1; x++) { //lets see if we find a wall Segment Line wallSegment = null; for (int y = 1; y < planningMap.GetLength(1) - 1; y++) { if (planningMap[x, y] == PlanningMapItemType.WALL) { //Three possibilities exist. Either this is the start of a wall segment //Or this is a continuation of a wall segment //Or this is the end of a wall segment // -> Because there is an intersection // -> Because there was an active wall segment and there is no wall in this one if (wallSegment == null) { //Its a start wallSegment = new Line(new MapCoordinate(x, y, 0, DRObjects.Enums.MapType.LOCAL), null); } else { //Continuation or end //Check if there's an interesection //Go up one and down one. If there is the maplet's walls there won't be a door - but then there'll be a double wall anyway which makes no sense if (planningMap[x + 1, y] == PlanningMapItemType.WALL || planningMap[x - 1, y] == PlanningMapItemType.WALL) { //terminate the wall - and start a new one wallSegment.End = new MapCoordinate(x, y, 0, DRObjects.Enums.MapType.LOCAL); wallSegments.Add(wallSegment); wallSegment = new Line(new MapCoordinate(x, y, 0, DRObjects.Enums.MapType.LOCAL), null); } else { //do nothing, its a continuation } } } else { //Mayhaps a line has stopped? if (wallSegment != null) { //It has - lets terminate it wallSegment.End = new MapCoordinate(x, y - 1, 0, DRObjects.Enums.MapType.LOCAL); wallSegments.Add(wallSegment); wallSegment = null; } } } //Check if there's an active line - maybe it reaches till the end of the maplet if (wallSegment != null) { wallSegment.End = new MapCoordinate(x, (planningMap.GetLength(1) - 1), 0, DRObjects.Enums.MapType.LOCAL); wallSegments.Add(wallSegment); wallSegment = null; } } //Now stripe in the other direction for (int y = 1; y < planningMap.GetLength(1) - 1; y++) { //lets see if we find a wall Segment Line wallSegment = null; for (int x = 1; x < planningMap.GetLength(0) - 1; x++) { if (planningMap[x, y] == PlanningMapItemType.WALL) { //Three possibilities exist. Either this is the start of a wall segment //Or this is a continuation of a wall segment //Or this is the end of a wall segment // -> Because there is an intersection // -> Because there was an active wall segment and there is no wall in this one if (wallSegment == null) { //Its a start wallSegment = new Line(new MapCoordinate(x, y, 0, DRObjects.Enums.MapType.LOCAL), null); } else { //Continuation or end //Check if there's an interesection //Go up one and down one. If there is the maplet's walls there won't be a door - but then there'll be a double wall anyway which makes no sense if (planningMap[x, y + 1] == PlanningMapItemType.WALL || planningMap[x, y - 1] == PlanningMapItemType.WALL) { //terminate the wall - and start a new one wallSegment.End = new MapCoordinate(x, y, 0, DRObjects.Enums.MapType.LOCAL); wallSegments.Add(wallSegment); wallSegment = new Line(new MapCoordinate(x, y, 0, DRObjects.Enums.MapType.LOCAL), null); } else { //do nothing, its a continuation } } } else { //Mayhaps a line has stopped? if (wallSegment != null) { //It has - lets terminate it wallSegment.End = new MapCoordinate(x - 1, y, 0, DRObjects.Enums.MapType.LOCAL); wallSegments.Add(wallSegment); wallSegment = null; } } } //Check if there's an active line - maybe it reaches till the end of the maplet if (wallSegment != null) { wallSegment.End = new MapCoordinate(planningMap.GetLength(0) - 1, y, 0, DRObjects.Enums.MapType.LOCAL); wallSegments.Add(wallSegment); wallSegment = null; } } #endregion Wall Segments #region Doors //Get all wall segments larger than 0, and we can put a door there foreach (Line segment in wallSegments.Where(ws => ws.Length() > 1)) { //Put a door somewhere, as long as its not the start or end //Oh and remove the wall MapBlock block = null; if (segment.Start.X == segment.End.X) { //Get the entirety of the segment List<int> possibleYs = new List<int>(); int smallerY = Math.Min(segment.Start.Y, segment.End.Y); int largerY = Math.Max(segment.Start.Y, segment.End.Y); //Check in the real map whether the tile next to it is free for walking in for (int y = smallerY + 1; y <= largerY; y++) { if (generatedMap[segment.Start.X - 1, y].MayContainItems && generatedMap[segment.Start.X + 1, y].MayContainItems) { possibleYs.Add(y); } } //Now check whether there's a possible y, and pick a random one from it if (possibleYs.Count != 0) { block = generatedMap[segment.Start.X, possibleYs[random.Next(possibleYs.Count - 1)]]; } else { //nothing to do - take smallest block = generatedMap[segment.Start.X, segment.Start.Y + 1]; } } else { List<int> possibleXs = new List<int>(); int smallerX = Math.Min(segment.Start.X, segment.End.X); int largerX = Math.Max(segment.Start.X, segment.End.X); //Check in the real map whether the tile next to it is free for walking in for (int x = smallerX + 1; x <= largerX; x++) { if (generatedMap[x, segment.Start.Y - 1].MayContainItems && generatedMap[x, segment.Start.Y + 1].MayContainItems) { possibleXs.Add(x); } } //Now check whether there's a possible x, and pick a random one from it if (possibleXs.Count != 0) { block = generatedMap[possibleXs[random.Next(possibleXs.Count - 1)], segment.Start.Y]; } else { //nothing to do - take the smallest one block = generatedMap[segment.Start.X + 1, segment.Start.Y]; } } try { if (block != null) { block.RemoveTopItem(); int doorID = -1; block.PutItemOnBlock(factory.CreateItem(DRObjects.Enums.Archetype.MUNDANEITEMS, "door", out doorID)); } } catch { } } #endregion #region Enemies //Now lets create enemies :) foreach (var mc in maplet.MapletContents.Where(mc => mc.GetType().Equals(typeof(MapletActor))).OrderByDescending(o => o.ProbabilityPercentage)) { var actor = mc as MapletActor; for (int i = 0; i < actor.MaxAmount; i++) { //Check the random if (random.Next(100) < actor.ProbabilityPercentage) { int actorID = 0; string enemyType = actor.UseLocalType ? actorType : actor.EnemyType; //For now set gear cost to 0 Actor newActor = ActorGeneration.CreateActor(enemyType, actor.EnemyTag, null, 10, 0, null, out actorID); if (actor.VendorType.HasValue) { GenerateVendor(newActor, actor); } //Generate the map character var mapCharacter = factory.CreateItem("enemies", actorID); newActor.MapCharacter = mapCharacter; if (!actor.Factions.HasFlag(owner)) { //inactive character newActor.MapCharacter.IsActive = false; } (mapCharacter as LocalCharacter).Actor = newActor; //Lets position them randomly for (int attempt = 0; attempt < 150; attempt++) { //Try 150 times int x = random.Next(maplet.SizeX); int y = random.Next(maplet.SizeY); if (generatedMap[x, y].MayContainItems) { //Put it there mapCharacter.Coordinate = new MapCoordinate(x, y, 0, MapType.LOCAL); generatedMap[x, y].ForcePutItemOnBlock(mapCharacter); actorList.Add(newActor); //What mission does he have? if (actor.EnemyMission == ActorMissionType.WANDER) { newActor.MissionStack.Push(new WanderMission() { LoiterPercentage = 80, WanderPoint = new MapCoordinate(mapCharacter.Coordinate), WanderRectangle = new Rectangle(0, 0, generatedMap.GetLength(0), generatedMap.GetLength(1)) }); } break; } } } } } #endregion #region Wander Areas foreach (var mc in maplet.MapletContents.Where(mc => mc.GetType().Equals(typeof(MapletActorWanderArea)))) { var wander = mc as MapletActorWanderArea; //The area of this is going to be the entire maplet (so if we're in a submaplet, they'll wander in there - Awesome no ?) wander.WanderRect = new Rectangle(0, 0, generatedMap.GetLength(0), generatedMap.GetLength(1)); //Pick the wander point to be the middle of the rectangle. If the point isn't valid we might have a problem wander.WanderPoint = new MapCoordinate(generatedMap.GetLength(0) / 2, generatedMap.GetLength(1) / 2, 0, MapType.LOCAL); MapletActorWanderArea clone = new MapletActorWanderArea(); clone.WanderPoint = new MapCoordinate(wander.WanderPoint); clone.WanderRect = new Rectangle(wander.WanderRect.X, wander.WanderRect.Y, wander.WanderRect.Width, wander.WanderRect.Height); clone.Profession = wander.Profession; clone.OwnerFactions = wander.OwnerFactions; wanderAreas.Add(clone); } wAreas = wanderAreas.ToArray(); #endregion #region Patrol Points & Paths foreach (var mc in maplet.MapletContents.Where(mc => mc.GetType().Equals(typeof(MapletPatrolPoint)))) { var point = mc as MapletPatrolPoint; //The point is going to be in the middle of the entire maplet point.Point = new MapCoordinate(generatedMap.GetLength(0) / 2, generatedMap.GetLength(1) / 2, 0, MapType.LOCAL); MapletPatrolPoint clone = new MapletPatrolPoint(); clone.Point = new MapCoordinate(point.Point); clone.OwnerFactions = point.OwnerFactions; clone.Profession = point.Profession; clone.PointRadius = point.PointRadius; patrolRouteList.Add(clone); } patrolRoutes = patrolRouteList.ToArray(); foreach (var n in maplet.MapletContents.Where(mc => mc.GetType().Equals(typeof(MapletFootpathNode)))) { var node = n as MapletFootpathNode; //Point is going to be in the middle of the entire maplet node.Point = new MapCoordinate(generatedMap.GetLength(0) / 2, generatedMap.GetLength(1) / 2, 0, MapType.LOCAL); //Better create a new one MapletFootpathNode clone = new MapletFootpathNode(); clone.Point = new MapCoordinate(node.Point); clone.IsPrimary = node.IsPrimary; footpathNodeList.Add(clone); } footpathNodes = footpathNodeList.ToArray(); #endregion #region Aniamls //Now lets create enemies :) foreach (var mc in maplet.MapletContents.Where(mc => mc.GetType().Equals(typeof(MapletHerd))).OrderByDescending(o => o.ProbabilityPercentage)) { var herd = mc as MapletHerd; for (int i = 0; i < herd.MaxAmount; i++) { //Check the random if (random.Next(100) < herd.ProbabilityPercentage) { var herds = ActorGeneration.CreateAnimalHerds(herd.Biome, herd.Domesticated, herd.HerdTag, 1); foreach (var animalHerd in herds) { foreach (var animal in animalHerd) { //Position them on the map for (int attempt = 0; attempt < 150; attempt++) { //Try 150 times int x = random.Next(maplet.SizeX); int y = random.Next(maplet.SizeY); if (generatedMap[x, y].MayContainItems) { //Put it there animal.MapCharacter.Coordinate = new MapCoordinate(x, y, 0, MapType.LOCAL); generatedMap[x, y].ForcePutItemOnBlock(animal.MapCharacter); actorList.Add(animal); //Wander around does he have? animal.MissionStack.Push(new WanderMission() { LoiterPercentage = 80, WanderPoint = new MapCoordinate(animal.MapCharacter.Coordinate), WanderRectangle = new Rectangle(0, 0, generatedMap.GetLength(0), generatedMap.GetLength(1)) }); break; } } } } } } } #endregion actors = actorList.ToArray(); #region Ownership //Go through all map items - If they're not valid for this particular owner, make them inactive. foreach (var mapBlock in generatedMap) { foreach (var item in mapBlock.GetItems()) { if (!item.OwnedBy.HasFlag(owner)) { item.IsActive = false; } } } #endregion //we're done return generatedMap; }
/// <summary> /// Assigns each location to a different region /// </summary> private static void AssignRegions() { Random random = new Random(); lock (GlobalMap.lockMe) { //setting the region centres - 0 doesn't count for (int i = 1; i < regions.Length; i++) { int x = random.Next(WORLDSIZE - 1); int y = random.Next(WORLDSIZE - 1); if ((GameState.GlobalMap.GetBlockAtCoordinate(new MapCoordinate(x, y, 0, MapType.GLOBAL)).Tile as GlobalTile).Region == -1) { //not assigned //set this location as the centre regions[i].Center = new MapCoordinate(x, y, 0, MapType.GLOBAL); regions[i].Blocks.Add(GameState.GlobalMap.GetBlockAtCoordinate(new MapCoordinate(x, y, 0, MapType.GLOBAL))); (GameState.GlobalMap.GetBlockAtCoordinate(new MapCoordinate(x, y, 0, MapType.GLOBAL)).Tile as GlobalTile).Region = i; } else { i--; //try again } } } for (int x = 0; x < WORLDSIZE; x++) { int assignedRegion = -1; for (int y = 0; y < WORLDSIZE; y++) { //populate the regions lock (GlobalMap.lockMe) { //Is this tile already in a region? MapCoordinate currentCoordinate = new MapCoordinate(x, y, 0, MapType.GLOBAL); MapBlock block = GameState.GlobalMap.GetBlockAtCoordinate(currentCoordinate); //Find the closest region to the current unassigned block if ((block.Tile as GlobalTile).Region.Equals(-1)) { double minDistance = WORLDSIZE * WORLDSIZE; for (int i = 1; i <= REGIONSIZE; i++) { double distance = Math.Abs(currentCoordinate - regions[i].Center); if (distance <= minDistance) { minDistance = distance; assignedRegion = i; //this is the smallest region } } regions[assignedRegion].Blocks.Add(block); (block.Tile as GlobalTile).Region = assignedRegion; } } } } }
/// <summary> /// Generates a dungeon having a particular amount of tiers, trap rooms, guard rooms and treasure rooms /// </summary> /// <param name="tiers">How many 'layers' the dungeon contains</param> /// <param name="enemyArray">A list of enemy actors</param> /// <param name="guardRooms">The maximum amount of guardrooms contained in the dungeon</param> /// <param name="maxOwnedPopulation">For each owned room which generates enemies, the maximum amount GENERATED in each room (does not preclude patrols from entering the same room)</param> /// <param name="maxWildPopulation">For each wild room which generates enemies, the maximum amount GENERATED in each room.</param> /// <param name="ownerType">For each owned room, the type of the enemies to create</param> /// <param name="percentageOwned">The percentage of the rooms which are owned as opposed to being wild. Bear in mind that wild rooms can spawn quite a bit of enemies</param> /// <param name="pointsOfInterest">The points of interest (ie guard and treasure rooms for instance) which have been generated. Used for patrols</param> /// <param name="startPoint">The entrance start point</param> /// <param name="utilityRooms">The maximum amount of Utility rooms to generate - these might contain civilian orcs which ignore the maxOwnedPopulation value</param> /// <param name="treasureRooms">The maximum amount of teasure rooms to generate</param> /// <returns></returns> public MapBlock[,] GenerateDungeon(int tiers, int utilityRooms, int guardRooms, int treasureRooms, string ownerType, decimal percentageOwned, int maxWildPopulation, int maxOwnedPopulation, out MapCoordinate startPoint, out DRObjects.Actor[] enemyArray, out List<PointOfInterest> pointsOfInterest) { startPoint = new MapCoordinate(0, 0, 0, MapType.LOCAL); pointsOfInterest = new List<PointOfInterest>(); List<DRObjects.Actor> enemies = new List<DRObjects.Actor>(); List<CitadelRoom> rooms = new List<CitadelRoom>(); int uniqueID = 0; //Start with the root node CitadelRoom root = new CitadelRoom(); root.SquareNumber = (int)Math.Ceiling((double)WIDTH / 2); root.TierNumber = 0; root.UniqueID = uniqueID++; root.Connections.Add(-1); //this is a special id. We'll use it to create a start point rooms.Add(root); int currentTier = 1; int square = (int)Math.Ceiling((double)WIDTH / 2); CitadelRoom focusNode = root; Random random = new Random(DateTime.UtcNow.Millisecond); while (currentTier < tiers) { //Create a new node CitadelRoom newNode = new CitadelRoom(); newNode.SquareNumber = square; newNode.TierNumber = currentTier; newNode.UniqueID = uniqueID++; newNode.CitadelRoomType = CitadelRoomType.EMPTY_ROOM; //connect the focus node to this node focusNode.Connections.Add(newNode.UniqueID); newNode.Connections.Add(focusNode.UniqueID); //change the focus node focusNode = newNode; //aaaand add it to the list rooms.Add(newNode); //Now we decide whether to stay in the same tier - or increase the tier int randomNumber = random.Next(100); int siblings = rooms.Where(r => r.TierNumber.Equals(currentTier)).Count(); int treshold = 0; switch (siblings) { case 1: treshold = PROB_2; break; case 2: treshold = PROB_3; break; case 3: treshold = PROB_4; break; case 4: treshold = PROB_5; break; default: treshold = 0; break; //NEVER } if (randomNumber < treshold) { //then stay in the same place - go either left or right. Can we go in that direction? bool canGoRight = !rooms.Any(r => (r.SquareNumber.Equals(square + 1) && r.TierNumber.Equals(currentTier)) || square + 1 > WIDTH); bool canGoLeft = !rooms.Any(r => (r.SquareNumber.Equals(square - 1) && r.TierNumber.Equals(currentTier)) || square - 1 < 0); if (canGoLeft && canGoRight) { //pick one at random square += random.Next(2) == 1 ? 1 : -1; } else if (canGoLeft) { square -= 1; } else if (canGoRight) { square += 1; } else { //We've done it all currentTier++; } } else { currentTier++; } } //Now that that part is done, lets add some more paths so we turn this into a graph foreach (CitadelRoom room in rooms) { //For each room, check who is a sibling or immediatly under him. There is a 50% chance of forming a link CitadelRoom[] potentialRooms = GetPathableRooms(rooms, room.TierNumber, room.SquareNumber); foreach (CitadelRoom potentialRoom in potentialRooms) { //Is there a connection already? if (!potentialRoom.Connections.Contains(room.UniqueID)) { if (random.Next(2) == 1) { //add a connection room.Connections.Add(potentialRoom.UniqueID); potentialRoom.Connections.Add(room.UniqueID); } } } } //go through the rooms and set some as wild rooms already foreach (var room in rooms) { if (random.Next(100) > percentageOwned) { //Wild room room.CitadelRoomType = CitadelRoomType.WILD_ROOM; } } //Lets assign the rooms based on the maximum amount. //Some rooms have more probability in certain regions. //So lets divide the rooms in 3 //Favoured - x3 //Other - x2 //Unfavoured - x1 int lowerBoundary = rooms.Count / 3; int upperBoundary = 2 * rooms.Count / 3; var orderedUtilities = rooms.Where (o => o.CitadelRoomType == CitadelRoomType.EMPTY_ROOM).OrderByDescending(o => random.Next(100) * (o.UniqueID > upperBoundary ? 1 : o.UniqueID > lowerBoundary ? 2 : 3)).Where(r => r.CitadelRoomType == CitadelRoomType.EMPTY_ROOM ).ToArray().Take(utilityRooms); foreach (var room in orderedUtilities) { room.CitadelRoomType = CitadelRoomType.UTILITY_ROOM; } //Same thing for treasure rooms var orderedTreasure = rooms.Where (o => o.CitadelRoomType == CitadelRoomType.EMPTY_ROOM).OrderByDescending(o => random.Next(100) * (o.UniqueID > upperBoundary ? 3 : o.UniqueID > lowerBoundary ? 2 : 1)).Where(r => r.CitadelRoomType == CitadelRoomType.EMPTY_ROOM ).Take(treasureRooms); foreach (var room in orderedTreasure) { room.CitadelRoomType = CitadelRoomType.TREASURE_ROOM; } //And guard rooms var orderedGuard = rooms.Where (o => o.CitadelRoomType == CitadelRoomType.EMPTY_ROOM).OrderByDescending(o => random.Next(100) * (o.UniqueID > upperBoundary ? 1 : o.UniqueID > lowerBoundary ? 3 : 2)).Where(r => r.CitadelRoomType == CitadelRoomType.EMPTY_ROOM ).Take(guardRooms); foreach (var room in orderedGuard) { room.CitadelRoomType = CitadelRoomType.GUARD_ROOM; } //Now that that part is done, we put them on the actual grid. //We go for a 15x15 room and connect the items in it. //15x15 - with a gap of 7 between them for tunnels int mapWidth = ((WIDTH + 7) * 20); int mapHeight = ((tiers + 7) * 20); //Create new blocks MapBlock[,] map = new MapBlock[mapWidth, mapHeight]; for (int x = 0; x < map.GetLength(0); x++) { for (int y = 0; y < map.GetLength(1); y++) { map[x, y] = new MapBlock() { Tile = new MapItem() { Coordinate = new MapCoordinate(x, y, 0, DRObjects.Enums.MapType.LOCAL), MayContainItems = false } }; } } LocalMapGenerator gen = new LocalMapGenerator(); LocalMapXMLParser xmlGen = new LocalMapXMLParser(); //Start generating the maps and then stitch them upon the main map foreach (CitadelRoom room in rooms) { MapBlock[,] gennedMap = null; string tag = String.Empty; switch (room.CitadelRoomType) { case CitadelRoomType.EMPTY_ROOM: tag = "Empty Dungeon"; break; case CitadelRoomType.GUARD_ROOM: tag = "Guard Dungeon"; break; case CitadelRoomType.UTILITY_ROOM: tag = "Utility Dungeon"; break; case CitadelRoomType.TREASURE_ROOM: tag = "Treasure Dungeon"; break; case CitadelRoomType.WILD_ROOM: tag = "Empty Dungeon"; break; default: throw new NotImplementedException("Dungeon Room " + room.CitadelRoomType + " not planned for yet."); } //Generate it :) Maplet maplet = xmlGen.ParseMapletFromTag(tag); Actor[] acts = null; MapletActorWanderArea[] wanderAreas = null; MapletPatrolPoint[] patrolPoints = null; MapletFootpathNode[] footPath = null; gennedMap = gen.GenerateMap(25, null, maplet, true, "", OwningFactions.ORCS ,out acts,out wanderAreas,out patrolPoints,out footPath); enemies.AddRange(acts); //Is it a treasure room? if (room.CitadelRoomType == CitadelRoomType.TREASURE_ROOM) { //Generate some loot GenerateLoot(gennedMap, room.TierNumber); } PointOfInterest mapletInterest = null; if (room.CitadelRoomType == CitadelRoomType.GUARD_ROOM || room.CitadelRoomType == CitadelRoomType.TREASURE_ROOM) { //This will be a point of interest. Select a random walkable point in the room and mark the place as such for (int tryAmount = 0; tryAmount < 50; tryAmount++) { //Try for a maximum of 50 times int x = random.Next(gennedMap.GetLength(0)); int y = random.Next(gennedMap.GetLength(1)); if (gennedMap[x, y].Tile.MayContainItems) { //Put this as the point PointOfInterest interest = new PointOfInterest(); interest.Coordinate = new MapCoordinate(x, y, 0, MapType.LOCAL); if (room.CitadelRoomType == CitadelRoomType.GUARD_ROOM) { interest.Type = PointOfInterestType.GUARD_ROOM; } else if (room.CitadelRoomType == CitadelRoomType.TREASURE_ROOM) { interest.Type = PointOfInterestType.TREASURE; } pointsOfInterest.Add(interest); mapletInterest = interest; break; } } } DRObjects.Actor[] roomEnemies = new DRObjects.Actor[] { }; if (room.CitadelRoomType == CitadelRoomType.GUARD_ROOM || room.CitadelRoomType == CitadelRoomType.TREASURE_ROOM) { //Create an amount of enemies - level doesn't matter, we'll regen later gennedMap = gen.GenerateEnemies(gennedMap, random.Next(maxOwnedPopulation), ownerType, out roomEnemies, 10); enemies.AddRange(roomEnemies); } if (room.CitadelRoomType == CitadelRoomType.WILD_ROOM) { //Create an amount of wild enemies - let's get a random type for this room. This will be of level 5. Later we'll have proper wildlife string type = ActorGeneration.GetEnemyType(false); gennedMap = gen.GenerateEnemies(gennedMap, random.Next(maxWildPopulation), type, out roomEnemies, 5); //go through all of room enemies and set them to idle foreach (var enemy in roomEnemies) { enemy.MissionStack.Clear(); enemy.MissionStack.Push(new IdleMission()); } enemies.AddRange(roomEnemies); } //fit her onto the main map int xIncreaser = room.SquareNumber * 20; int yIncreaser = (room.TierNumber * 20) + 3; //Fix the patrol points of any enemies foreach (Actor enemy in roomEnemies.Union(acts)) { if (enemy.MissionStack.Count != 0 && enemy.MissionStack.Peek().MissionType == DRObjects.ActorHandling.ActorMissionType.WANDER) { //Change patrol point MapCoordinate point = (enemy.MissionStack.Peek() as WanderMission).WanderPoint; point.X += xIncreaser; point.Y += yIncreaser; //Change the rectangle x and y too Rectangle rect = (enemy.MissionStack.Peek() as WanderMission).WanderRectangle; rect.X = xIncreaser; rect.Y = yIncreaser; (enemy.MissionStack.Peek() as WanderMission).WanderRectangle = rect; //apparently rectangles are immutable or something } } //Update the point of interest if there is one if (mapletInterest != null) { mapletInterest.Coordinate.X += xIncreaser; mapletInterest.Coordinate.Y += yIncreaser; } for (int x = 0; x < gennedMap.GetLength(0); x++) { for (int y = 0; y < gennedMap.GetLength(1); y++) { map[x + xIncreaser, y + yIncreaser] = gennedMap[x, y]; map[x + xIncreaser, y + yIncreaser].Tile.Coordinate = new MapCoordinate(x + xIncreaser, y + yIncreaser, 0, DRObjects.Enums.MapType.LOCAL); foreach (var item in map[x + xIncreaser, y + yIncreaser].GetItems()) { item.Coordinate = new MapCoordinate(x + xIncreaser, y + yIncreaser, 0, DRObjects.Enums.MapType.LOCAL); } } } //Lets draw the connections - only the ones who's rooms we've drawn yet ItemFactory.ItemFactory factory = new ItemFactory.ItemFactory(); foreach (var connection in room.Connections.Where(c => c < room.UniqueID)) { if (connection == -1) { //Entrance hall! //Create a line of 3 at the bottom and return the coordinates int topEdgeY = yIncreaser; int bottomEdgeXMin = 0 + xIncreaser; int bottomEdgeXMax = gennedMap.GetLength(0) + xIncreaser; //Find the start int xStart = (bottomEdgeXMax - bottomEdgeXMin) / 2 + bottomEdgeXMin; //Set the start point startPoint = new MapCoordinate(xStart + 1, topEdgeY - 2, 0, MapType.LOCAL); //Put the 'leave town' item on it map[startPoint.X, startPoint.Y].ForcePutItemOnBlock(new LeaveTownItem()); int x = xStart; int y = topEdgeY; //go 3 steps down for (int a = 0; a < 3; a++) { for (int x1 = 0; x1 < 3; x1++) { //Draw! map[x + x1, y].Tile = factory.CreateItem("TILES", 25); map[x + x1, y].Tile.Coordinate = new MapCoordinate(x + x1, y, 0, MapType.LOCAL); } y--; //move down } y = topEdgeY; //Walk back do { for (int x1 = 0; x1 < 3; x1++) { //Draw! map[x + x1, y].Tile = factory.CreateItem("TILES", 25); map[x + x1, y].Tile.Coordinate = new MapCoordinate(x + x1, y, 0, MapType.LOCAL); } y++; //move up } while (x >= 0 && y < map.GetLength(1) && !map[x, y].Tile.MayContainItems); //Put the spikes at the entrance int dummy = -1; map[xStart, topEdgeY - 2].ForcePutItemOnBlock(factory.CreateItem(Archetype.MUNDANEITEMS, "spikes", out dummy)); map[xStart + 2, topEdgeY - 2].ForcePutItemOnBlock(factory.CreateItem(Archetype.MUNDANEITEMS, "spikes", out dummy)); continue; } //Identify the room to be connected with var roomToBeConnected = rooms.Where(r => r.UniqueID.Equals(connection)).FirstOrDefault(); //Determine the direction relative to the current room if (roomToBeConnected.SquareNumber > room.SquareNumber) { //RIGHT //Find the rightmost edge of the room and start... somewhere int rightEdgeX = gennedMap.GetLength(0) + xIncreaser; int rightEdgeYMin = 0 + yIncreaser; int rightEdgeYMax = gennedMap.GetLength(1) + yIncreaser; //Pick a start at random int yStart = random.Next(rightEdgeYMax - rightEdgeYMin - 2) + rightEdgeYMin; //Now 'walk' from ystart-ystart+3 until you hit on something which has a block in it int x = rightEdgeX; int y = yStart; while (x < map.GetLength(0) && y < map.GetLength(1) && !map[x, y].Tile.MayContainItems) { for (int y1 = 0; y1 < 3; y1++) { //Draw! map[x, y + y1].Tile = factory.CreateItem("TILES", 25); map[x, y + y1].Tile.Coordinate = new MapCoordinate(x, y + y1, 0, MapType.LOCAL); } x++; //increment x } x = rightEdgeX - 1; //now lets walk backwards too while (x >= 0 && x < map.GetLength(0) && y >= 0 && y < map.GetLength(1) && !map[x, y].Tile.MayContainItems) { for (int y1 = 0; y1 < 3; y1++) { //Draw! map[x, y + y1].Tile = factory.CreateItem("TILES", 25); map[x, y + y1].Tile.Coordinate = new MapCoordinate(x, y + y1, 0, MapType.LOCAL); } x--; //walk back } } else if (roomToBeConnected.SquareNumber < room.SquareNumber) { //LEFT //Find the leftMose edge of the room and start... somewhere int leftEdgeX = xIncreaser; int leftEdgeYMin = 0 + yIncreaser; int leftEdgeYMax = gennedMap.GetLength(1) + yIncreaser; //Pick a start at random int yStart = random.Next(leftEdgeYMax - leftEdgeYMin - 2) + leftEdgeYMin; //Now 'walk' from ystart-ystart+3 until you hit on something which has a block in it int x = leftEdgeX; int y = yStart; do { for (int y1 = 0; y1 < 3; y1++) { //Draw! map[x, y + y1].Tile = factory.CreateItem("TILES", 25); map[x, y + y1].Tile.Coordinate = new MapCoordinate(x, y + y1, 0, MapType.LOCAL); } x--; //decrement x } while (x >= 0 && x < map.GetLength(0) && y >= 0 && y < map.GetLength(1) && !map[x, y].Tile.MayContainItems); x = leftEdgeX + 1; //walk backwards do { for (int y1 = 0; y1 < 3; y1++) { //Draw! map[x, y + y1].Tile = factory.CreateItem("TILES", 25); map[x, y + y1].Tile.Coordinate = new MapCoordinate(x, y + y1, 0, MapType.LOCAL); } x++; //walk back } while (x >= 0 && x < map.GetLength(0) && y >= 0 && y < map.GetLength(1) && !map[x, y].Tile.MayContainItems); } else if (roomToBeConnected.TierNumber < room.TierNumber) { //BOTTOM //Find the bottommost edge of the room and start... somewhere int bottomEdgeY = yIncreaser; int bottomEdgeXMin = 0 + xIncreaser; int bottomEdgeXMax = gennedMap.GetLength(0) + xIncreaser; //Pick a start at random int xStart = random.Next(bottomEdgeXMax - bottomEdgeXMin - 2) + bottomEdgeXMin; //Now 'walk' from xstart-xstart+3 until you hit on something which has a block in it int x = xStart; int y = bottomEdgeY; do { for (int x1 = 0; x1 < 3; x1++) { //Draw! map[x + x1, y].Tile = factory.CreateItem("TILES", 25); map[x + x1, y].Tile.Coordinate = new MapCoordinate(x + x1, y, 0, MapType.LOCAL); } y--; //decrement y } while (x >= 0 && x < map.GetLength(0) && y >= 0 && y < map.GetLength(1) && !map[x, y].Tile.MayContainItems); y = bottomEdgeY + 1; //Walk backwards do { for (int x1 = 0; x1 < 3; x1++) { //Draw! map[x + x1, y].Tile = factory.CreateItem("TILES", 25); map[x + x1, y].Tile.Coordinate = new MapCoordinate(x + x1, y, 0, MapType.LOCAL); } y++; //walk back } while (x >= 0 && x < map.GetLength(0) && y >= 0 && y < map.GetLength(1) && !map[x, y].Tile.MayContainItems); } else if (roomToBeConnected.TierNumber > room.TierNumber) { //TOP - Won't ever happen - but sure //Find the topmost edge of the room and start... somewhere int topEdgeY = yIncreaser + gennedMap.GetLength(1); int bottomEdgeXMin = 0 + xIncreaser; int bottomEdgeXMax = gennedMap.GetLength(0) + xIncreaser; //Pick a start at random int xStart = random.Next(bottomEdgeXMax - bottomEdgeXMin - 2) + bottomEdgeXMin; //Now 'walk' from xstart-xstart+3 until you hit on something which has a block in it int x = xStart; int y = topEdgeY; do { bool holed = false; for (int x1 = 0; x1 < 3; x1++) { if (!holed) { //Have a chance of putting in a hole if (random.Next(8) == 0) { holed = true; continue; //don't put in a tile } } //Draw! map[x + x1, y].Tile = factory.CreateItem("TILES", 25); map[x + x1, y].Tile.Coordinate = new MapCoordinate(x + x1, y, 0, MapType.LOCAL); } y++; //move up } while (x >= 0 && x < map.GetLength(0) && y >= 0 && y < map.GetLength(1) && !map[x, y].Tile.MayContainItems); //walk back y = topEdgeY + 1; do { for (int x1 = 0; x1 < 3; x1++) { //Draw! map[x + x1, y].Tile = factory.CreateItem("TILES", 25); map[x + x1, y].Tile.Coordinate = new MapCoordinate(x + x1, y, 0, MapType.LOCAL); } y--; //move down } while (x >= 0 && x < map.GetLength(0) && y >= 0 && y < map.GetLength(1) && !map[x, y].Tile.MayContainItems); } } } //We need to fix the enemies to conform to the standards ConformEnemies(enemies); enemyArray = enemies.ToArray(); return map; }