World Builder – MainForm.cs
#region Using Statements using System; using System.IO; using System.Reflection; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; using System.Xml; using System.Xml.Serialization; using System.Drawing; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Storage; using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate; using ParadigmEngine; using ParadigmEngine.TileEngine; using ParadigmEngine.ParticleSystem; using ParadigmEngine.DeferredRenderer; using ParadigmCommon; #endregion namespace Paradigm { /// <summary> /// Main editor form. Handles tile selection as well as XNA pane for map editing. /// </summary> public partial class MainForm : Form, IXnaGraphicsForm { #region Fields //XNA private SpriteBatch spriteBatch; private MouseState prevMouseState; private static WinFormsContentManager contentManager; private DeferredRenderer deferredRenderer; private RenderTarget2D colorTarget; private RenderTarget2D normalTarget; private RenderTarget2D depthTarget; /// <summary> /// The resolution we had prior to a resize /// </summary> private Vector2 prevRenderResolution; /// <summary> /// Has the XNA pane been initialized? /// </summary> private bool xnaInitialized = false; // GDI private Image tilesImage; private Pen gridPen = new Pen(System.Drawing.Color.Black, 1); private Pen selectedPen = new Pen(System.Drawing.Color.Purple, 3.5f); // Plugin management private List<string> pluginAssemblies = new List<string>(); private List<ParadigmPlugin> loadedPlugins = new List<ParadigmPlugin>(); // Map Engine private Map map; private Texture2D mapTiles; private string tileSet; private const string defaultMapAssetType = "ParadigmEngine.TileEngine.Map"; private string mapAssetType = "ParadigmEngine.TileEngine.Map"; private string mapID; private Microsoft.Xna.Framework.Graphics.Color mapBgColor = Microsoft.Xna.Framework.Graphics.Color.CornflowerBlue; private int mapTileSize = 32; private int mapTileWidth = 50; private int mapTileHeight = 50; private int mapPixelWidth = 0; private int mapPixelHeight = 0; private List<bool> mapForegroundLayers = new List<bool>(); private List<PortalInfo> mapPortalInfoList; private List<ShaderRegionInfo> mapShaderRegionInfoList; private List<MapEventInfo> mapEventInfoList; private List<TerrainInfo> mapTerrainInfoList; // Map Editor private const int minLayerCount = 3; private int layerCount = 3; bool[] defaultForegroundData = { false, false, true }; private bool loadedTexture = false; private MapEntity selectedEntity; private AnimatedTile selectedAnimatedTile; private MouseSelectionHandler mouseSelectionHandler; private LightLayerInputHandler lightLayerInputHandler; private int tilesetXCount; private int tilesetYCount; private CollisionLayer collisionLayer; private HazardLayer hazardLayer; private EntityLayer entityLayer; private ParticleLayer particleLayer; private PortalLayer portalLayer; private ShaderEffectLayer shaderEffectLayer; private TerrainLayer terrainLayer; private EventLayer eventLayer; private TileCoordLayer tileCoordLayer; private LightLayer lightLayer; // Editor Support private SpriteFont spriteFont; private SelectionReticle selectionReticle; private Microsoft.Xna.Framework.Rectangle prevSelectionRectBounds; private const int bufferTiles = 5; // Particle system private ParticleEmitter selectedEmitter; private Texture2D particleTexture; private ParticleManager particleManager; // initialize brush to a null brush private EditorBrush currentBrush = EditorBrush.None; // initialize layer to 0 private int currentLayer = 0; // tile selector private SelectedTile selectedTile; private bool selectedMultipleTiles; private Vector2 selectedStartTile; private Vector2 selectedEndTile; private Vector2 selectedTileRange; // matrix scale private int prevWheelValue; private float mapScale = 1.0f; // game timer private System.Diagnostics.Stopwatch timer; private TimeSpan lastUpdate; #endregion #region Attributes /// <summary> /// GraphicsDevice /// </summary> public GraphicsDevice GraphicsDevice { get { return xnaDisplay.GraphicsDevice; } } /// <summary> /// Public content manager /// </summary> public static WinFormsContentManager ContentManager { get { return contentManager; } } /// <summary> /// Currently Loaded Map /// </summary> public Map Map { get { return map; } } /// <summary> /// The actual XNA asset type this map will be exported as /// </summary> public string MapAssetType { get { return mapAssetType; } set { mapAssetType = value; } } /// <summary> /// Map ID /// </summary> public string MapID { get { return mapID; } set { mapID = value; } } /// <summary> /// Background color of map /// </summary> public Microsoft.Xna.Framework.Graphics.Color MapBgColor { get { return mapBgColor; } set { mapBgColor = value; } } /// <summary> /// Tile size /// </summary> public int MapTileSize { get { return mapTileSize; } set { mapTileSize = value; } } /// <summary> /// Map width in tiles /// </summary> public int MapTileWidth { get { return mapTileWidth; } set { mapTileWidth = value; } } /// <summary> /// Map height in tiles /// </summary> public int MapTileHeight { get { return mapTileHeight; } set { mapTileHeight = value; } } /// <summary> /// Map width in pixels /// </summary> public int MapPixelWidth { get { return mapPixelWidth; } set { mapPixelWidth = value; } } /// <summary> /// Map height in pixels /// </summary> public int MapPixelHeight { get { return mapPixelHeight; } set { mapPixelHeight = value; } } /// <summary> /// List defining layers tagged as foreground /// </summary> public List<bool> MapForegroundLayers { get { return mapForegroundLayers; } set { mapForegroundLayers = value; } } /// <summary> /// List of PortalInfo objects from map /// </summary> public List<PortalInfo> PortalInfoList { get { return mapPortalInfoList; } set { mapPortalInfoList = value; } } /// <summary> /// List of ShaderRegionInfo objects from map /// </summary> public List<ShaderRegionInfo> ShaderRegionInfoList { get { return mapShaderRegionInfoList; } set { mapShaderRegionInfoList = value; } } /// <summary> /// List of TerrainInfo objects from map /// </summary> public List<TerrainInfo> TerrainInfoList { get { return mapTerrainInfoList; } set { mapTerrainInfoList = value; } } /// <summary> /// List of MapEventInfo objects from map /// </summary> public List<MapEventInfo> EventInfoList { get { return mapEventInfoList; } set { mapEventInfoList = value; } } /// <summary> /// The dimensions of the XNA pane /// </summary> public Vector2 TileDisplayDimensions { get { return new Vector2((float)xnaDisplay.Width, (float)xnaDisplay.Height); } } /// <summary> /// is the map texture loaded? /// </summary> public bool LoadedTexture { get { return loadedTexture; } } /// <summary> /// Selected map entitiy /// </summary> public MapEntity SelectedEntity { get { return selectedEntity; } set { selectedEntity = value; } } /// <summary> /// Selected animated tile /// </summary> public AnimatedTile SelectedAnimatedTile { get { return selectedAnimatedTile; } set { selectedAnimatedTile = value; } } /// <summary> /// Selected particle emitter /// </summary> public ParticleEmitter SelectedEmitter { get { return selectedEmitter; } set { selectedEmitter = value; } } /// <summary> /// Particle manager /// </summary> public ParticleManager ParticleManager { get { return particleManager; } } /// <summary> /// Number of map layers /// </summary> public int LayerCount { get { return layerCount; } set { layerCount = value; } } /// <summary> /// Minimum layers a map can have /// </summary> public int MinLayerCount { get { return minLayerCount; } } /// <summary> /// Selection reticle /// </summary> public SelectionReticle SelectionReticle { get { return selectionReticle; } set { selectionReticle = value; } } /// <summary> /// Paradigm particle texture /// </summary> public Texture2D ParticleTexture { get { return particleTexture; } } /// <summary> /// The current map scale value /// </summary> public float MapScale { get {return mapScale;} } #endregion #region Form Control Event Methods public MainForm() { InitializeComponent(); // form event handlers xnaDisplay.OnInitialize += new EventHandler(xnaDisplay_OnInitialize); xnaDisplay.OnDraw += new EventHandler(xnaDisplay_OnDraw); xnaDisplay.MouseEnter += new EventHandler(xnaDisplay_MouseEnter); xnaDisplay.MouseLeave += new EventHandler(xnaDisplay_MouseLeave); xnaDisplay.MouseDown += new MouseEventHandler(xnaDisplay_MouseDown); xnaDisplay.MouseUp += new MouseEventHandler(xnaDisplay_MouseUp); xnaDisplay.Layout += new LayoutEventHandler(xnaDisplay_SizeChanged); currentBrushBox.SelectedIndexChanged += new EventHandler(currentBrushBox_SelectedIndexChanged); currentLayerBox.SelectedIndexChanged += new EventHandler(currentLayerBox_SelectedIndexChanged); tileSelectionBox.Paint += new PaintEventHandler(tileSelectionBox_Paint); tileSelectionBox.MouseEnter += new EventHandler(tileSelectionBox_MouseEnter); tileSelectionBox.MouseLeave += new EventHandler(tileSelectionBox_MouseLeave); tileSelectionBox.MouseDown += new MouseEventHandler(tileSelectionBox_MouseDown); tileSelectionBox.MouseUp += new MouseEventHandler(tileSelectionBox_MouseUp); toolStrip1.MouseEnter += new EventHandler(toolStrip1_MouseEnter); hScrollBar1.Scroll += delegate { xnaDisplay.Invalidate(); }; vScrollBar1.Scroll += delegate { xnaDisplay.Invalidate(); }; // redraw the tile display when application is idle Application.Idle += delegate { xnaDisplay.Invalidate(); }; Application.Idle += delegate { tileSelectionBox.Invalidate(); }; // load available plugins LoadPlugins(); InitializeContentBuilder(FileHelper.GetAssemblyDirectory()); LoadDefaultSettings(); mapPixelWidth = mapTileWidth * mapTileSize; mapPixelHeight = mapTileHeight * mapTileSize; // set default foreground data mapForegroundLayers.AddRange(defaultForegroundData); // start timer timer = new System.Diagnostics.Stopwatch(); lastUpdate = new TimeSpan(); ParadigmEngine.Rand.random = new Random(); ParadigmCommon.Rand.random = new Random(); // initialize static map selections class to hold data for supporting selection form MapSelections.Initialize(this); timer.Start(); } /// <summary> /// Load default editor options /// </summary> private void LoadDefaultSettings() { string fileName = FileHelper.GetAssemblyDirectory() + "/options.xml"; // if the options file exists, load the options if (File.Exists(fileName)) { DefaultSettings settings = new DefaultSettings(); FileHelper.DeserializeXMLObject<DefaultSettings>(fileName, out settings); mapAssetType = settings.MapAssetType; mapBgColor = settings.MapBgColor; mapTileWidth = settings.MapTileWidth; mapTileHeight = settings.MapTileHeight; mapTileSize = settings.MapTileSize; } else // if not, create the options file from those saved in the form { SaveDefaultSettings(); } } /// <summary> /// Saves the default editor settings /// </summary> /// <param name="assemblyLocation"></param> private void SaveDefaultSettings() { string fileName = FileHelper.GetAssemblyDirectory() + "/options.xml"; DefaultSettings settings = new DefaultSettings(mapAssetType, mapBgColor, mapTileSize, mapTileWidth, mapTileHeight); FileHelper.SerializeXMLObject<DefaultSettings>(settings, fileName); } /// <summary> /// Initialize the content builder /// </summary> private void InitializeContentBuilder(string assemblyLocation) { List<string> buildAssemblies = new List<string>(); // determine relative locations for map engine libraries string dataDllPath = assemblyLocation + "\\ParadigmData.dll"; string engineDllPath = assemblyLocation + "\\ParadigmEngine.dll"; string commonDllPath = assemblyLocation + "\\ParadigmCommon.dll"; buildAssemblies.Add(Path.GetFullPath(dataDllPath)); buildAssemblies.Add(Path.GetFullPath(engineDllPath)); buildAssemblies.Add(Path.GetFullPath(commonDllPath)); // create new content builder with new reference libraries //ContentBuilder contentBuilder = new ContentBuilder( // new string[] { Path.GetFullPath(engineDllPath), Path.GetFullPath(extensionDllPath) } // ); foreach (string assembly in pluginAssemblies) buildAssemblies.Add(assembly); ContentBuilder builder = new ContentBuilder(buildAssemblies); // create content manager ContentManager manager = new ContentManager(xnaDisplay.Services, builder.OutputDirectory); /* Not using HDD based cache * // we no longer want to use the builders output directory as it is // constantly cleared. since we want to dynamically load content, // we are going to switch to the new cache system. ContentManager manager = new ContentManager(xnaDisplay.Services, builder.CacheDirectory); */ contentManager = new WinFormsContentManager(builder, manager); } private void InitializeDeferredRenderer() { string buildError; string currentDirectory = FileHelper.GetAssemblyDirectory(); deferredRenderer = new DeferredRenderer(GraphicsDevice, true); Effect normalEffect = contentManager.LoadContent<Effect> (currentDirectory + "/Content/Shaders/DeferredLighting.fx", null, "EffectProcessor", out buildError, false, false); Effect combineLightMapsEffect = contentManager.LoadContent<Effect> (currentDirectory + "/Content/Shaders/CombineLightMaps.fx", null, "EffectProcessor", out buildError, false, false); Effect finalLightingEffect = contentManager.LoadContent<Effect> (currentDirectory + "/Content/Shaders/CombineLighting.fx", null, "EffectProcessor", out buildError, false, false); deferredRenderer.Initialize(normalEffect, combineLightMapsEffect, finalLightingEffect); } /// <summary> /// Change Current Brush /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void currentBrushBox_SelectedIndexChanged(object sender, EventArgs e) { switch (Convert.ToString(currentBrushBox.SelectedItem)) { case "None": currentBrush = EditorBrush.None; break; case "Tile Draw": currentBrush = EditorBrush.Tile_Draw; break; case "Tile Layer Fill": currentBrush = EditorBrush.Tile_Layer_Fill; break; case "Animated Tile Draw": currentBrush = EditorBrush.Animated_Tile_Draw; break; case "Animated Tile Layer Fill": currentBrush = EditorBrush.Animated_Tile_Layer_Fill; break; case "Erase Tiles": currentBrush = EditorBrush.Tile_Erase; break; case "Collision - None": currentBrush = EditorBrush.Collision_Passable; break; case "Collision - Impassible": currentBrush = EditorBrush.Collision_Impassable; break; case "Collision - Slope Down": currentBrush = EditorBrush.Collision_SlopeDown; break; case "Collision - Slope Up": currentBrush = EditorBrush.Collision_SlopeUp; break; case "Collision - Platform": currentBrush = EditorBrush.Collision_Platform; break; case "Collision - Abnormal": currentBrush = EditorBrush.Collision_Abnormal; break; case "Collision - NPC": currentBrush = EditorBrush.Collision_NPC; break; case "Entity Draw": currentBrush = EditorBrush.Entity_Draw; break; case "Entity Erase": currentBrush = EditorBrush.Entity_Erase; break; case "Mass Entity Erase": currentBrush = EditorBrush.Mass_Entity_Erase; break; case "Particle Emitter Draw": currentBrush = EditorBrush.Particle_Emitter_Draw; break; case "Particle Emitter Erase": currentBrush = EditorBrush.Particle_Emitter_Erase; break; case "Portal Draw": currentBrush = EditorBrush.Portal_Draw; break; case "Event Region Draw": currentBrush = EditorBrush.Event_Region_Draw; break; case "Shader Region Draw": currentBrush = EditorBrush.Shader_Region_Draw; break; case "Terrain Draw": currentBrush = EditorBrush.Terrain_Draw; break; case "Terrain Erase": currentBrush = EditorBrush.Terrain_Erase; break; case "Hazard - None": currentBrush = EditorBrush.Hazard_None; break; case "Lighting": currentBrush = EditorBrush.Lighting; break; default: break; } } private void currentLayerBox_SelectedIndexChanged(object sender, EventArgs e) { currentLayer = currentLayerBox.SelectedIndex; } private void toolStrip1_MouseEnter(object sender, EventArgs e) { if (ActiveForm == this) Mouse.WindowHandle = toolStrip1.Handle; } private void xnaDisplay_MouseLeave(object sender, EventArgs e) { if (ActiveForm == this) Mouse.WindowHandle = this.Handle; } private void xnaDisplay_MouseEnter(object sender, EventArgs e) { if (ActiveForm == this) { xnaDisplay.Focus(); Mouse.WindowHandle = xnaDisplay.Handle; } } /// <summary> /// Mouse down. Ensures mouse handle is set to XNA Display /// </summary> private void xnaDisplay_MouseDown(object sender, MouseEventArgs e) { Mouse.WindowHandle = xnaDisplay.Handle; } /// <summary> /// Mouse Up. Displays context menu depending on brush /// </summary> private void xnaDisplay_MouseUp(object sender, MouseEventArgs e) { // show context menu if right mouse button is clicked if (e.Button == MouseButtons.Right) { // get mouse loc System.Drawing.Point p = new System.Drawing.Point(e.X, e.Y); // determine context menu by tag switch (currentBrush) { // Iterate through all map entities from back to front. If we // find a collision, that will be the entity we are editing. case EditorBrush.Entity_Draw: MapEntity entity = null; for (int i = entityLayer.EntityList.Count - 1; i >= 0; i--) { if (entityLayer.EntityList[i].BoundingRectangle.Intersects(selectionReticle.BoundingRectangle)) { entity = entityLayer.EntityList[i]; break; } } if (entity != null) { prevSelectionRectBounds = selectionReticle.BoundingRectangle; entityIDCM.Show(xnaDisplay, p); } break; } } } void xnaDisplay_SizeChanged(object sender, EventArgs e) { if (!xnaInitialized) return; int width = GraphicsDevice.DisplayMode.Width; int height = GraphicsDevice.DisplayMode.Height; // get new resolution Vector2 newResolution = new Vector2((float)width,(float)height); // only progress if we ACTUALLY changed resoltuion if (newResolution == prevRenderResolution) return; colorTarget.Dispose(); normalTarget.Dispose(); depthTarget.Dispose(); // recreate render targets on size change colorTarget = new RenderTarget2D(GraphicsDevice, width, height, 1, GraphicsDevice.PresentationParameters.BackBufferFormat); normalTarget = new RenderTarget2D(GraphicsDevice, width, height, 1, GraphicsDevice.PresentationParameters.BackBufferFormat); depthTarget = new RenderTarget2D(GraphicsDevice, width, height, 1, GraphicsDevice.PresentationParameters.BackBufferFormat); if (deferredRenderer.IsInitialized) deferredRenderer.UpdateResolution(GraphicsDevice); prevRenderResolution = newResolution; } /// <summary> /// Form load /// </summary> private void MainForm_Load(object sender, EventArgs e) { GenerateNewMap(); UpdateLayers(); // create dummy selected tile selectedTile = new SelectedTile(-1, Vector2.Zero, Microsoft.Xna.Framework.Rectangle.Empty); } private void editEntityIDCMItem_Click(object sender, EventArgs e) { MapEntity entity = null; for (int i = entityLayer.EntityList.Count - 1; i >= 0; i--) { if (entityLayer.EntityList[i].BoundingRectangle.Intersects(prevSelectionRectBounds)) { entity = entityLayer.EntityList[i]; break; } } if (entity != null) { EntityIDForm dialog = new EntityIDForm(entity.ID); dialog.TopMost = true; dialog.ShowDialog(); if (dialog.DialogResult == DialogResult.OK) { try { entity.ID = dialog.EntityID; } catch (System.Exception ex) { MessageBox.Show(ex.Message); } } } } #endregion #region XNA /// <summary> /// Initialize Tile Display /// </summary> private void xnaDisplay_OnInitialize(object sender, EventArgs e) { spriteBatch = new SpriteBatch(GraphicsDevice); collisionLayer = new CollisionLayer(this, GraphicsDevice); hazardLayer = new HazardLayer(this, GraphicsDevice); entityLayer = new EntityLayer(this, GraphicsDevice); particleLayer = new ParticleLayer(this, GraphicsDevice); portalLayer = new PortalLayer(this, GraphicsDevice); shaderEffectLayer = new ShaderEffectLayer(this, GraphicsDevice); terrainLayer = new TerrainLayer(this, GraphicsDevice); eventLayer = new EventLayer(this, GraphicsDevice); tileCoordLayer = new TileCoordLayer(this, GraphicsDevice); lightLayer = new LightLayer(this, GraphicsDevice); mouseSelectionHandler = new MouseSelectionHandler(this, GraphicsDevice); lightLayerInputHandler = new LightLayerInputHandler(this, lightLayer); string currentDirectory = FileHelper.GetAssemblyDirectory(); string buildError; // load sprite font spriteFont = contentManager.LoadContent<SpriteFont> (currentDirectory + "/Content/font.spritefont", null, "FontDescriptionProcessor", out buildError, false, false); // load selection reticle texture Texture2D selectionReticleTex = contentManager.LoadContent<Texture2D> (currentDirectory + "/Content/reticle.png", null, null, out buildError, false, false); selectionReticle = new SelectionReticle(selectionReticleTex, mapTileSize); // load particle texture and manager particleTexture = contentManager.LoadContent<Texture2D> (currentDirectory + "/Content/paradigm_particles.png", null, null, out buildError, false, false); particleManager = new ParticleManager(spriteBatch); EntityRenderer.Initialize(); //int width = xnaDisplay.Width; //int height = xnaDisplay.Height; int width = GraphicsDevice.DisplayMode.Width; int height = GraphicsDevice.DisplayMode.Height; // save resolution prevRenderResolution = new Vector2((float)width, (float)height); // set up render targets on size change colorTarget = new RenderTarget2D(GraphicsDevice, width, height, 1, GraphicsDevice.PresentationParameters.BackBufferFormat); normalTarget = new RenderTarget2D(GraphicsDevice, width, height, 1, GraphicsDevice.PresentationParameters.BackBufferFormat); depthTarget = new RenderTarget2D(GraphicsDevice, width, height, 1, GraphicsDevice.PresentationParameters.BackBufferFormat); InitializeDeferredRenderer(); xnaInitialized = true; } /// <summary> /// Method called before every frame draw. Similar to the XNA Update() method. /// </summary> private void xnaDisplay_OnDraw(object sender, EventArgs e) { // total game time TimeSpan total = timer.Elapsed; // time elapsed since the last update TimeSpan elapsed = total - lastUpdate; // create the game time using those values GameTime gameTime = new GameTime(total, elapsed, total, elapsed); // clear the display xnaDisplay.GraphicsDevice.Clear(mapBgColor); if ((map != null) && (map.IsInitialized) && (mapTiles != null)) { Logic(gameTime); Render(gameTime); } lastUpdate = total; } /// <summary> /// Perform rendering logic for XNA /// TODO: Clean up input handling /// </summary> private void Logic(GameTime gameTime) { // TODO // Figure out why there is not a scroll bar buffer anymore // update scroll bars hScrollBar1.Minimum = (xnaDisplay.Width / 2) - (int)(map.Tile_Size * bufferTiles); vScrollBar1.Minimum = (xnaDisplay.Height / 2) - (int)(map.Tile_Size * bufferTiles); hScrollBar1.Maximum = (int)(map.Width * map.Tile_Size * mapScale) - hScrollBar1.Minimum + (int)(map.Tile_Size * bufferTiles); vScrollBar1.Maximum = (int)(map.Height * map.Tile_Size * mapScale) - vScrollBar1.Minimum + (int)(map.Tile_Size * bufferTiles); MouseState mouseState = Mouse.GetState(); // adjust scale CalculateZoom(); // The mouse x and y positions are returned relative to the // upper-left corner of the game window. // only edit the map if the mouse is within bounds, the mouse button is down, // and the mouse handle is set to this xna control Microsoft.Xna.Framework.Rectangle viewportRect; viewportRect.X = 0; viewportRect.Y = 0; viewportRect.Width = mapTileSize * mapTileWidth; viewportRect.Height = mapTileSize * mapTileHeight; // get tile location of mouse cursor Vector2 mouseTileLocation = XnaMouseTileLocation(mouseState); Microsoft.Xna.Framework.Point mouseLoc; mouseLoc.X = (int)(mouseTileLocation.X * mapTileSize); mouseLoc.Y = (int)(mouseTileLocation.Y * mapTileSize); tileCoordLabel.Text = "Tile Coordinates: " + mouseTileLocation.X + ", " + mouseTileLocation.Y; mouseCoordLabel.Text = "Mouse Coordinates: " + mouseLoc.X + ", " + mouseLoc.Y; Vector2 worldPosition = new Vector2(mouseTileLocation.X * mapTileSize, mouseTileLocation.Y * mapTileSize); // update selection reticle selectionReticle.UpdatePosition(new Vector2(mouseTileLocation.X * mapTileSize, mouseTileLocation.Y * mapTileSize)); // selected entity - calculate position based on mouse coords if (selectedEntity != null) { selectedEntity.WorldPosition = new Vector2(worldPosition.X + selectedEntity.Sprite.Origin.X, worldPosition.Y); } // selected animated tile - calculate position based on mouse coords if (selectedAnimatedTile != null) selectedAnimatedTile.WorldPosition = worldPosition; // selected animated tile - calculate position based on mouse coords if (selectedEmitter != null) { selectedEmitter.WorldPosition = new Vector2(worldPosition.X + mapTileSize / 2, worldPosition.Y + mapTileSize / 2); } // create selected emitter particles if we are drawing them if (currentBrush == EditorBrush.Particle_Emitter_Draw) { if (selectedEmitter != null) selectedEmitter.CreateParticles(); } if (currentBrush == EditorBrush.Entity_Draw) // spawn draw { if (selectedEntity != null) { if ((selectedEntity.BoundingRectangle.Intersects(viewportRect)) && (mouseState.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed) && (Mouse.WindowHandle == xnaDisplay.Handle)) { EditMap(mouseState, viewportRect); } } } else if (currentBrush == EditorBrush.Entity_Erase) // spawn erase { if ((mouseState.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed) && (Mouse.WindowHandle == xnaDisplay.Handle)) { EditMap(mouseState, viewportRect); } } else // otherwise { if ((viewportRect.Contains(mouseLoc)) && (mouseState.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed) && (Mouse.WindowHandle == xnaDisplay.Handle)) { EditMap(mouseState, viewportRect); } } // only update the input handler if we are using brushes that utilize it, // and the mouse handle is the xna display if (((currentBrush == EditorBrush.Portal_Draw) || (currentBrush == EditorBrush.Shader_Region_Draw) || (currentBrush == EditorBrush.Event_Region_Draw)) && (Mouse.WindowHandle == xnaDisplay.Handle)) mouseSelectionHandler.Update(mouseState); if (((currentBrush == EditorBrush.Lighting)) && (Mouse.WindowHandle == xnaDisplay.Handle)) lightLayerInputHandler.Update(mouseState); prevMouseState = Mouse.GetState(); float frameTime = (float)gameTime.ElapsedGameTime.TotalSeconds; particleManager.UpdateParticles(frameTime); UpdateEntityRenderer(); map.Update(gameTime); } /// <summary> /// Updates entity render with current map entities and particles /// </summary> private void UpdateEntityRenderer() { EntityRenderer.ClearEntities(); List<DrawableWorldEntity> tempEntityList = new List<DrawableWorldEntity>(); // Map entities if (showSpawnLayerToolStripMenuItem.Checked) { for (int i = 0; i < entityLayer.EntityList.Count; i++) { DrawableWorldEntity entity = entityLayer.EntityList[i] as DrawableWorldEntity; tempEntityList.Add(entity); } } // Particle Emitters if (showParticleEmittersToolStripMenuItem.Checked) { particleLayer.CreateParticles(); for (int i = 0; i < particleManager.ParticleCount; i++) { if (particleManager.Particle[i] != null) { DrawableWorldEntity particle = particleManager.Particle[i] as DrawableWorldEntity; tempEntityList.Add(particle); } } } EntityRenderer.UseZOrdering = zOrderEntitiesToolStripMenuItem.Checked; EntityRenderer.AddEntities(tempEntityList); } /// <summary> /// Perform Edits to Map /// TODO: Create MapEditor class and delegate all of this code into functions as this is a bit messy atm /// </summary> /// <param name="mouseState"></param> private void EditMap(MouseState mouseState, Microsoft.Xna.Framework.Rectangle viewportRect) { Vector2 mouseTileLocation = XnaMouseTileLocation(mouseState); if (map != null) { switch (currentBrush) { case EditorBrush.None: break; // TODO: Find a better way to group these collision brushes, this is messy and // unnecessary case EditorBrush.Collision_Passable: if ((mouseTileLocation.X < mapTileWidth) && (mouseTileLocation.Y < mapTileHeight)) MapEditor.SetCollision(map, mouseTileLocation, currentBrush); break; case EditorBrush.Collision_Impassable: if ((mouseTileLocation.X < mapTileWidth) && (mouseTileLocation.Y < mapTileHeight)) MapEditor.SetCollision(map, mouseTileLocation, currentBrush); break; case EditorBrush.Collision_Platform: if ((mouseTileLocation.X < mapTileWidth) && (mouseTileLocation.Y < mapTileHeight)) MapEditor.SetCollision(map, mouseTileLocation, currentBrush); break; case EditorBrush.Collision_SlopeDown: if ((mouseTileLocation.X < mapTileWidth) && (mouseTileLocation.Y < mapTileHeight)) MapEditor.SetCollision(map, mouseTileLocation, currentBrush); break; case EditorBrush.Collision_SlopeUp: if ((mouseTileLocation.X < mapTileWidth) && (mouseTileLocation.Y < mapTileHeight)) MapEditor.SetCollision(map, mouseTileLocation, currentBrush); break; case EditorBrush.Collision_Abnormal: if ((mouseTileLocation.X < mapTileWidth) && (mouseTileLocation.Y < mapTileHeight)) MapEditor.SetCollision(map, mouseTileLocation, currentBrush); break; case EditorBrush.Collision_NPC: if ((mouseTileLocation.X < mapTileWidth) && (mouseTileLocation.Y < mapTileHeight)) MapEditor.SetCollision(map, mouseTileLocation, currentBrush); break; case EditorBrush.Tile_Draw: if ((mouseTileLocation.X < mapTileWidth) && (mouseTileLocation.Y < mapTileHeight)) MapEditor.TileDraw(map, mouseTileLocation, currentLayerBox.SelectedIndex, selectedTile, selectedMultipleTiles, selectedStartTile, selectedTileRange); break; case EditorBrush.Animated_Tile_Draw: if ((mouseTileLocation.X < mapTileWidth) && (mouseTileLocation.Y < mapTileHeight)) MapEditor.AnimatedTileDraw(map, currentLayer, selectedAnimatedTile, mouseTileLocation); break; case EditorBrush.Animated_Tile_Layer_Fill: if ((mouseTileLocation.X < mapTileWidth) && (mouseTileLocation.Y < mapTileHeight)) MapEditor.AnimatedTileLayerFill(map, currentLayer, selectedAnimatedTile, mapTileWidth, mapTileHeight); break; case EditorBrush.Tile_Layer_Fill: if ((mouseTileLocation.X < mapTileWidth) && (mouseTileLocation.Y < mapTileHeight)) MapEditor.TileLayerFill(map, currentLayerBox.SelectedIndex, selectedTile); break; case EditorBrush.Tile_Erase: if ((mouseTileLocation.X < mapTileWidth) && (mouseTileLocation.Y < mapTileHeight)) MapEditor.TileErase(map, mouseTileLocation, currentLayerBox.SelectedIndex); break; case EditorBrush.Entity_Draw: if (selectedEntity.BoundingRectangle.Intersects(viewportRect)) { MapEditor.EntityDraw(map, entityLayer, selectedEntity, mouseTileLocation); } break; case EditorBrush.Entity_Erase: if (prevMouseState.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Released) { MapEditor.EntityErase(entityLayer, selectionReticle); } break; case EditorBrush.Mass_Entity_Erase: MapEditor.EntityErase(entityLayer, selectionReticle); break; case EditorBrush.Particle_Emitter_Draw: if ((mouseTileLocation.X < mapTileWidth) && (mouseTileLocation.Y < mapTileHeight)) MapEditor.ParticleEmitterDraw(map, particleLayer, selectedEmitter, mouseTileLocation); break; case EditorBrush.Particle_Emitter_Erase: if ((mouseTileLocation.X < mapTileWidth) && (mouseTileLocation.Y < mapTileHeight)) MapEditor.ParticleEmitterErase(map, particleLayer, mouseTileLocation); break; case EditorBrush.Portal_Draw: if (MapSelections.PortalSelection > -1) { if ((mouseTileLocation.X < mapTileWidth) && (mouseTileLocation.Y < mapTileHeight)) MapEditor.PortalDraw(map, mouseSelectionHandler, mapPortalInfoList, MapSelections.PortalSelection); } else { MessageBox.Show("Please select a portal from the portals dropdown within the selection window before attempting to draw.", "No portal selected", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } break; case EditorBrush.Event_Region_Draw: if (MapSelections.EventSelection > -1) { if ((mouseTileLocation.X < mapTileWidth) && (mouseTileLocation.Y < mapTileHeight)) MapEditor.EventRegionDraw(map, mouseSelectionHandler, mapEventInfoList, MapSelections.EventSelection); } else { MessageBox.Show("Please select an event from the shader dropdown within the selection window before attempting to draw.", "No event selected", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } break; case EditorBrush.Shader_Region_Draw: if (MapSelections.ShaderSelection > -1) { if ((mouseTileLocation.X < mapTileWidth) && (mouseTileLocation.Y < mapTileHeight)) MapEditor.ShaderRegionDraw(map, mouseSelectionHandler, mapShaderRegionInfoList, MapSelections.ShaderSelection); } else { MessageBox.Show("Please select a shader from the shader dropdown within the selection window before attempting to draw.", "No shader selected", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } break; case EditorBrush.Terrain_Draw: if ((mouseTileLocation.X < mapTileWidth) && (mouseTileLocation.Y < mapTileHeight)) if (MapSelections.TerrainSelection > -1) MapEditor.TerrainSet(map, mouseTileLocation, MapSelections.TerrainSelection); break; case EditorBrush.Terrain_Erase: if ((mouseTileLocation.X < mapTileWidth) && (mouseTileLocation.Y < mapTileHeight)) MapEditor.TerrainNone(map, mouseTileLocation); break; case EditorBrush.Hazard_Damage: if ((mouseTileLocation.X < mapTileWidth) && (mouseTileLocation.Y < mapTileHeight)) MapEditor.HazardDamage(map, mouseTileLocation); break; case EditorBrush.Hazard_None: if ((mouseTileLocation.X < mapTileWidth) && (mouseTileLocation.Y < mapTileHeight)) MapEditor.HazardNone(map, mouseTileLocation); break; default: break; } } } /// <summary> /// Render XNA graphic content to tile display /// </summary> private void Render(GameTime gameTime) { // declare transform matrix Matrix transformMatrix; // create a map display translation matrix and multiply by a scale matrix transformMatrix = CreateDisplayTranslation(); transformMatrix = Matrix.Multiply(transformMatrix, Matrix.CreateScale(mapScale)); GraphicsDevice.SetRenderTarget(0, null); GraphicsDevice.Clear(Microsoft.Xna.Framework.Graphics.Color.Black); Texture2D colorMap = GetColorMap(gameTime, transformMatrix); //if (map.TilesetTextures.ColorMap != null) // colorMap.Save("color.png", ImageFileFormat.Png); // if we are using the deferred renderer, use it if (useDeferredRendererToolStripMenuItem.Checked) { Texture2D normalMap = GetNormalMap(gameTime, transformMatrix); Texture2D depthMap = GetDepthMap(gameTime, transformMatrix); //if (map.TilesetTextures.NormalMap != null) // normalMap.Save("normal.png", ImageFileFormat.Png); //if (map.TilesetTextures.DepthMap != null) // depthMap.Save("depth.png", ImageFileFormat.Png); // TODO: // Move this coordinate transformation block into the DeferredRenderer class List<PointLight> lights = new List<PointLight>(); foreach (MapLight light in lightLayer.LightList) { // create a copy of the original light PointLight l = new PointLight(light.Light); // modify values Vector3 lPos = l.Position; lPos.X += TranslationVector().X; lPos.Y += TranslationVector().Y; lPos.X *= mapScale; lPos.Y *= mapScale; lPos.Z *= mapScale; l.Position = lPos; l.Brightness *= mapScale; l.Flicker *= mapScale; lights.Add(l); } Texture2D litScene = deferredRenderer.Draw(spriteBatch, colorMap, normalMap, depthMap, lights, map.LightParams, transformMatrix); spriteBatch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate, SaveStateMode.None); spriteBatch.Draw(litScene, Vector2.Zero, Microsoft.Xna.Framework.Graphics.Color.White); spriteBatch.End(); } else { spriteBatch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate, SaveStateMode.None); spriteBatch.Draw(colorMap, Vector2.Zero, Microsoft.Xna.Framework.Graphics.Color.White); spriteBatch.End(); } spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None, transformMatrix); { if (showCollisionLayerToolStripMenuItem.Checked) collisionLayer.Draw(spriteBatch); if (showHazardLayerToolStripMenuItem.Checked) hazardLayer.Draw(spriteBatch); if (showTerrainLayerToolStripMenuItem.Checked) terrainLayer.Draw(spriteBatch); if (showTerrainLabelsToolStripMenuItem.Checked) terrainLayer.DrawLabels(gameTime, spriteBatch, spriteFont); if (showEntityLabelsToolStripMenuItem.Checked) entityLayer.DrawLabels(gameTime, spriteBatch, spriteFont); if (showParticleLabelsToolStripMenuItem.Checked) particleLayer.DrawLabels(gameTime, spriteBatch, spriteFont); if (showPortalLayerToolStripMenuItem.Checked) portalLayer.Draw(mapPortalInfoList, spriteBatch, spriteFont); if (showEventRegionsToolStripMenuItem.Checked) eventLayer.Draw(mapEventInfoList, spriteBatch, spriteFont); if (showShaderLayerToolStripMenuItem.Checked) shaderEffectLayer.Draw(mapShaderRegionInfoList, spriteBatch, spriteFont); if (showTileCoordinatesToolStripMenuItem.Checked) tileCoordLayer.DrawLabels(spriteBatch, spriteFont); if (showTileGridToolStripMenuItem.Checked) tileCoordLayer.DrawGrid(spriteBatch); if (showLightLayerToolStripMenuItem.Checked) lightLayer.Draw(spriteBatch); if (showLightLabelsToolStripMenuItem.Checked) lightLayer.DrawLabels(spriteBatch, spriteFont); if (Mouse.WindowHandle == xnaDisplay.Handle) { // get tile location from mouse Vector2 mouseTileLocation = XnaMouseTileLocation(Mouse.GetState()); // if we are drawing multiple tiles, hover the selected tiles with alpha if ((currentBrush == EditorBrush.Tile_Draw) && (selectedMultipleTiles == true)) { spriteBatch.Draw( map.TilesetTextures.ColorMap, new Vector2(mouseTileLocation.X * mapTileSize, mouseTileLocation.Y * mapTileSize), selectedTile.SourceRect, new Microsoft.Xna.Framework.Graphics.Color(255, 255, 255, 100) ); } // if we are working with spawns else if ((selectedEntity != null) && (currentBrush == EditorBrush.Entity_Draw)) { // draw entity with transparency selectedEntity.Draw(gameTime, spriteBatch, true); } // if we are working with animated tiles else if ((selectedAnimatedTile != null) && ((currentBrush == EditorBrush.Animated_Tile_Draw) || (currentBrush == EditorBrush.Animated_Tile_Layer_Fill))) { // draw entity with transparency selectedAnimatedTile.Draw(gameTime, spriteBatch, true); } else { // if we are drawing portals, effect regions, or event regions use the input handler if ((currentBrush == EditorBrush.Portal_Draw) || (currentBrush == EditorBrush.Shader_Region_Draw) || (currentBrush == EditorBrush.Event_Region_Draw)) { HighlightSelectedTiles(spriteBatch); mouseSelectionHandler.Draw(spriteBatch); } else if (currentBrush == EditorBrush.Lighting) { // lighting drawing here } else { // if we are drawing anything else draw a reticle around the tile we are hovering over selectionReticle.Draw(spriteBatch); } } } } spriteBatch.End(); } /// <summary> /// Render the entire scene and return the color map /// </summary> private Texture2D GetColorMap(GameTime gameTime, Matrix transformMatrix) { GraphicsDevice.SetRenderTarget(0, colorTarget); // attempt to clear the buffer. this may fail due to not // having a correctly formatted Depth Stencil buffer. If // that happens, we need to create a new one try { GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, map.BgColor, 1, 0); } catch (InvalidOperationException) { // Set our custom depth buffer GraphicsDevice.DepthStencilBuffer = RenderHelper.CreateDepthStencil(colorTarget); // try to clear again GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, map.BgColor, 1, 0); } if (showLayerShadingToolStripMenuItem.Checked) // draw with layer shading { // draw normal map layer spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None, transformMatrix); { map.Draw(gameTime, spriteBatch, MapDraw.Normal, MapTexture.Color, false, currentLayer); } // draw particle markers if (showParticleMarkersToolStripMenuItem.Checked) particleLayer.DrawMarkers(spriteBatch); spriteBatch.End(); // draw entities EntityRenderer.Draw(gameTime, spriteBatch, particleTexture, transformMatrix, true); // draw foreground if (showForegroundHighlightToolStripMenuItem.Checked) { spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None, transformMatrix); { map.Draw(gameTime, spriteBatch, MapDraw.Foreground, MapTexture.Color, true, currentLayer); } spriteBatch.End(); } else { spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None, transformMatrix); { map.Draw(gameTime, spriteBatch, MapDraw.Foreground, MapTexture.Color, false, currentLayer); } spriteBatch.End(); } } else // draw without shading { // draw normal map layer spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None, transformMatrix); { map.Draw(gameTime, spriteBatch, MapDraw.Normal, MapTexture.Color, false, -1); } // draw particle markers if (showParticleMarkersToolStripMenuItem.Checked) particleLayer.DrawMarkers(spriteBatch); spriteBatch.End(); // draw entities EntityRenderer.Draw(gameTime, spriteBatch, particleTexture, transformMatrix, true); // draw foreground if (showForegroundHighlightToolStripMenuItem.Checked) { spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None, transformMatrix); { map.Draw(gameTime, spriteBatch, MapDraw.Foreground, MapTexture.Color, true, -1); } spriteBatch.End(); } else { spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None, transformMatrix); { map.Draw(gameTime, spriteBatch, MapDraw.Foreground, MapTexture.Color, false, -1); } spriteBatch.End(); } } GraphicsDevice.SetRenderTarget(0, null); return colorTarget.GetTexture(); } /// <summary> /// Render the entire scene and return the color map /// </summary> private Texture2D GetNormalMap(GameTime gameTime, Matrix transformMatrix) { GraphicsDevice.SetRenderTarget(0, normalTarget); GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Microsoft.Xna.Framework.Graphics.Color.TransparentWhite, 1, 0); if (showLayerShadingToolStripMenuItem.Checked) // draw with layer shading { // draw normal map layer spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None, transformMatrix); { map.Draw(gameTime, spriteBatch, MapDraw.Normal, MapTexture.Normals, false, currentLayer); } // draw particle markers if (showParticleMarkersToolStripMenuItem.Checked) particleLayer.DrawMarkers(spriteBatch); spriteBatch.End(); // draw entities EntityRenderer.DrawNormals(gameTime, spriteBatch, particleTexture, transformMatrix, true); // draw foreground if (showForegroundHighlightToolStripMenuItem.Checked) { spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None, transformMatrix); { map.Draw(gameTime, spriteBatch, MapDraw.Foreground, MapTexture.Normals, true, currentLayer); } spriteBatch.End(); } else { spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None, transformMatrix); { map.Draw(gameTime, spriteBatch, MapDraw.Foreground, MapTexture.Normals, false, currentLayer); } spriteBatch.End(); } } else // draw without shading { // draw normal map layer spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None, transformMatrix); { map.Draw(gameTime, spriteBatch, MapDraw.Normal, MapTexture.Normals, false, -1); } // draw particle markers if (showParticleMarkersToolStripMenuItem.Checked) particleLayer.DrawMarkers(spriteBatch); spriteBatch.End(); // draw entities EntityRenderer.DrawNormals(gameTime, spriteBatch, particleTexture, transformMatrix, true); // draw foreground if (showForegroundHighlightToolStripMenuItem.Checked) { spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None, transformMatrix); { map.Draw(gameTime, spriteBatch, MapDraw.Foreground, MapTexture.Normals, true, -1); } spriteBatch.End(); } else { spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None, transformMatrix); { map.Draw(gameTime, spriteBatch, MapDraw.Foreground, MapTexture.Normals, false, -1); } spriteBatch.End(); } } GraphicsDevice.SetRenderTarget(0, null); return normalTarget.GetTexture(); } /// <summary> /// Render the entire scene and return the color map /// </summary> private Texture2D GetDepthMap(GameTime gameTime, Matrix transformMatrix) { GraphicsDevice.SetRenderTarget(0, depthTarget); GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Microsoft.Xna.Framework.Graphics.Color.Black, 1, 0); if (showLayerShadingToolStripMenuItem.Checked) // draw with layer shading { // draw normal map layer spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None, transformMatrix); { map.Draw(gameTime, spriteBatch, MapDraw.Normal, MapTexture.Depth, false, currentLayer); } // draw particle markers if (showParticleMarkersToolStripMenuItem.Checked) particleLayer.DrawMarkers(spriteBatch); spriteBatch.End(); // draw entities //EntityRenderer.DrawNormals(gameTime, spriteBatch, particleTexture, transformMatrix, true); // draw foreground if (showForegroundHighlightToolStripMenuItem.Checked) { spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None, transformMatrix); { map.Draw(gameTime, spriteBatch, MapDraw.Foreground, MapTexture.Depth, true, currentLayer); } spriteBatch.End(); } else { spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None, transformMatrix); { map.Draw(gameTime, spriteBatch, MapDraw.Foreground, MapTexture.Depth, false, currentLayer); } spriteBatch.End(); } } else // draw without shading { // draw normal map layer spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None, transformMatrix); { map.Draw(gameTime, spriteBatch, MapDraw.Normal, MapTexture.Depth, false, -1); } // draw particle markers if (showParticleMarkersToolStripMenuItem.Checked) particleLayer.DrawMarkers(spriteBatch); spriteBatch.End(); // draw entities //EntityRenderer.DrawNormals(gameTime, spriteBatch, particleTexture, transformMatrix, true); // draw foreground if (showForegroundHighlightToolStripMenuItem.Checked) { spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None, transformMatrix); { map.Draw(gameTime, spriteBatch, MapDraw.Foreground, MapTexture.Depth, true, -1); } spriteBatch.End(); } else { spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None, transformMatrix); { map.Draw(gameTime, spriteBatch, MapDraw.Foreground, MapTexture.Depth, false, -1); } spriteBatch.End(); } } GraphicsDevice.SetRenderTarget(0, null); return depthTarget.GetTexture(); } /// <summary> /// Highlight tiles selected by the input handler /// </summary> /// <param name="spriteBatch">SpriteBatch</param> private void HighlightSelectedTiles(SpriteBatch spriteBatch) { // create transparent orange texture for selected tiles Texture2D selectedTex = RuntimeTexture.CreateRectangle( GraphicsDevice, mapTileSize, mapTileSize, new Microsoft.Xna.Framework.Graphics.Color(0,169,235,100)); for (int y = 0; y < mapTileHeight; y++) { for (int x = 0; x < mapTileWidth; x++) { Tile tempTile = map.GetTile(x, y); if (mouseSelectionHandler.MouseRec.Contains(tempTile.MapRectangle.Center)) { spriteBatch.Draw(selectedTex, tempTile.Location, Microsoft.Xna.Framework.Graphics.Color.White); } } } } #endregion
#region Loading, Saving, Resizing, and Generating Methods /// <summary> /// Load XML Map File /// </summary> /// <param name="fileName">Full path to XML map</param> private void LoadMap(string fileName) { // get the initial time to measure load time DateTime startTime = DateTime.Now; Cursor = Cursors.WaitCursor; string buildError, tempFileName; // correct asset type in xml and dump to a temp file for loading //XmlAssetCorrector.CorrectInputAssetType(fileName, defaultMapAssetType ,out tempFileName); // load the temp file map = contentManager.LoadContent<Map>(fileName, null, null, out buildError, true, false); // delete the temp file //File.Delete(tempFileName); if (map.ID != null) mapID = map.ID; else mapID = ""; mapAssetType = map.AssetType; mapBgColor = map.BgColor; tileSet = map.TileSet; mapForegroundLayers.Clear(); mapForegroundLayers.AddRange(map.ForegroundLayers); mapTileSize = map.Tile_Size; mapTileWidth = map.Width; mapTileHeight = map.Height; mapPixelWidth = map.WidthInPixels; mapPixelHeight = map.HeightInPixels; lightLayer.AddList(map.LightList); mapPortalInfoList.Clear(); mapPortalInfoList.AddRange(map.PortalList); mapShaderRegionInfoList.Clear(); mapShaderRegionInfoList.AddRange(map.ShaderRegionList); mapTerrainInfoList.Clear(); mapTerrainInfoList.AddRange(map.TerrainList); mapEventInfoList.Clear(); mapEventInfoList.AddRange(map.EventList); layerCount = map.Layers.Count; // clear spawn layer entityLayer.Clear(); // clear particle layer particleLayer.Clear(); // process entities MapEntityDesc entityInfo = new MapEntityDesc(); string entityResource, prevEntityResource; prevEntityResource = ""; for (int i = 0; i < map.SpawnList.Count; i++) { MapEntity entity = new MapEntity(); // get entity directory and filename entityResource = FileHelper.GetAssemblyDirectory() + "/Entities/" + map.SpawnList[i].EntityResource + ".xml"; // if we are using the same resource, no need to load a new one. otherwise, load the new one. if (entityResource != prevEntityResource) { if (File.Exists(entityResource)) { // load entity info from resource entityInfo = contentManager.LoadContent<MapEntityDesc> (entityResource, null, null, out buildError, false, true); // create new entity and initialize entity = new MapEntity(entityInfo); entity.ResourcePath = map.SpawnList[i].EntityResource; entity.Initialize(this, mapTileSize); } } else // use a copy of the last entity if we can { if ((entityLayer.EntityList[i - 1] != null) && (entityLayer.EntityList[i - 1].Animation != null)) entity = entityLayer.EntityList[i - 1]; } if ((entity != null) && (entity.Animation != null)) { // add entity to map entityLayer.Add(entity, map.SpawnList[i].SpawnPoint, map.SpawnList[i].SpawnTile, map.SpawnList[i].ID); // save the last entity loaded prevEntityResource = entityResource; } } entityLayer.Sort(); // process particles MapParticleDesc particleDesc = new MapParticleDesc(); string particleResource, prevParticleResource; prevParticleResource = ""; for (int i = 0; i < map.ParticleEmitterList.Count; i++) { ParticleEmitter particleEmitter = new ParticleEmitter(); // get entity directory and filename particleResource = FileHelper.GetAssemblyDirectory() + "/Particles/" + map.ParticleEmitterList[i].EmitterResource + ".xml"; // if we are using the same resource, no need to load a new one. otherwise, load the new one. if (particleResource != prevParticleResource) { if (File.Exists(particleResource)) { // load entity info from resource particleDesc = contentManager.LoadContent<MapParticleDesc> (particleResource, null, null, out buildError, false, true); // create new entity and initialize particleEmitter = new ParticleEmitter(particleManager, map.ParticleEmitterList[i].SpawnPoint, particleDesc); particleEmitter.ResourcePath = map.ParticleEmitterList[i].EmitterResource; } } else // use a copy of the last entity if we can { if (particleLayer.EmitterList[i - 1] != null) particleEmitter = particleLayer.EmitterList[i - 1].Clone(); } if (particleEmitter != null) { // add entity to map particleLayer.Add(particleEmitter, map.ParticleEmitterList[i].SpawnPoint, map.ParticleEmitterList[i].SpawnTile); // save the last entity loaded prevParticleResource = particleResource; } } MapSelections.UpdateAll(); UpdateLayers(); Cursor = Cursors.Arrow; // get the stop time and duration DateTime stopTime = DateTime.Now; TimeSpan duration = stopTime - startTime; // display load time MessageBox.Show("Loading took " + duration, "Map Load Complete", MessageBoxButtons.OK, MessageBoxIcon.Information); // update reticle selectionReticle.SetSize(mapTileSize); } /// <summary> /// Loads all animated tiles for the map post initialization /// </summary> private void LoadAnimatedTiles() { List<AnimatedTile> animatedTiles = new List<AnimatedTile>(); AnimatedTileDesc tileDesc; Texture2D animationTex; string animationTexPath, buildError; string tileResource = ""; string prevTileResource = ""; for (int i = 0; i < map.AnimatedTileList.Count; i++) { AnimatedTile animatedTile = new AnimatedTile(); animatedTile.ID = -1; // get tile directory and filename tileResource = map.AnimatedTileList[i].TileResource; // if we are using the same resource, no need to load a new one. otherwise, load the new one. if (tileResource != prevTileResource) { // load tile info from resource string fullResourcePath = FileHelper.GetAssemblyDirectory() + "/Animated Tiles/" + tileResource + ".xml"; if (File.Exists(fullResourcePath)) { tileDesc = contentManager.LoadContent<AnimatedTileDesc> (fullResourcePath, null, null, out buildError, false, true); // load texture from desc animationTexPath = FileHelper.GetAssemblyDirectory() + tileDesc.Texture; if (File.Exists(animationTexPath)) { animationTex = contentManager.LoadContent<Texture2D> (animationTexPath, null, null, out buildError, false, true); // create new tile and initialize animatedTile = new AnimatedTile(); animatedTile.ResourcePath = tileResource; animatedTile.WorldPosition = map.AnimatedTileList[i].SpawnPoint; animatedTile.TilePosition = map.AnimatedTileList[i].SpawnTile; animatedTile.ID = map.AnimatedTileList[i].ID; animatedTile.Layer = map.AnimatedTileList[i].Layer; animatedTile.Initialize(animationTex, tileDesc); } } } else // use a copy of the last tile if we can { if (animatedTiles[i - 1] != null) { animatedTile = animatedTiles[i - 1].Clone(); animatedTile.ResourcePath = tileResource; animatedTile.WorldPosition = map.AnimatedTileList[i].SpawnPoint; animatedTile.TilePosition = map.AnimatedTileList[i].SpawnTile; animatedTile.ID = map.AnimatedTileList[i].ID; animatedTile.Layer = map.AnimatedTileList[i].Layer; } } if (animatedTile.ID != -1) { // add tile to temporary list animatedTiles.Add(animatedTile); // add to map map.AddAnimatedTile(animatedTile); // save the last tile loaded prevTileResource = tileResource; } } } /// <summary> /// Load tile texture /// </summary> /// <param name="fileName">Full path to texture file</param> private void LoadTexture(string fileName) { Cursor = Cursors.WaitCursor; string buildError; string path = System.IO.Path.GetDirectoryName(fileName); string file = System.IO.Path.GetFileNameWithoutExtension(fileName); string fileExt = System.IO.Path.GetExtension(fileName); string normalsFile = path + "//" + file + "_Normal" + fileExt; string depthFile = path + "//" + file + "_Depth" + fileExt; tileSet = "Tiles/" + file; tilesImage = Image.FromFile(fileName); tileSelectionBox.Image = tilesImage; mapTiles = contentManager.LoadContent<Texture2D>(fileName, null, null, out buildError, true, true); map.SetColorTexture(mapTiles); map.TilesetWidth = map.TilesetTextures.Width; map.TilesetHeight = map.TilesetTextures.Height; buildError = null; Texture2D mapNormals; mapNormals = contentManager.LoadContent<Texture2D>(normalsFile, null, null, out buildError, true, true); // this build error would indicate an issue loading normals // we will circumvent this by creating a dummy texture if (buildError != null) { mapNormals = RuntimeTexture.CreateRectangle(GraphicsDevice, map.TilesetTextures.Width, map.TilesetTextures.Height, new Microsoft.Xna.Framework.Graphics.Color(0, 0, 0, 0)); } map.SetNormalTexture(mapNormals); Texture2D mapDepth; mapDepth = contentManager.LoadContent<Texture2D> (depthFile, null, null, out buildError, true, true); // this build error would indicate an issue loading normals // we will circumvent this by creating a dummy texture if (buildError != null) { mapDepth = RuntimeTexture.CreateRectangle(GraphicsDevice, map.TilesetTextures.Width, map.TilesetTextures.Height, new Microsoft.Xna.Framework.Graphics.Color(0, 0, 0, 0)); } map.SetDepthTexture(mapDepth); map.Initialize(); tilesetXCount = (int)(tilesImage.Width / mapTileSize); tilesetYCount = (int)(tilesImage.Height / mapTileSize); Cursor = Cursors.Arrow; loadedTexture = true; } /// <summary> /// Exports new tile map to an XML file /// </summary> /// <param name="fileName">File Name</param> private void ExportMap(string fileName) { // declare temp rows List<string> layerRows = new List<string>(); // create new map Map newMap = new Map(); // create spawn list List<MapEntityInfo> spawnList = new List<MapEntityInfo>(); // create animated tile list List<AnimatedTileInfo> animatedTileList = new List<AnimatedTileInfo>(); // create particle emitter list List<ParticleEmitterInfo> particleEmitterList = new List<ParticleEmitterInfo>(); // process spawn list for (int i = 0; i < entityLayer.EntityList.Count; i++) { // create and fill out spawn info MapEntityInfo spawnInfo = new MapEntityInfo(); spawnInfo.ID = entityLayer.EntityList[i].ID; spawnInfo.EntityResource = entityLayer.EntityList[i].ResourcePath; spawnInfo.SpawnPoint = entityLayer.EntityList[i].WorldPosition; spawnInfo.SpawnTile = entityLayer.EntityList[i].TilePosition; // add to list spawnList.Add(spawnInfo); } // sort spawn list by resource spawnList.Sort(delegate(MapEntityInfo spawn1, MapEntityInfo spawn2) { return spawn1.EntityResource.CompareTo(spawn2.EntityResource); }); // process particle emitters for (int i = 0; i < particleLayer.EmitterList.Count; i++) { // create and fill out spawn info ParticleEmitterInfo particleInfo = new ParticleEmitterInfo(); particleInfo.EmitterResource = particleLayer.EmitterList[i].ResourcePath; particleInfo.SpawnPoint = particleLayer.EmitterList[i].WorldPosition; particleInfo.SpawnTile = particleLayer.EmitterList[i].TilePosition; particleInfo.IsForeground = false; // add to list particleEmitterList.Add(particleInfo); } // sort spawn list by resource particleEmitterList.Sort(delegate(ParticleEmitterInfo emitter1, ParticleEmitterInfo emitter2) { return emitter1.EmitterResource.CompareTo(emitter2.EmitterResource); }); // save data to map newMap.AssetType = mapAssetType; newMap.ID = mapID; newMap.BgColor = mapBgColor; newMap.TileSet = tileSet; newMap.TilesetXCount = tilesetXCount; newMap.TilesetYCount = tilesetYCount; newMap.Tile_Size = mapTileSize; newMap.Width = mapTileWidth; newMap.Height = mapTileHeight; newMap.TilesetWidth = tilesImage.Width; newMap.TilesetHeight = tilesImage.Height; // process lists newMap.SpawnList = new List<MapEntityInfo>(); newMap.SpawnList.AddRange(spawnList); newMap.ParticleEmitterList = new List<ParticleEmitterInfo>(); newMap.ParticleEmitterList.AddRange(particleEmitterList); newMap.PortalList = new List<PortalInfo>(); newMap.PortalList.AddRange(mapPortalInfoList); newMap.ShaderRegionList = new List<ShaderRegionInfo>(); newMap.ShaderRegionList.AddRange(mapShaderRegionInfoList); newMap.TerrainList = new List<TerrainInfo>(); newMap.TerrainList.AddRange(mapTerrainInfoList); newMap.EventList = new List<MapEventInfo>(); newMap.EventList.AddRange(mapEventInfoList); newMap.ForegroundLayers.Clear(); newMap.ForegroundLayers.AddRange(mapForegroundLayers); // save light data newMap.LightParams = new LightParams(map.LightParams); newMap.LightList = new List<PointLight>(); foreach (MapLight mapLight in lightLayer.LightList) newMap.LightList.Add(mapLight.Light); AnimatedTileInfo tempAnimatedTileInfo = new AnimatedTileInfo(); // generate layer lists and get all animated tiles for (int i = 0; i < layerCount; i++) { // add new layer newMap.Layers.Add(new List<string>()); for (int y = 0; y < mapTileHeight; y++) { // add new row in the layer newMap.Layers[i].Add(null); for (int x = 0; x < mapTileWidth; x++) { Tile currentTile = map.GetTile(x, y); newMap.Layers[i][y] += currentTile.Layers[i] + ","; // create info objects for each animated tile and add to list for (int j = 0; j < currentTile.AnimatedTiles.Count; j++) { tempAnimatedTileInfo = new AnimatedTileInfo(); tempAnimatedTileInfo.ID = currentTile.ID; tempAnimatedTileInfo.Layer = currentTile.AnimatedTiles[j].Layer; tempAnimatedTileInfo.SpawnPoint = currentTile.AnimatedTiles[j].WorldPosition; tempAnimatedTileInfo.SpawnTile = currentTile.AnimatedTiles[j].TilePosition; tempAnimatedTileInfo.TileResource = currentTile.AnimatedTiles[j].ResourcePath; animatedTileList.Add(tempAnimatedTileInfo); } } // remove trailing comma newMap.Layers[i][y] = newMap.Layers[i][y].TrimEnd(new Char[] { ',' }); } } // sort animated tiles by resource animatedTileList.Sort(delegate(AnimatedTileInfo tile1, AnimatedTileInfo tile2) { return tile1.TileResource.CompareTo(tile2.TileResource); }); // save animated tiles to map newMap.AnimatedTileList = animatedTileList; string hazardLayerRow; string collisionLayerRow; string terrainLayerRow; // iterate through tiles once more to get collision and hazard data for (int y = 0; y < map.Height; y++) { // clear all temp layers collisionLayerRow = null; hazardLayerRow = null; terrainLayerRow = null; for (int x = 0; x < map.Width; x++) { Tile currentTile = map.GetTile(x, y); // the third to last layer in the tile layers contains hazard data hazardLayerRow += currentTile.Layers[currentTile.Layers.Count - 3] + ","; // the second to last layer in the tile layers contains collision data collisionLayerRow += currentTile.Layers[currentTile.Layers.Count - 2] + ","; // the last layer in the tile layers contains terrain data terrainLayerRow += currentTile.Layers[currentTile.Layers.Count - 1] + ","; } // trim trailing commas hazardLayerRow = hazardLayerRow.TrimEnd(new Char[] { ',' }); collisionLayerRow = collisionLayerRow.TrimEnd(new Char[] { ',' }); terrainLayerRow = terrainLayerRow.TrimEnd(new Char[] { ',' }); // add the new rows to the map layers newMap.HazardLayer.Add(hazardLayerRow); newMap.CollisionLayer.Add(collisionLayerRow); newMap.TerrainLayer.Add(terrainLayerRow); } /* * Now using intermediate serialzier, leaving this code for educational value * * // Open the file, creating it if necessary * // FileStream stream = File.Open(fileName, FileMode.OpenOrCreate); * // Convert the object to XML data and put it in the stream * // XmlSerializer serializer = new XmlSerializer(typeof(Map)); * // serializer.Serialize(stream, map); * // Close the file * // stream.Close(); * */ XmlWriterSettings xmlSettings = new XmlWriterSettings(); xmlSettings.Indent = true; using (XmlWriter xmlWriter = XmlWriter.Create(fileName, xmlSettings)) { IntermediateSerializer.Serialize(xmlWriter, newMap, null); } // correct asset type //XmlAssetCorrector.CorrectOutputAssetType(fileName, defaultMapAssetType, mapAssetType); // update cached copy //contentManager.UpdateCache<Map>(fileName, newMap); // set textures newMap.SetColorTexture(map.TilesetTextures.ColorMap); newMap.SetNormalTexture(map.TilesetTextures.NormalMap); newMap.SetDepthTexture(map.TilesetTextures.DepthMap); // set current map to new map map = newMap; map.Initialize(); LoadAnimatedTiles(); } /// <summary> /// Generate new map /// </summary> private void GenerateNewMap() { // load default settings for use with new map LoadDefaultSettings(); map = new Map(); // temporary rows used to create empty layers string tempRow; for (int i = 0; i < minLayerCount; i++) { // add a new layer map.Layers.Add(new List<string>()); for (int y = 0; y < mapTileHeight; y++) { // add a new layer row map.Layers[i].Add(null); for (int x = 0; x < mapTileWidth; x++) { map.Layers[i][y] += "-1,"; } // trim trailing comma map.Layers[i][y] = map.Layers[i][y].TrimEnd(new Char[] { ',' }); } } // process hazard layer for (int y = 0; y < mapTileHeight; y++) { tempRow = null; for (int x = 0; x < mapTileWidth; x++) { tempRow += "-1,"; } tempRow = tempRow.TrimEnd(new Char[] { ',' }); // fill all layers with "null" data map.HazardLayer.Add(tempRow); } // process terrain layer for (int y = 0; y < mapTileHeight; y++) { tempRow = null; for (int x = 0; x < mapTileWidth; x++) { tempRow += "-1,"; } tempRow = tempRow.TrimEnd(new Char[] { ',' }); // fill all layers with "null" data map.TerrainLayer.Add(tempRow); } // things are a bit different for the collision map, default is passable so we will write that instead of -1 for (int y = 0; y < mapTileHeight; y++) { tempRow = null; for (int x = 0; x < mapTileWidth; x++) { tempRow += (int)TileCollision.Passable + ","; } tempRow = tempRow.TrimEnd(new Char[] { ',' }); map.CollisionLayer.Add(tempRow); } /* // create new lists for the map map.AnimatedTileList = new List<AnimatedTileInfo>(); map.SpawnList = new List<MapEntityInfo>(); map.ParticleEmitterList = new List<ParticleEmitterInfo>(); map.PortalList = new List<PortalInfo>(); map.ShaderRegionList = new List<ShaderRegionInfo>(); map.TerrainList = new List<TerrainInfo>(); map.EventList = new List<MapEventInfo>(); */ // create new lists for the editor mapPortalInfoList = new List<PortalInfo>(); mapShaderRegionInfoList = new List<ShaderRegionInfo>(); mapTerrainInfoList = new List<TerrainInfo>(); mapEventInfoList = new List<MapEventInfo>(); // set tile data and bg color map.BgColor = mapBgColor; map.Tile_Size = mapTileSize; map.Width = mapTileWidth; map.Height = mapTileHeight; /* map.LightParams = new LightParams(); map.LightList = new List<PointLight>(); */ // clear all light data and reset lightLayer.ClearLights(); layerCount = minLayerCount; // set default foreground data mapForegroundLayers.Clear(); mapForegroundLayers.AddRange(defaultForegroundData); map.ForegroundLayers.Clear(); map.ForegroundLayers.AddRange(defaultForegroundData); // clear spawn layer entityLayer.Clear(); // clear particle layer particleLayer.Clear(); // clear tiles image tileSelectionBox.Image = null; MapSelections.UpdateAll(); UpdateLayers(); } /// <summary> /// Resize the map /// </summary> /// <param name="width">New map width</param> /// <param name="height">New map height</param> public void ResizeMap(int width, int height) { // create a new map object Map newMap = new Map(); // temporary rows used to create empty layers string tempRow; string tempColRow; Tile currentTile; int oldWidth = mapTileWidth; int oldHeight = mapTileHeight; #region Tile Layers // process tile layers for (int i = 0; i < layerCount; i++) { // add new layer newMap.Layers.Add(new List<string>()); for (int y = 0; y < height; y++) { // add null row newMap.Layers[i].Add(null); tempRow = null; // modify rows if they already existed in old map if (y < oldHeight) { for (int x = 0; x < width; x++) { // if the tile exists already, copy its data to the layers if (x < oldWidth) { currentTile = map.GetTile(x, y); newMap.Layers[i][y] += currentTile.Layers[i] + ","; } else // if the tile doesnt exist, add null data { newMap.Layers[i][y] += "-1,"; } } // trim trailing commas newMap.Layers[i][y] = newMap.Layers[i][y].TrimEnd(new Char[] { ',' }); } // if these are new rows use "null" data if ((height > oldHeight) && (y >= oldHeight)) { // create temp data for new rows for (int x = 0; x < width; x++) { tempRow += "-1,"; } // trim trailing commas tempRow = tempRow.TrimEnd(new Char[] { ',' }); // replace null rows with data newMap.Layers[i][y] = tempRow; } } } #endregion #region Collision, Hazard, and Terrain Layers for (int y = 0; y < height; y++) { tempRow = null; tempColRow = null; // add rows newMap.HazardLayer.Add(tempRow); newMap.CollisionLayer.Add(tempColRow); newMap.TerrainLayer.Add(tempRow); // modify rows if they already existed in old map if (y < oldHeight) { for (int x = 0; x < width; x++) { // if the tile exists already, copy its data to the layers if (x < oldWidth) { currentTile = map.GetTile(x, y); newMap.HazardLayer[y] += currentTile.Layers[currentTile.Layers.Count - 3] + ","; newMap.CollisionLayer[y] += currentTile.Layers[currentTile.Layers.Count - 2] + ","; newMap.TerrainLayer[y] += currentTile.Layers[currentTile.Layers.Count - 1] + ","; } else // if the tile doesn't exist, add null data { newMap.HazardLayer[y] += "-1,"; newMap.CollisionLayer[y] += (int)TileCollision.Passable + ","; newMap.TerrainLayer[y] += "-1,"; } } // trim trailing commas newMap.HazardLayer[y] = newMap.HazardLayer[y].TrimEnd(new Char[] { ',' }); newMap.CollisionLayer[y] = newMap.CollisionLayer[y].TrimEnd(new Char[] { ',' }); newMap.TerrainLayer[y] = newMap.TerrainLayer[y].TrimEnd(new Char[] { ',' }); } // if these are new rows use "null" data if ((height > oldHeight) && (y >= oldHeight)) { // create temp data for new rows for (int x = 0; x < width; x++) { tempRow += "-1,"; tempColRow += (int)TileCollision.Passable + ","; } // trim trailing commas tempRow = tempRow.TrimEnd(new Char[] { ',' }); tempColRow = tempColRow.TrimEnd(new Char[] { ',' }); // replace null rows with data newMap.HazardLayer[y] = tempRow; newMap.CollisionLayer[y] = tempColRow; newMap.TerrainLayer[y] = tempRow; } } #endregion // copy remaining map attributes newMap.BgColor = map.BgColor; newMap.ForegroundLayers.Clear(); newMap.ForegroundLayers.AddRange(map.ForegroundLayers); newMap.LightParams = new LightParams(map.LightParams); newMap.TerrainList = new List<TerrainInfo>(); newMap.TerrainList.AddRange(map.TerrainList); newMap.Tile_Size = map.Tile_Size; newMap.TilesetHeight = map.TilesetHeight; newMap.TilesetWidth = map.TilesetWidth; newMap.SetColorTexture(map.TilesetTextures.ColorMap); newMap.SetNormalTexture(map.TilesetTextures.NormalMap); newMap.SetDepthTexture(map.TilesetTextures.DepthMap); // set new width and height attributes newMap.Width = width; newMap.Height = height; // make sure editor matches mapTileWidth = newMap.Width; mapTileHeight = newMap.Height; mapTileSize = newMap.Tile_Size; #region Entities // remove any entities that are no longer within map bounds // get viewport rect Microsoft.Xna.Framework.Rectangle viewportRect; viewportRect.X = 0; viewportRect.Y = 0; viewportRect.Width = mapTileSize * mapTileWidth; viewportRect.Height = mapTileSize * mapTileHeight; // create a new list of entities List<MapEntity> entitiesInBounds = new List<MapEntity>(); for (int i = 0; i < entityLayer.EntityList.Count; i++) { // if the entity intersects our viewport, add it to our new list of entities if (entityLayer.EntityList[i].BoundingRectangle.Intersects(viewportRect)) entitiesInBounds.Add(entityLayer.EntityList[i]); } // replace the old entity list with the new one entityLayer.Replace(entitiesInBounds); newMap.SpawnList.Clear(); newMap.SpawnList.AddRange(map.SpawnList); #endregion #region Portals // create a new list of portals List<PortalInfo> portalsInBounds = new List<PortalInfo>(); for (int i = 0; i < mapPortalInfoList.Count; i++) { PortalInfo pInfo = mapPortalInfoList[i]; // if the portal is not contained in our viewport // reset its bounding box if (!viewportRect.Contains(pInfo.boundingBox)) pInfo.boundingBox = Microsoft.Xna.Framework.Rectangle.Empty; portalsInBounds.Add(pInfo); } mapPortalInfoList.Clear(); mapPortalInfoList.AddRange(portalsInBounds); newMap.PortalList.Clear(); newMap.PortalList.AddRange(portalsInBounds); #endregion #region Lights newMap.LightList.Clear(); newMap.LightList.AddRange(map.LightList); #endregion #region Animated Tiles // create a new list of lights List<AnimatedTileInfo> animTilesInBounds = new List<AnimatedTileInfo>(); Microsoft.Xna.Framework.Rectangle mapDimensions = new Microsoft.Xna.Framework.Rectangle(0,0,width,height); for (int i = 0; i < map.AnimatedTileList.Count; i++) { AnimatedTileInfo aTile = map.AnimatedTileList[i]; // check to make sure the tile position of our animated tile // falls within the tile based map dimensions Microsoft.Xna.Framework.Point tPos = new Microsoft.Xna.Framework.Point( (int)aTile.SpawnTile.X, (int)aTile.SpawnTile.X); if (mapDimensions.Contains(tPos)) animTilesInBounds.Add(aTile); } newMap.AnimatedTileList = new List<AnimatedTileInfo>(); newMap.AnimatedTileList.AddRange(animTilesInBounds); #endregion #region Particle Emitters // create a new list of lights List<ParticleEmitter> emittersInBounds = new List<ParticleEmitter>(); for (int i = 0; i < particleLayer.EmitterList.Count; i++) { ParticleEmitter pEmitter = particleLayer.EmitterList[i]; // if the viewport contains our emitter, add it to our list // of acceptable emitters Microsoft.Xna.Framework.Point lPos = new Microsoft.Xna.Framework.Point( (int)pEmitter.WorldPosition.X, (int)pEmitter.WorldPosition.Y); if (viewportRect.Contains(lPos)) emittersInBounds.Add(pEmitter); } particleLayer.Replace(emittersInBounds); newMap.ParticleEmitterList.Clear(); newMap.ParticleEmitterList.AddRange(map.ParticleEmitterList); #endregion #region Shader Regions // create a new list of portals List<ShaderRegionInfo> shadersInBounds = new List<ShaderRegionInfo>(); for (int i = 0; i < mapShaderRegionInfoList.Count; i++) { ShaderRegionInfo sInfo = mapShaderRegionInfoList[i]; // if the portal is not contained in our viewport // reset its bounding box if (!viewportRect.Contains(sInfo.boundingBox)) sInfo.boundingBox = Microsoft.Xna.Framework.Rectangle.Empty; shadersInBounds.Add(sInfo); } mapShaderRegionInfoList.Clear(); mapShaderRegionInfoList.AddRange(shadersInBounds); newMap.ShaderRegionList.Clear(); newMap.ShaderRegionList.AddRange(shadersInBounds); #endregion #region Event Regions // create a new list of portals List<MapEventInfo> eventsInBounds = new List<MapEventInfo>(); for (int i = 0; i < mapEventInfoList.Count; i++) { MapEventInfo eInfo = mapEventInfoList[i]; // if the portal is not contained in our viewport // reset its bounding box if (!viewportRect.Contains(eInfo.boundingBox)) eInfo.boundingBox = Microsoft.Xna.Framework.Rectangle.Empty; eventsInBounds.Add(eInfo); } mapEventInfoList.Clear(); mapEventInfoList.AddRange(eventsInBounds); newMap.EventList.Clear(); newMap.EventList.AddRange(eventsInBounds); #endregion // initialize new map newMap.Initialize(); // copy new map to our map object map = newMap; } #endregion #region Menu Methods /// <summary> /// Generate a new map /// </summary> private void newMapToolStripMenuItem_Click(object sender, EventArgs e) { GenerateNewMap(); } /// <summary> /// Load a XML map /// </summary> private void loadXMLMapToolStripMenuItem_Click(object sender, EventArgs e) { OpenFileDialog fileDialog = new OpenFileDialog(); // Default to the directory which contains our content files. string assemblyLocation = Assembly.GetExecutingAssembly().Location; string relativePath = Path.Combine(assemblyLocation, "../Maps"); string contentPath = Path.GetFullPath(relativePath); // setup dial dialog fileDialog.InitialDirectory = contentPath; fileDialog.Title = "Load XML Map"; fileDialog.Filter = "XML (*.xml)|*.xml|" + "All Files (*.*)|*.*"; if (fileDialog.ShowDialog() == DialogResult.OK) { LoadMap(fileDialog.FileName); // if there is a predefined tileset... if (map.TileSet != null) { string tileSetFile = FileHelper.GetAssemblyDirectory() + "/" + map.TileSet + ".png"; // if the file exists, load it if (File.Exists(tileSetFile)) { LoadTexture(tileSetFile); } else // if not, select a new one { loadTileTextureToolStripMenuItem_Click(null, null); } } // once map is initialized, load all animated tiles if (map.IsInitialized) LoadAnimatedTiles(); } } /// <summary> /// Load a tile texture /// </summary> private void loadTileTextureToolStripMenuItem_Click(object sender, EventArgs e) { OpenFileDialog fileDialog = new OpenFileDialog(); // Default to the directory which contains our content files. string assemblyLocation = Assembly.GetExecutingAssembly().Location; string relativePath = Path.Combine(assemblyLocation, "../Tiles"); string contentPath = Path.GetFullPath(relativePath); // setup file dialog fileDialog.InitialDirectory = contentPath; fileDialog.Title = "Load Tile Set"; fileDialog.Filter = "PNG (*.png)|*.png|" + "All Files (*.*)|*.*"; if (fileDialog.ShowDialog() == DialogResult.OK) { LoadTexture(fileDialog.FileName); } } /// <summary> /// Save Map /// </summary> private void saveToolStripMenuItem_Click(object sender, EventArgs e) { SaveFileDialog fileDialog = new SaveFileDialog(); // Default to the directory which contains our content files. string assemblyLocation = Assembly.GetExecutingAssembly().Location; string relativePath = Path.Combine(assemblyLocation, "../Maps"); string contentPath = Path.GetFullPath(relativePath); // setup file dialog fileDialog.InitialDirectory = contentPath; fileDialog.Title = "Export Map"; fileDialog.Filter = "XML (*.xml)|*.xml|" + "All Files (*.*)|*.*"; if (fileDialog.ShowDialog() == DialogResult.OK) { ExportMap(fileDialog.FileName); } } /// <summary> /// Exit program /// </summary> private void exitToolStripMenuItem_Click(object sender, EventArgs e) { Close(); } /// <summary> /// Show manage layers form /// </summary> private void manageLayersToolStripMenuItem_Click(object sender, EventArgs e) { ManageLayersForm manageLayersForm = ManageLayersForm.CreateInstance(this); if (manageLayersForm != null) { manageLayersForm.Show(); manageLayersForm.TopMost = true; manageLayersForm.Activate(); } } /// <summary> /// Show manage animated tiles form /// </summary> private void manageAnimatedTilesToolStripMenuItem_Click(object sender, EventArgs e) { ManageAnimatedTilesForm manageAnimatedTilesForm = ManageAnimatedTilesForm.CreateInstance(this); if (manageAnimatedTilesForm != null) { { manageAnimatedTilesForm.Show(); manageAnimatedTilesForm.TopMost = true; manageAnimatedTilesForm.Activate(); } } } /// <summary> /// Show manage portals form /// </summary> private void managePortalsToolStripMenuItem_Click(object sender, EventArgs e) { ManagePortalsForm managePortalsForm = ManagePortalsForm.CreateInstance(this); if (managePortalsForm != null) { managePortalsForm.Show(); managePortalsForm.TopMost = true; managePortalsForm.Activate(); } } /// <summary> /// Show configure map settings form /// </summary> private void configureMapToolStripMenuItem_Click(object sender, EventArgs e) { MapSettingsForm mapSettingsForm = MapSettingsForm.CreateInstance(this); if (mapSettingsForm != null) { mapSettingsForm.SaveDefaultSettings += delegate { SaveDefaultSettings(); }; mapSettingsForm.ShowDialog(); mapSettingsForm.TopMost = true; mapSettingsForm.Activate(); } } /// <summary> /// Show manage shader effects form /// </summary> private void manageShadersToolStripMenuItem_Click(object sender, EventArgs e) { ManageShadersForm manageShadersForm = ManageShadersForm.CreateInstance(this); if (manageShadersForm != null) { manageShadersForm.Show(); manageShadersForm.TopMost = true; manageShadersForm.Activate(); } } /// <summary> /// Show manage entities form /// </summary> private void manageEntitiesToolStripMenuItem_Click(object sender, EventArgs e) { ManageEntitiesForm manageEntitiesForm = ManageEntitiesForm.CreateInstance(this); if (manageEntitiesForm != null) { manageEntitiesForm.Show(); manageEntitiesForm.TopMost = true; manageEntitiesForm.Activate(); } } /// <summary> /// Show manage particle emitters form /// </summary> private void manageParticleEmittersToolStripMenuItem_Click(object sender, EventArgs e) { ManageParticlesForm manageParticlesForm = ManageParticlesForm.CreateInstance(this); if (manageParticlesForm != null) { manageParticlesForm.Show(); manageParticlesForm.TopMost = true; manageParticlesForm.Activate(); } } /// <summary> /// Show manage terrain types form /// </summary> private void manageTerrainTypesToolStripMenuItem_Click(object sender, EventArgs e) { ManageTerrainForm manageTerrainForm = ManageTerrainForm.CreateInstance(this); if (manageTerrainForm != null) { manageTerrainForm.Show(); manageTerrainForm.TopMost = true; manageTerrainForm.Activate(); } } /// <summary> /// Show map selections form /// </summary> private void showMapSelectionsToolStripMenuItem_Click(object sender, EventArgs e) { MapSelectionsForm mapSelectionsForm = MapSelectionsForm.CreateInstance(this); if (mapSelectionsForm != null) { mapSelectionsForm.Show(); mapSelectionsForm.TopMost = true; mapSelectionsForm.Activate(); } } /// <summary> /// Show map event form /// </summary> private void manageEventsToolStripMenuItem_Click(object sender, EventArgs e) { ManageEventsForm manageEventsForm = ManageEventsForm.CreateInstance(this); if (manageEventsForm != null) { manageEventsForm.Show(); manageEventsForm.TopMost = true; manageEventsForm.Activate(); } } /// <summary> /// Show manage world lighting form /// </summary> private void manageWorldLightingToolStripMenuItem_Click(object sender, EventArgs e) { ManageWorldLightForm manageWorldLightForm = ManageWorldLightForm.CreateInstance(this, map.LightParams); if (manageWorldLightForm != null) { manageWorldLightForm.Show(); manageWorldLightForm.TopMost = true; manageWorldLightForm.Activate(); } } /// <summary> /// Show manage lights form /// </summary> private void manageLightsToolStripMenuItem_Click(object sender, EventArgs e) { ManageLightsForm manageLightsForm = ManageLightsForm.CreateInstance(this, lightLayer); if (manageLightsForm != null) { manageLightsForm.Show(); manageLightsForm.TopMost = true; manageLightsForm.Activate(); } } #endregion #region Misc Helper Methods /// <summary> /// Loads available plugins using reflection /// </summary> private void LoadPlugins() { // search plugin directory for dlls string[] files = Directory.GetFiles("Plugins", "*.dll"); // check each file in plugin direction foreach (string file in files) { try { // load the assembly and get types Assembly assembly = Assembly.LoadFrom(file); System.Type[] types = assembly.GetTypes(); // look for our interface in the assembly foreach (System.Type type in types) { // if we found our interface, verify attributes if (type.GetInterface("IParadigmPlugin") != null) { if (type.GetCustomAttributes(typeof(ParadigmPluginDisplayNameAttribute), false).Length != 1) throw new PluginNotValidException(type, "Plugin display name is not supported!"); if (type.GetCustomAttributes(typeof(ParadigmPluginDescriptionAttribute), false).Length != 1) throw new PluginNotValidException(type, "Plugin description is not supported"); string name = type.GetCustomAttributes(typeof(ParadigmPluginDisplayNameAttribute), false)[0].ToString(); string description = type.GetCustomAttributes(typeof(ParadigmPluginDescriptionAttribute), false)[0].ToString(); // create the plugin using reflection Object o = Activator.CreateInstance(type); ParadigmPlugin plugin = new ParadigmPlugin(o as IParadigmPlugin, name, description); pluginAssemblies.Add(Path.GetFullPath(FileHelper.GetAssemblyDirectory() + "\\" + file)); loadedPlugins.Add(plugin); } } } catch (Exception e) { MessageBox.Show(e.Message, "Plugin Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } // iterate through list and add them to our form control for (int i = 0; i < loadedPlugins.Count; i++) { ParadigmPlugin plugin = loadedPlugins[i]; pluginsToolStripMenuItem.DropDownItems.Add(plugin.Name); pluginsToolStripMenuItem.DropDownItems[i].Click += delegate { plugin.Activate(contentManager); }; pluginsToolStripMenuItem.DropDownItems[i].Text = plugin.Name; pluginsToolStripMenuItem.DropDownItems[i].ToolTipText = plugin.Description; } } /// <summary> /// Update layers box with the current amount of layers /// </summary> public void UpdateLayers() { currentLayerBox.Items.Clear(); for (int i = 0; i < layerCount; i++) { currentLayerBox.Items.Add(Convert.ToString(i)); if (mapForegroundLayers[i] == true) currentLayerBox.Items[i] = currentLayerBox.Items[i] + " [Fore]"; } map.ForegroundLayers.Clear(); map.ForegroundLayers.AddRange(mapForegroundLayers); currentLayerBox.SelectedIndex = 0; currentLayer = 0; } /// <summary> /// Returns a matrix to be used with SpriteBatch.Begin /// </summary> public Matrix CreateDisplayTranslation() { // variables needed for upcoming matrix translations and decompose Vector3 scale, translation; Quaternion rotation; float X, Y; // create translation matrix initially based upon player being centered Matrix transformMatrix = Matrix.CreateTranslation((float)(-hScrollBar1.Value + xnaDisplay.Width / 2), (float)(-vScrollBar1.Value + xnaDisplay.Height / 2), 1); transformMatrix.Decompose(out scale, out rotation, out translation); X = translation.X; Y = translation.Y; /* Unlock boundaries // check left boundary if (X > 0) X = 0; // check right boundary if (mapPixelWidth >= tileDisplay1.Width) if (mapPixelWidth + X + (map.Tile_Size / 2) < tileDisplay1.Width) X = (float)(-(mapPixelWidth - tileDisplay1.Width + (mapTileSize / 2))); // check top boundary if (Y > 0) Y = 0; // check bottom boundary if (mapPixelHeight >= tileDisplay1.Height) if (mapPixelHeight + Y + (map.Tile_Size / 2) < tileDisplay1.Height) Y = (float)(-(mapPixelHeight - tileDisplay1.Height + (mapTileSize /2))); */ transformMatrix = Matrix.CreateTranslation(new Vector3(X, Y, 1)); return transformMatrix; } /// <summary> /// Retrieve a vector indicating the current camera offset /// </summary> /// <returns></returns> public Vector2 TranslationVector() { Vector3 scale, translation; Quaternion rotation; Matrix translationMatrix = CreateDisplayTranslation(); translationMatrix.Decompose(out scale, out rotation, out translation); Vector2 translationVector2D = new Vector2(translation.X, translation.Y); return translationVector2D; } /// <summary> /// Calculate Map Zoom /// </summary> private void CalculateZoom() { // get the current scroll wheel value int currentWheelValue = Mouse.GetState().ScrollWheelValue; // get the amount of "notches" turned int zoomNotches = (currentWheelValue - prevWheelValue) / 120; // update the map scale by a percentage mapScale += (float)(zoomNotches * .25f); // clamp scale to 1 decimal mapScale = (float)Math.Round((double)mapScale, 2); // clamp the zoom mapScale = MathHelper.Clamp(mapScale, 0.25f, 1f); // update old wheel value prevWheelValue = currentWheelValue; } /// <summary> /// Return a vector containing the x,y coords of the mouse in terms of tiles /// </summary> private Vector2 FormMouseTileLocation() { int mx = Mouse.GetState().X; int my = Mouse.GetState().Y; Vector2 mouseTileLocation = new Vector2((float)(mx / mapTileSize), (float)(my / mapTileSize)); return mouseTileLocation; } /// <summary> /// Return a vector containing the x,y coords of the mouse in terms of tiles /// </summary> private Vector2 XnaMouseTileLocation(MouseState mouseState) { // clamp mouse to confines of display int mx = (int)MathHelper.Clamp(mouseState.X, 0, (float)xnaDisplay.Width); int my = (int)MathHelper.Clamp(mouseState.Y, 0, (float)xnaDisplay.Height); mx += (int)(-TranslationVector().X * mapScale); my += (int)(-TranslationVector().Y * mapScale); int mouseTileX = (int)(mx / (mapTileSize * mapScale)); int mouseTileY = (int)(my / (mapTileSize * mapScale)); Vector2 mouseTileLocation = new Vector2((float)mouseTileX, (float)mouseTileY); return mouseTileLocation; } /// <summary> /// Update the spawn layer /// </summary> public void UpdateSpawnLayer(MapEntityDesc entityDesc) { entityLayer.Update(entityDesc); } /// <summary> /// Update the particle layer /// </summary> /// <param name="particleDesc"></param> public void UpdateParticleLayer(MapParticleDesc particleDesc) { particleLayer.Update(particleDesc); } #endregion #region Tiles Selector /// <summary> /// Paint method for tile selector box. /// </summary> private void tileSelectionBox_Paint(object sender, PaintEventArgs e) { DrawGrid(e.Graphics); DrawSelection(e.Graphics); } /// <summary> /// On mouse enter, set mouse handle to tile selector handle /// </summary> private void tileSelectionBox_MouseEnter(object sender, EventArgs e) { Mouse.WindowHandle = tileSelectionBox.Handle; } /// <summary> /// On mouse leave, set mouse handle to XNA window handle /// </summary> private void tileSelectionBox_MouseLeave(object sender, EventArgs e) { // For now we aren't doing this as the tildDisplay is taking input // when it shouldn't have focus. //Mouse.WindowHandle = tileDisplay1.Handle; } /// <summary> /// On mouse down within tile selector /// </summary> private void tileSelectionBox_MouseDown(object sender, MouseEventArgs e) { int mx = Mouse.GetState().X; int my = Mouse.GetState().Y; Vector2 mouseTileLocation = FormMouseTileLocation(); selectedStartTile = mouseTileLocation; prevMouseState = Mouse.GetState(); } /// <summary> /// On mouse up within tile selector /// </summary> private void tileSelectionBox_MouseUp(object sender, MouseEventArgs e) { if (prevMouseState.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed) { int mx = Mouse.GetState().X; int my = Mouse.GetState().Y; Vector2 mouseTileLocation = FormMouseTileLocation(); selectedEndTile = mouseTileLocation; if (selectedStartTile == selectedEndTile) { selectedMultipleTiles = false; // set selected tile selectedTile.Position = mouseTileLocation; selectedTile.SourceRect = new Microsoft.Xna.Framework.Rectangle( (int)mouseTileLocation.X * mapTileSize, (int)mouseTileLocation.Y * mapTileSize, mapTileSize, mapTileSize ); selectedTile.ID = (int)((mouseTileLocation.Y * tilesetXCount) + mouseTileLocation.X); } else { int xRange = (int)(selectedEndTile.X - selectedStartTile.X); int yRange = (int)(selectedEndTile.Y - selectedStartTile.Y); selectedTileRange = new Vector2(xRange, yRange); selectedMultipleTiles = true; // set selected tile selectedTile.Position = selectedStartTile; selectedTile.SourceRect = new Microsoft.Xna.Framework.Rectangle( (int)selectedStartTile.X * mapTileSize, (int)selectedStartTile.Y * mapTileSize, (xRange * mapTileSize) + mapTileSize, (yRange * mapTileSize) + mapTileSize ); selectedTile.ID = (int)((mouseTileLocation.Y * tilesetXCount) + mouseTileLocation.X); } } prevMouseState = Mouse.GetState(); } /// <summary> /// Draw box around selected tile(s) /// </summary> /// <param name="g"></param> private void DrawSelection(Graphics g) { Graphics graphics = g; if ((tileSelectionBox.Image != null)) { // as we cannot convert from an XNA rect to a GDI rect, use the // values directly graphics.DrawRectangle(selectedPen, selectedTile.SourceRect.X, selectedTile.SourceRect.Y, selectedTile.SourceRect.Width, selectedTile.SourceRect.Height); } } /// <summary> /// Draw a grid over the tile texture /// </summary> private void DrawGrid(Graphics g) { if (tileSelectionBox.Image != null) { Graphics graphics = g; for (int y = 0; y < tileSelectionBox.Image.Height / mapTileSize; y++) { for (int x = 0; x < tileSelectionBox.Image.Width / mapTileSize; x++) { graphics.DrawLine(gridPen, x * mapTileSize, 0, x * mapTileSize, tileSelectionBox.Image.Height); } graphics.DrawLine(gridPen, 0, y * mapTileSize, tileSelectionBox.Image.Width, y * mapTileSize); } } } #endregion } }